Include inline SVGs in Org Mode HTML and Markdown exports

Posted: - Modified: | emacs, org
  • [2024-10-14 Mon]: Fixed path when inlining file URLs.
  • [2024-10-07 Mon]: Now I can specify #+ATTR_HTML :data-link t to make it link instead of include.
  • [2024-09-26 Thu]: Whoops, forgot to make sure ox-11ty is also covered.

In my Org Mode HTML and Markdown exports, I usually want to include SVGs inline so that I can use links. Sometimes I also want to use Javascript and CSS to modify elements within the images. I used to use a my-include: link to do this, but I realized that I can also modify this behaviour by making my own functions that call org-html-link or org-md-link and then put those functions in org-export-backend-transcoders.

Here is an example of an SVG:

g Graphviz Graphviz Org Mode Org Mode Graphviz->Org Mode SVG HTML HTML Org Mode->HTML Markdown Markdown Org Mode->Markdown

The following code overrides HTML and Markdown exports to include SVGs.

(defun my-ox-link-path (link _ info)
  (let* ((raw-path (org-element-property :path link)))
    (setq raw-path
          (org-export-file-uri
           (org-publish-file-relative-name raw-path info)))
    ;; Possibly append `:html-link-home' to relative file
    ;; name.
    (let ((home (and (plist-get info :html-link-home)
                     (org-trim (plist-get info :html-link-home)))))
      (when (and home
                 (plist-get info :html-link-use-abs-url)
                 (not (file-name-absolute-p raw-path)))
        (setq raw-path (concat (file-name-as-directory home) raw-path))))
    raw-path))

(defun my-org-html-link (link desc info)
  (if (and
       (string= (org-element-property :type link) "file")
       (not (plist-get (org-export-read-attribute :attr_html (org-element-parent-element link))
                       :data-link))
       (org-export-inline-image-p link (plist-get info :html-inline-image-rules)))
      (let ((path (org-element-property :path link)))
        (if (string= (file-name-extension path) "svg")
            (with-temp-buffer
              (insert-file-contents-literally path)
              (buffer-string))
          (org-html-link link desc info)))
    (org-html-link link desc info)))

(defun my-org-md-link (link desc info)
  (if (and (string= (org-element-property :type link) "file")
           (not (plist-get (org-export-read-attribute :attr_html (org-element-parent-element link))
                       :data-link)))
      (let ((path (org-element-property :path link)))
        (if (string= (file-name-extension path) "svg")
            (with-temp-buffer
              (insert-file-contents-literally path)
              (buffer-string))
          (org-md-link link desc info)))
    (org-md-link link desc info)))

(defun my-org-11ty-link (link desc info)
  (if (and (string= (org-element-property :type link) "file")
           (not (plist-get (org-export-read-attribute :attr_html (org-element-parent-element link))
                       :data-link)))
      (let ((path (org-element-property :path link)))
        (if (string= (file-name-extension path) "svg")
            (with-temp-buffer
              (insert-file-contents-literally path)
              (buffer-string))
          (org-11ty-link link desc info)))
    (org-11ty-link link desc info)))

(with-eval-after-load 'ox-html
  (setf
   (alist-get 'link (org-export-backend-transcoders (org-export-get-backend 'html)))
   'my-org-html-link))
(with-eval-after-load 'ox-md
  (setf
   (alist-get 'link (org-export-backend-transcoders (org-export-get-backend 'md)))
   'my-org-md-link))
(with-eval-after-load 'ox-11ty
  (setf
   (alist-get 'link (org-export-backend-transcoders (org-export-get-backend '11ty)))
   'my-org-11ty-link))
This is part of my Emacs configuration.
View org source for this post
You can comment with Disqus or you can e-mail me at sacha@sachachua.com.