Custom Org HTML document preambles and postambles

There are multiple ways to set preambles and postambles when exporting from Org to HTML.1


The easiest to use is the :html-preamble key when setting org-publish-project-alist2:

(setq org-publish-project-alist
	 :base-directory "/src"
	 :publishing-directory "/dist"
	 :publishing-function org-custom-html-publish-to-html
	 :html-preamble "By %a on %C (originally published on %d)")))

The exporter supports placeholders like %a to insert the author name, %d for the publication date and %C for the last modification date3.


For more control (like showing some data conditionally), you can set org-html-preamble to point to a function. This disables the placeholders, but the function is called with the info attribute, which holds the same information4:

(defun custom-preamble (info)
  (let ((timestamp-format (plist-get info :html-metadata-timestamp-format)))
    (concat "By "
	    (org-export-data (plist-get info :author) info)
	    " on "
	    (let ((file (plist-get info :input-file)))
	      (format-time-string timestamp-format
				  (and file (file-attribute-modification-time
					     (file-attributes file)))))
	    "(originally published on "
	    (org-export-data (org-export-get-date info timestamp-format) info)

(setq org-html-preamble 'custom-preamble)

Now, we can only show the updated date when it differs from the publication date, for example:

(defun custom-preamble (info)
  (let ((timestamp-format (plist-get info :html-metadata-timestamp-format))
	(file (plist-get info :input-file)))
    (let ((published (org-export-data (org-export-get-date info timestamp-format) info))
	  (updated (format-time-string timestamp-format
				       (and file (file-attribute-modification-time
						  (file-attributes file))))))
      (concat "By " (org-export-data (plist-get info :author) info)
	      " on " updated
	      (unless (equal published updated)
		(concat " (originally published on " published ")"))))))

(setq org-html-preamble 'custom-preamble)

  1. This document refers only to preambles, but everything discussed works for postambles as well.

  2. This is equivalent to setting the org-html-preamble variable:

    (setq org-html-preamble "By %a on %C (originally published on %d)")
  3. :

    From Emacs’ help for the org-html-preamble-format variable:

    Alist of languages and format strings for the HTML preamble.


    This format string can contain these elements:

    %t stands for the title. %s stands for the subtitle. %a stands for the author’s name. %e stands for the author’s email. %d stands for the date. %c will be replaced by ‘org-html-creator-string’. %v will be replaced by ‘org-html-validation-link’. %T will be replaced by the export time. %C will be replaced by the last modification time.

    If you need to use a “%” character, you need to escape it like that: “%%”.

  4. (defun org-html-format-spec (info)
      "Return format specification for preamble and postamble.
    INFO is a plist used as a communication channel."
      (let ((timestamp-format (plist-get info :html-metadata-timestamp-format)))
        `((?t . ,(org-export-data (plist-get info :title) info))
          (?s . ,(org-export-data (plist-get info :subtitle) info))
          (?d . ,(org-export-data (org-export-get-date info timestamp-format)
          (?T . ,(format-time-string timestamp-format))
          (?a . ,(org-export-data (plist-get info :author) info))
          (?e . ,(mapconcat
    	      (lambda (e) (format "<a href=\"mailto:%s\">%s</a>" e e))
    	      (split-string (plist-get info :email)  ",+ *")
    	      ", "))
          (?c . ,(plist-get info :creator))
          (?C . ,(let ((file (plist-get info :input-file)))
    	       (format-time-string timestamp-format
    				   (and file (file-attribute-modification-time
    					      (file-attributes file))))))
          (?v . ,(or (plist-get info :html-validation-link) "")))))