导出过程会在实际导出开始前执行两个钩子。第一个钩子 org-export-before-processing-functions 会在缓冲区中的宏、Babel 代码以及包含关键字展开之前运行。第二个钩子 org-export-before-parsing-functions 会在缓冲区被解析之前运行。
添加到这些钩子中的函数会以单个参数调用:实际使用的导出后端,以符号形式表示。你可以用它们对文档进行大规模的结构修改。例如,你可以在导出期间移除缓冲区中的所有标题,示例如下:
(defun my-headline-removal (backend)
"Remove all headlines in the current buffer.
BACKEND is the export backend being used, as a symbol."
(org-map-entries
(lambda ()
(delete-region (point) (line-beginning-position 2))
;; We need to tell `org-map-entries' to not skip over heading at
;; point. Otherwise, it would continue from _next_ heading. See
;; the docstring of `org-map-entries' for details.
(setq org-map-continue-from (point)))))
(add-hook 'org-export-before-parsing-functions #'my-headline-removal)
过滤器是应用于指定后端特定部分的函数列表。过滤器中第一个函数的输出会传递给下一个函数,最终输出为过滤器中最后一个函数的结果。
Org 导出过程拥有许多适用于不同类型对象、纯文本、语法树、导出选项和最终输出格式的过滤器集。过滤器以元素类型或对象类型命名:~org-export-filter-TYPE-functions~ ,其中 TYPE 为过滤器的目标类型。有效类型包括:
| body | bold | babel-call |
| center-block | clock | code |
| diary-sexp | drawer | dynamic-block |
| entity | example-block | export-block |
| export-snippet | final-output | fixed-width |
| footnote-definition | footnote-reference | headline |
| horizontal-rule | inline-babel-call | inline-src-block |
| inlinetask | italic | item |
| keyword | latex-environment | latex-fragment |
| line-break | link | node-property |
| options | paragraph | parse-tree |
| plain-list | plain-text | planning |
| property-drawer | quote-block | radio-target |
| section | special-block | src-block |
| statistics-cookie | strike-through | subscript |
| superscript | table | table-cell |
| table-row | target | timestamp |
| underline | verbatim | verse-block |
下面是一个过滤器示例,它会在 LaTeX 后端中将 Org 缓冲区中的非换行空格 ~ ~ 替换为 ‘~’ 。
(defun my-latex-filter-nobreaks (text backend info)
"Ensure \" \" are properly handled in LaTeX export."
(when (org-export-derived-backend-p backend 'latex)
(replace-regexp-in-string " " "~" text)))
(add-to-list 'org-export-filter-plain-text-functions
'my-latex-filter-nobreaks)
过滤器需要三个参数:待转换的代码、后端名称以及一些关于导出过程的可选信息。第三个参数可以安全忽略。注意使用 org-export-derived-backend-p 谓词来判断 latex 后端或从 latex 派生的其他后端(如 beamer )。
Org 导出不仅可以针对后端设置过滤器,还可以通过 ‘BIND’ 关键字针对特定文件设置过滤器。下面是包含两个过滤器的示例:一个移除时间戳中的括号,另一个移除删除线文本。过滤器函数定义在同一 Org 文件的代码块中,这对调试十分方便。
#+BIND: org-export-filter-timestamp-functions (tmp-f-timestamp)
#+BIND: org-export-filter-strike-through-functions (tmp-f-strike-through)
#+BEGIN_SRC emacs-lisp :exports results :results none
(defun tmp-f-timestamp (s backend info)
(replace-regexp-in-string "&[lg]t;\\|[][]" "" s))
(defun tmp-f-strike-through (s backend info) "")
#+END_SRC
Org 模式导出是一个多步骤过程,基于缓冲区的临时副本进行操作。导出过程包含 4 个主要步骤:
处理源 Org 缓冲区的临时副本 151:
org-export-before-processing-functions (见 Export hooks);
org-export-replace-macros 为 nil;
org-export-use-babel 非空(默认值)时,处理代码块:
解析临时缓冲区,创建 AST:
org-export-before-parsing-functions (见 Export hooks)。钩子函数仍可修改缓冲区;
org-org-with-cite-processors 非空(默认值)时,确定相关参考文献并记录到导出选项中(见 Citations)。整个缓冲区都会被纳入考量;
org-export-filter-options-functions ;
(org-data ... (heading (section (paragraph (plain-text) (bold (plain-text)))) (heading) (heading (section ...))))
在此步骤之后,对临时缓冲区的修改将不再影响导出;Org 导出仅基于 AST 进行操作;
:with-special-rows 导出选项设为非空(见 Column Width and Alignment);
org-export-filter-parse-tree-functions 。这些函数可通过副作用修改 AST;
org-org-with-cite-processors 非空(默认值)时,根据所选引用导出处理器的规则替换引用 AST 节点和 ‘#+print_bibliography’ 关键字 AST 节点(见 Citation export processors)。
通过深度优先遍历 AST 节点,将 AST 转换为文本:
对导出文本进行后处理:
org-export-filter-body-functions ;
org-org-with-cite-processors 非空(默认值)时,根据引用导出处理器规则添加参考文献元数据;
org-export-filter-final-output-functions 。
转换过程的部分内容可针对特定元素进行扩展,以引入新的或修改后的转换规则。HTML 导出后端正是通过这种方式扩展以支持 Markdown 格式。扩展可无缝工作,扩展后端未处理的过滤内容将由原后端处理。在 Org 的所有导出自定义方式中,扩展功能十分强大,因为它在解析器层面运作。
在本示例中,让 ascii 后端显示源代码块中使用的语言,并且仅在某个属性为非 nil 时显示,如下所示:
#+ATTR_ASCII: :language t
然后基于 ASCII 后端扩展自定义的 “my-ascii” 后端。
(defun my-ascii-src-block (src-block contents info)
"Transcode a SRC-BLOCK element from Org to ASCII.
CONTENTS is nil. INFO is a plist used as a communication
channel."
(if (not (org-export-read-attribute :attr_ascii src-block :language))
(org-export-with-backend 'ascii src-block contents info)
(concat
(format ",--[ %s ]--\n%s`----"
(org-element-property :language src-block)
(replace-regexp-in-string
"^" "| "
(org-element-normalize-string
(org-export-format-code-default src-block info)))))))
(org-export-define-derived-backend 'my-ascii 'ascii
:translate-alist '((src-block . my-ascii-src-block)))
my-ascii-src-block 函数会读取当前元素上方的属性。若值为假,则交由 ascii 后端处理;若为真(本示例中即为真),则在代码周围创建框线并预留位置插入语言字符串。最后一段代码创建新后端,仅在转换 src-block 类型元素时生效。
要使用新定义的后端,在 Org 缓冲区中执行以下代码:
(org-export-to-buffer 'my-ascii "*Org MY-ASCII Export*")
后续可考虑实现交互式函数、在导出调度器菜单中自动添加选项等用户友好的改进。更多细节见 https://orgmode.org/worg/dev/org-export-reference.html。
除非另有说明,导出过程的每一步仅对缓冲区的可访问部分进行操作。当选择子树导出时(参见 The Export Dispatcher),缓冲区会被缩小到所选子树的主体范围,因此缓冲区中除导出关键字外的其余文本不会进入导出结果。
参见 org-export-define-backend 与 org-export-define-derived-backend 文档字符串中的转码器及 :translate-alist 。
参见 org-export-define-backend 文档字符串中的 inner-template 。