Config Org-Mode For Publishing
2025-02-21, Fri
Org-Mode comes with exporting capability through a bunch of
org-export-*
functions. e.g. there is org-html-export-to-html
that
converts current buffer to an HTML file. However, these export
functions typically handle a single file, which means when working
with multiple files in a directory, it's org-publish-*
functions
that come to the rescue. When
org-publish
is invoked for the first time, it asks for a proper
configuration of variable org-publish-project-alist
to be
made1, which is what this article is about.
Notes:
- before introducing any changes into
init.el
, consider to link it into a source repo for better version control, e.g. with Git, Mercurial, etc. - Both
org-export*
andorg-publish*
are available interactively throughorg-export-dispatch
(C-c C-e).
1. Quick Set Up
The Org-Mode manual provides a sample configuration2 that works with little tweak. Combined it with another example3, we now have:
(setq org-publish-project-alist `(("orgfiles" :base-extension "org" :exclude "PrivateFile.org" ;; :exclude ,(rx (or "PrivateFile.org" (seq line-start "private/"))) ;; regexp :base-directory "/path/to/org-src/" :publishing-directory "/path/to/org-publish/" :publishing-function org-html-publish-to-html ;; :headline-levels 3 ;; :section-numbers nil :with-toc nil :html-head "<link rel=\"stylesheet\" href=\"/main-styles.css\" type=\"text/css\"/>" :html-preamble "<a href=\"/\">Home</a>" :html-postamble t :recursive t) ("assets" :base-directory "/path/to/org-src/" :publishing-directory "/path/to/org-publish" :base-extension "jpg\\|gif\\|png\\|css" :publishing-function org-publish-attachment :recursive t) ("website" :components ("orgfiles" "assets"))))
Notable properties here are:
:recursive
scans all sub-directories:html-head
puts a stylesheet<link>
element into<head>
:html-preamble
puts a customheader
element into<body>
:htmt-postamble
puts a defaultfooter
element into<body>
Full list of other properties could be found in the Org-Mode manual4.
They are also listed in ox-publish.el
and ox-html.el
.
2. Organize Configs
Configurations are like code – well, techically the are code here, so when duplications happen, it is time for refactoring. Out first step is to extract string literals into constants.
(setq my-src-dir "/path/to/org-src/" my-publish-dir "/path/to/org-publish/" my-html-head "<link rel=\"stylesheet\" href=\"/main-styles.css\" type=\"text/css\"/>" my-html-preamble "<a href=\"/\">Home</a>" my-html-postamble "<p class=\"author\">Author: %a (%e)</p> <p class=\"date\">Date: %d</p> <p class=\"creator\">%c</p>") (setq org-publish-project-alist `(("orgfiles" :base-extension "org" :exclude "PrivateFile.org" :base-directory ,my-src-dir :publishing-directory ,my-publish-dir :publishing-function org-html-publish-to-html :with-toc nil :html-head ,my-html-head :html-preamble ,my-html-preamble :html-postamble ,my-html-postamble :recursive t) ("assets" :base-directory ,my-src-dir :publishing-directory ,my-publish-dir :base-extension "jpg\\|gif\\|png\\|css" :publishing-function org-publish-attachment :recursive t) ("website" :components ("orgfiles" "assets"))))
Even with new configurations available, org-publish
still checks the
source files' timestamp to decide whether to re-export them. According
to the function doc: "When optional argument FORCE is non-nil, force
publishing all files in PROJECT", hence invoke C-u before org-publish
should trigger a fresh publish.
2.1. Handle index.org separately
The home page doesn't need a preamble that contains link to Home itself (or does it?). In order to customize this behavior, we could introduce a new project config to handle home page separately, e.g.
(setq org-publish-project-alist `(("index" :base-extension "org" :exclude "PrivateFile.org" :base-directory ,my-src-dir :publishing-directory ,my-publish-dir :publishing-function org-html-publish-to-html :with-toc nil :html-head ,my-html-head :html-preamble nil :html-postamble ,my-html-postamble) ("orgfiles" :base-extension "org" :exclude "\\(index\\|PrivateFile\\).org" ;; exclude index.org along with private files :base-directory ,my-src-dir :publishing-directory ,my-publish-dir :publishing-function org-html-publish-to-html :with-toc nil :html-head ,my-html-head :html-preamble ,my-html-preamble :html-postamble ,my-html-postamble :recursive t) ("assets" :base-directory ,my-src-dir :publishing-directory ,my-publish-dir :base-extension "jpg\\|gif\\|png\\|css" :publishing-function org-publish-attachment :recursive t) ("website" :components ("index" "orgfiles" "assets"))))
This seems like a candicate for macro, which will not be covered
here. If you are caught up with the regular expression used in
:exclude
, try to test a few custom patterns on function
string-match
in the *scratch*
buffer.
3. What's Next
org-publish
is defined in ox-publish.el
, and org-html-export-to-html
is defined in ox-html.el
. The source code is well maintained and documented,
so it shouldn't be too much trouble if further customization is needed.
Exercises:
- Check when
org-html-inner-template
is invoked, and try to customize its behavior - Check when
org-html-template
is invoked, and try to customize its behavior - Instead of utilizing
org-html-publish-to-html
for HTML publication, try to come up with something likemy-org-html-publish-to-html
to designate the document structure and default style. Hint: start with the(org-export-define-backend 'html...)
line inox-html.el
.
Footnotes:
If it was some other GUI application, it might be the time when a "Wizard" modal dialog shows up that guides you step by step on how to completing the following process.
Org-Mode manual provides an example on complex publishing configuration: https://orgmode.org/manual/Complex-example.html. Similar content is also available on EmacsDocs.org through https://emacsdocs.org/docs/org/Complex-example.
Org-Mode publishing with different postamble: https://www.reddit.com/r/orgmode/comments/10u8k3l/org_publish_and_different_postamble/
Org Manual on Publishing Options: https://orgmode.org/manual/Publishing-options.html and https://emacsdocs.org/docs/org/Publishing-options