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)



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


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

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


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: "%%".

(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) "")))))