Orgmode HTML-Export Settings
题图来自网络
1 前言
配置 org-mode,使其成为静态 HTML 生成工具。实现类似于 Jekyll 的效果。
关于基础配置部分,我觉得没有必要赘述,可以直接查看我这个文件 org-publish-setting.el 就好了。 有什么问题,欢迎随时交流沟通。
2 Lazy Load Image
在某次写文章的时候,萌生了为自己的站点提供图片延迟加载的技术。 在改造的过程中,体会到了 Lisp “动态语言”的特性,在“外部”直接写方法覆盖其“内部”方法, 改变数据的形态,使其符合自己的实际需求。
Org-mode 生成 HTML 的代码都在 ox-html.el 中,其中涉及到图片 HTML 生成代码主要是这几个函数
org-html--wrap-image
和 org-html--format-image
(注:这个是9.0版本的函数,
如果是8.0的版本,是这2个函数: org-html--make-attribute-string
和 org-html--format-image
)
由于之前并不知道 elisp 的动态特性,傻傻的在源文件中修改代码,之后使用 elpa 升级 org-mode 包之后,全部没有了。
后来知道了这个特性之后。哈哈。就方便很多了(当然,你要小心的坑也多了,一不小心,你写的东西就没了)。最终我需要做的的就是在一个新的文件中写入如下代码。
其核心思想就是让 <img>
标签增加 lazy-load
和 data-src
属性,便于前端 JS 读取相关信息,然后做出对其进行延迟加载。
关于 JS 部分的代码可以看 这里 (Lazy load images)。
首先是对于 <img>
标签的改造, 核心代码在 org-html--format-image
函数中,
将下面的代码
(org-html-close-tag "img" (org-html--make-attribute-string (org-combine-plists (list :src source :alt (if (string-match-p "^ltxpng/" source) (org-html-encode-plain-text (org-find-text-property-in-string 'org-latex-src source)) (file-name-nondirectory source))) attributes)) info)
替换成
(let ((normal-img-attr (org-html-make-attribute-macro :src)) (lazy-load-img-attr (format "lazy-load %s" (org-html-make-attribute-macro :data-src)))) (if (plist-get info :html-image-lazy-load) (format "<noscript>%s</noscript>%s" (org-html-close-tag-img-macro normal-img-attr) (org-html-close-tag-img-macro lazy-load-img-attr)) (org-html-close-tag-img-macro normal-img-attr)))
其中,宏 org-html-make-attribute-macro
和 org-html-close-tag-img-macro
分别实现对 HTML 标签属性和 <img>
标签的自定义扩展。
(defmacro org-html-make-attribute-macro (src) "Return macro org-html--make-attribute-string. SRC." `(org-html--make-attribute-string (org-combine-plists (list ,src source :alt (if (string-match-p "^ltxpng/" source) (org-html-encode-plain-text (org-find-text-property-in-string 'org-latex-src source)) (file-name-nondirectory source))) attributes))) (defmacro org-html-close-tag-img-macro (attr) "Return. ATTR." `(org-html-close-tag "img" ,attr info))
上述过程对 <img>
标签自身属性进行了改造,并增加了对禁用 Javascript 情况下的支持(就是增加了 <noscript>
标签)。
但是外围的标签没有改动,如果想对其有一个更加深入的定制,
还需要将包裹 <img>
标签的父标签进行改造。在9.0版本中,需要改动的是 org-html--wrap-image
函数,
如果你是8.0版本,需要改动 org-html--make-attribute-string
函数。改动主要是将
(format "\n<p>%s</p>" contents)
改成
(if (plist-get info :html-image-lazy-load) (format "\n<p class=\"lazy-load-img-wrapper\">%s</p>" contents) (format "\n<p>%s</p>" contents))
至此,对于 org-mode 内置的图片导出功能改造完毕。
主要效果是给 <img>
标签增加了 data-src
和 lazy-load
属性,增加了 <noscript>
标签的支持。
并为其父标签增加了 class: lazy-load-img-wrapper
。
后面就是写 JS 来实现加载图片的功能啦。可以参考 这个 。
3 Mathjax
对 Mathjax 模板的定制,重写 org-html-mathjax-template
即可。
这里我主要是为其 <script>
标签增加了 async
的支持。使其能够异步加载
(不过在不支持的浏览器中会退化成同步加载,所以还是存在阻塞的风险)。
(org-html-mathjax-template "<script type=\"text/x-mathjax-config\"> MathJax.Hub.Config({ displayAlign: \"%ALIGN\", displayIndent: \"%INDENT\", \"HTML-CSS\": { scale: %SCALE, linebreaks: { automatic: \"%LINEBREAKS\" }, webFont: \"%FONT\" }, SVG: {scale: %SCALE, linebreaks: { automatic: \"%LINEBREAKS\" }, font: \"%FONT\"}, NativeMML: {scale: %SCALE}, TeX: { equationNumbers: {autoNumber: \"%AUTONUMBER\"}, MultLineWidth: \"%MULTLINEWIDTH\", TagSide: \"%TAGSIDE\", TagIndent: \"%TAGINDENT\" } });</script><script async type=\"text/javascript\" src=\"%PATH\"></script>")
4 Update from Version 8.0 to Version 9.0
这次升级应该是一个比较大的升级,其中修改了一个声明,将
#+BEGIN_backend ... #+END_backend
替换成了
#+BEGIN_EXPORT backend ... #+END_EXPORT
于是乎,之前如果用到了这些声明,也就意味着你需要更改到新的版本,才能正常导出。
还好,官方比较良心,给了一段替换之前 BLOCK 声明的代码,
我在其基础上增加了遍历项目中所有 org 文件并将其修改的功能,
大致代码如下(如果没有给代码,我准备用 sed
搞一下了):
(defun org-repair-export-blocks (dirname) "Repair export blocks and INCLUDE keywords in current buffer. DIRNAME." (interactive) (let ((files (directory-files-recursively dirname ".org"))) (dolist (file files) (progn (find-file file) (when (eq major-mode 'org-mode) (let ((case-fold-search t) (back-end-re (regexp-opt '("HTML" "ASCII" "LATEX" "ODT" "MARKDOWN" "MD" "ORG" "MAN" "BEAMER" "TEXINFO" "GROFF" "KOMA-LETTER") t))) (org-with-wide-buffer (goto-char (point-min)) (let ((block-re (concat "^[ \t]*#\\+BEGIN_" back-end-re))) (save-excursion (while (re-search-forward block-re nil t) (let ((element (save-match-data (org-element-at-point)))) (when (eq (org-element-type element) 'special-block) (save-excursion (goto-char (org-element-property :end element)) (save-match-data (search-backward "_")) (forward-char) (insert "EXPORT") (delete-region (point) (line-end-position))) (replace-match "EXPORT \\1" nil nil nil 1)))))) (let ((include-re (format "^[ \t]*#\\+INCLUDE: .*?%s[ \t]*$" back-end-re))) (while (re-search-forward include-re nil t) (let ((element (save-match-data (org-element-at-point)))) (when (and (eq (org-element-type element) 'keyword) (string= (org-element-property :key element) "INCLUDE")) (replace-match "EXPORT \\1" nil nil nil 1)))))))) (save-buffer (buffer-name)) (kill-buffer (buffer-name)))))) ;; (org-repair-export-blocks "_content")
5 尾声
期待更多有意思的“改造”。