;; -*- lexical-binding: t -*- ;; How I Convert Org Files To HTML. ;; ================================ ;; ;; Or: Why We Can't Have Nice Things: Exhibit #42. ;; ------------------------------------------- ;; ;; Or: I Got Way Too Much Time On My Hands, Apparently. ;; ------------------------------------------------ ;; ;; I see two straightforward ways to export Org files to HTML: ;; ;; 1. ox-html.el, Org's HTML backend: even with all the settings and ;; filters available, there are still a few things that annoy me: ;; lots of extra
s, unstable section IDs… ;; ;; Also, I want to squeeze pandoc somewhere in the pipeline, to run ;; my Lua filters. ;; ;; 2. pandoc: does not cover all of Org's features. Org is so crammed ;; with constructs that don't exist in other markup formats ;; (agendas, logbooks, spreadsheets, properties…) and so many knobs ;; can be tweaked on a per-file basis (link abbreviations, tags, ;; TODO cycles) that Elisp remains the least painful way to process ;; these files, IMO. ;; ;; A less-straightforward, but still reasonably simple way to go would ;; be to use Org's markdown backend, then run pandoc on the result. ;; Unfortunately, AFAICT ox-md.el does not implement definition lists, ;; nor syntax-highlighting in fenced code blocks. ;; ;; So here's where I'm at: using Elisp, I'll preprocess Org files to ;; add a bunch of #+OPTIONS pandoc recognizes, "dumb down" the stuff ;; pandoc does not recognize, format some other stuff arbitrarily, ;; *then* I'll run pandoc on the result. (defun pp-org/list-tags () (goto-char (point-min)) (while (re-search-forward org-heading-regexp nil t) (save-excursion (save-match-data (when-let ((tags (org-get-tags (point)))) (insert "\n#+begin_tags\n") (dolist (tag tags) (insert "- " tag "\n")) (insert "#+end_tags\n")))))) (defun pp-org/expand-links () ;; Expand #+LINK abbreviations, since pandoc does not grok them. ;; Also, use the abbreviation as default description for links that ;; lack one. (pcase-dolist (`(,key . ,expansion) org-link-abbrev-alist-local) (goto-char (point-min)) (let ((link-re (rx "[[" (group (literal key) ":" (group (+ (not "]")))) "]" (? (group "[" (group (+ (not "]"))) "]")) "]")) (expand-link (if (string-match-p "%s" expansion) (lambda (tag) (format expansion tag)) (lambda (tag) (concat expansion tag))))) (while (re-search-forward link-re nil t) (let ((link-beg (match-beginning 0)) (link-abbrev (match-string 1)) (link-tag (match-string 2)) (description (match-string 4))) (replace-match (funcall expand-link link-tag) t t nil 1) (unless description (save-excursion (goto-char (1+ link-beg)) (forward-sexp) (insert (format "[%s]" link-abbrev))))))))) (defun preprocess-org (input) (with-temp-buffer (insert "#+OPTIONS: ^:{} tags:nil\n") (insert-file-contents input) (org-mode) (pp-org/list-tags) (pp-org/expand-links) (princ (buffer-string))))