Include inline SVGs in Org Mode HTML and Markdown exports

Posted: - Modified: | emacs, org, images
  • : Fixed descriptions.
  • : Fixed path when inlining file URLs.
  • : Now I can specify #+ATTR_HTML :data-link t to make it link instead of include.
  • : 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:

digraph g {
    rankdir=LR;
    node [fontcolor="#000000",fontname="Roboto,Arial,sans-serif"];
    edge [fontcolor="#000000",fontname="Roboto,Arial,sans-serif"];
    Graphviz -> "Org Mode" [label="SVG"];
    "Org Mode" -> {HTML Markdown};
    Graphviz[URL="https://graphviz.org",fontcolor="blue"];
}

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

;;;###autoload
(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))

;;;###autoload
(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))
            (attr (org-export-read-attribute :attr_html (org-element-parent-element link))))
        (if (string= (file-name-extension path) "svg")
            (with-temp-buffer
              (set-buffer-multibyte t)
              (insert-file-contents path)
              (if attr
                  (replace-regexp-in-string
                   "<svg "
                   (concat
                    "<svg "
                    (org-html--make-attribute-string attr)
                    " ")
                   (buffer-string))
                (buffer-string)))
          (org-html-link link desc info)))
    (org-html-link link desc info)))

;;;###autoload
(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)))

;;;###autoload
(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))
           (not desc))
      (let ((path (org-element-property :path link))
            (attr (org-export-read-attribute :attr_html (org-element-parent-element link))))
        (if (string= (file-name-extension path) "svg")
            (with-temp-buffer
              (set-buffer-multibyte t)
              (insert-file-contents path)
              (if attr
                  (replace-regexp-in-string
                   "<svg "
                   (concat
                    "<svg "
                    (org-html--make-attribute-string attr)
                    " ")
                   (buffer-string))
                (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