Oct 16,
2019

Markdown On The Command Line

Greetings people from the Interwebs!

The other day I was in need of creating a brief documentation file for internal use and instead of using a simple text file I thought — "Why not using Markdown and having a kinda readable text file plus a nicely styled HTML document?" —.

Outside this blog I've never really used Markdown or even Org mode to style documents, so I only remember the tags used for the headings, bold and italics, for everything else I need to check the documentation. Nonetheless with minimum effort, basically sprinkling the text with a few '#' and '*' symbols, maximum results with no word processors involved.

I should also add that being an Emacs user I'm probably in the minority not using Org.

Anywho, on my desktop I had already installed the cmark-gfm package (Github Flavored Markdown) to use it with Emacs for rendering Markdown to HTML — or CommonMark in this specific case1 — but I wasn't able to find mentioned anywhere an utility that was able to add a specific style sheet file to the produced HTML.

It's not a complicated task, it's an header and a footer if we want to be really fancy, with an HTTP link tag that points to the chosen CSS file, it could be done in an editor or with a shell one liner, a plain cat header output.html footer > final-document.html would do the job, but it's clunky and I wanted something completed with a single command.

Maximum Effort

Well, as I should have expected, the Emacs's Markdown mode has hooks that can be used to personalize the output and there is already an example available on the Emacs WiKi where I only needed to add a few touches, so this is what I put in my ever expanding configuration:

(defalias 'markdown-add-xhtml-header-and-footer 
  'as/markdown-add-xhtml-header-and-footer)

(defun as/markdown-add-xhtml-header-and-footer (title)
  "Wrap XHTML header and footer with given TITLE around current buffer."
  (goto-char (point-min))
  (insert "<!DOCTYPE html5>\n"
    "<html>\n"
    "<head>\n<title>")
  (insert title)
  (insert "</title>\n")
  (insert "<meta charset=\"utf-8\" />\n")
  (when (> (length markdown-css-paths) 0)
    (insert (concat
             "<style type=\"text/css\">\n"
             (with-temp-buffer
               (insert-file-contents (car markdown-css-paths))
               (buffer-string))
             "\n.markdown-body {\n"
       "  box-sizing: border-box;\n"
       "  min-width: 200px;\n"
       "  max-width: 980px;\n"
       "  margin: 0 auto;\n"
       "  padding: 45px;\n"
       "}\n\n"
       "@media (max-width: 767px) {\n"
       "  .markdown-body {\n"
       "    padding: 15px;\n"
       "  }\n"
       "}\n"
             "</style>\n")))
  (insert "</head>\n"
    "<body class=\"markdown-body\">\n")
  (goto-char (point-max))
  (insert "</body>\n"
    "</html>\n")))

I prefer to have a copy available locally instead to depend to a file downloaded from a CDN so the code above embed the CSS in the header plus an incantation to add some padding to the page created. By the way the canonical home-page where to find the official CSS used on GitHub is primer.style, but there are many style-sheets to choose from, like for example:

I'm quite sure a query in the reader's favorite search engine would find others.

To complete the function above it requires a couple of tweaks for cmark-gfm's invocation to render tables and the path to the style-sheet file to embed, in this case hidden in my Emacs configuration folder.

(setq markdown-command "cmark-gfm -e table")
(setq markdown-css-paths '("~/.emacs.d/github-markdown.css"))

Colophon

The astute reader would have surely noted that the variable name above is markdown-css-pathsi.e. plural. I haven't deeply investigated how Markdown mode for Emacs works and if there's a way to choose a specific style-sheet from a list, nonetheless my code always picks the first path, if available, from the list.

Or I could have installed Pandoc from the beginning with the bonus of PDF creation...


  1. Markdown is very minimal so it was natural to imagine extensions would appear to "fix it", so we have now few specifications like CommonMark, RMarkdown or Markdown Extra and at least 20 implementations ↩︎