Add simple org-attach support for org-publish with a post-processing function

Table of Contents

Publishing attachments with org-publish

I use org-attach and org-download to attach files and screenshots to my documents. As far as I know, org-publish doesn’t support this out of the box. I tried to fix this with a custom :publishing-function, but this didn’t work well when files were included using #+INCLUDE: as we operate on a file-by-file basis. I also didn’t want to modify the files directly or do a full copy of all the files before modifying.

My solution was a :completion-function which walks the published html files, looks for local file:// links, symlinks the attachments over, and patches the reference to point to the relative file instead.

The code

(defun simendsjo/org-publish-include-attachments (plist)
  "Fix published html for org-attach attached files.

- Walks all html files
- Copies attached files it finds to a local .attach folder
- Fixes all src links to point to this new location"
  (let ((pattern (concat "src=\"file://\\(" (regexp-quote org-attach-id-dir) "\\)/\\([^\"]*\\)"))
        (pub-dir (plist-get plist :publishing-directory)))
    (dolist (file (directory-files-recursively pub-dir "\.html$" t))
      (let ((buffer (find-file-noselect file)))
        (with-current-buffer buffer
          (goto-char (point-min))
          (while (re-search-forward pattern nil t)
            (let* ((attach-part (match-string 1))
                   (file-part (match-string 2))
                   (srcfile (f-join attach-part file-part))
                   (dstfile-rel (f-join ".attach" file-part))
                   (dstfile (f-join pub-dir dstfile-rel)))
              ;; Make sure the directory exists as copy/symlink assumes it.
              (let ((dir (file-name-directory dstfile)))
                (unless (f-directory-p dir)
                  (message "Attachment directory %s missing, creating it" dir)
                  (make-directory dir t)))
              ;; Copy/symlink attachment
              (if IS-WINDOWS
                  (copy-file srcfile dstfile)
                (make-symbolic-link srcfile dstfile t))
              ;; Replace link to relative file
              ;; I assume the .attach folder is added at the root, and thus add
              ;; the / at the beginning
              (replace-match (concat "src=\"/" dstfile-rel "\"")))))))))

In action   ATTACH

Screenshot of the document in emacs before attaching something _20230629_222957screenshot.png

Date: 2023-06-29 Thu 00:00

Author: Simen Endsjø

Created: 2023-07-20 Thu 20:10