org: Advanced configuration
12.15 Advanced configuration
============================
Hooks
-----
Two hooks are run during the first steps of the export process. The
first one, ‘org-export-before-processing-hook’ is called before
expanding macros, Babel code and include keywords in the buffer. The
second one, ‘org-export-before-parsing-hook’, as its name suggests,
happens just before parsing the buffer. Their main use is for heavy
duties, that is duties involving structural modifications of the
document. For example, one may want to remove every headline in the
buffer during export. The following code can achieve this:
(defun my-headline-removal (backend)
"Remove all headlines in the current buffer.
BACKEND is the export back-end being used, as a symbol."
(org-map-entries
(lambda () (delete-region (point) (progn (forward-line) (point))))))
(add-hook 'org-export-before-parsing-hook 'my-headline-removal)
Note that functions used in these hooks require a mandatory argument,
a symbol representing the back-end used.
Filters
-------
Filters are lists of functions applied on a specific part of the output
from a given back-end. More explicitly, each time a back-end transforms
an Org object or element into another language, all functions within a
given filter type are called in turn on the string produced. The string
returned by the last function will be the one used in the final output.
There are filters sets for each type of element or object, for plain
text, for the parse tree, for the export options and for the final
output. They are all named after the same scheme:
‘org-export-filter-TYPE-functions’, where ‘TYPE’ is the type targeted by
the filter. Valid types are:
bold babel-call center-block
clock code comment
comment-block 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
quote-section radio-target section
special-block src-block statistics-cookie
strike-through subscript superscript
table table-cell table-row
target timestamp underline
verbatim verse-block
For example, the following snippet allows me to use non-breaking
spaces in the Org buffer and get them translated into LaTeX without
using the ‘\nbsp’ macro (where ‘_’ stands for the non-breaking space):
(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)
Three arguments must be provided to a filter: the code being changed,
the back-end used, and some information about the export process. You
can safely ignore the third argument for most purposes. Note the use of
‘org-export-derived-backend-p’, which ensures that the filter will only
be applied when using ‘latex’ back-end or any other back-end derived
from it (e.g., ‘beamer’).
Extending an existing back-end
------------------------------
This is obviously the most powerful customization, since the changes
happen at the parser level. Indeed, some export back-ends are built as
extensions of other ones (e.g., Markdown back-end an extension of HTML
back-end).
Extending a back-end means that if an element type is not transcoded
by the new back-end, it will be handled by the original one. Hence you
can extend specific parts of a back-end without too much work.
As an example, imagine we want the ‘ascii’ back-end to display the
language used in a source block, when it is available, but only when
some attribute is non-‘nil’, like the following:
#+ATTR_ASCII: :language t
Because that back-end is lacking in that area, we are going to create
a new back-end, ‘my-ascii’ that will do the job.
(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)))
The ‘my-ascii-src-block’ function looks at the attribute above the
element. If it isn’t true, it gives hand to the ‘ascii’ back-end.
Otherwise, it creates a box around the code, leaving room for the
language. A new back-end is then created. It only changes its behavior
when translating ‘src-block’ type element. Now, all it takes to use the
new back-end is calling the following from an Org buffer:
(org-export-to-buffer 'my-ascii "*Org MY-ASCII Export*")
It is obviously possible to write an interactive function for this,
install it in the export dispatcher menu, and so on.