在写 pandoc 文档时, 有时候需要定义自已的格式,
或者在插入了图片, 而将 pandoc 文档转换为其它格式时, 图片居中的方式有所不同.
对我来说, 经常将 pandoc 文档转换为 docx, html 和 pdf 格式. 若 pandoc
中包含了图片, 通常将其居中显示. 对于 docx, html 和 pdf 来说,
图片居中的使用的格式语法不尽相同. 此时, 我的做法是定义一种插入图片的语法,
然后编写一个过滤器对这种语法进行解析, 根据目标文档格式来插入不同的居中语法.
这里不过多描述 pandoc. 简单的说, pandoc 是一个文档转换工具. 源文档是 markdown
格式的文本文件, 或是符合 pandoc-markdown 语法格式的文本文件, 通过 pandoc
可以转换为其它格式的文档, 比如 html, pdf 等. pandoc
可以将源文档转换为数十种格式的文档, 如图 1 所示.
下面为 pandoc 文档转换过程.
INPUT --reader--> AST --filter--> AST --writer--> OUTPUT
其中:
INPUT 是指源文件, 符合 markdown 语法格式或扩展的 pandoc-markdown
语法格式的文本文件.
reader 是指源文件读取器. 调用 pandoc 时, reader 首先读取源文件,
然后进行语法分析, 最后将源文件转换为一种中间格式 AST.
AST 是 pandoc 文档转换的中间格式, 表示为 “abstract syntax tree” (AST,
抽象语法数). 实际上是用 json 文档来表示的.
filter 就是文中提到的过滤器. 由于 AST 是一种结构化的 JSON 文件,
所以可以通过编程对其进行控制, 比如添加一些内容, 删除一些内容, 替换一些内容等.
这种处理 AST 的程序就是过滤器.
AST(2) 是经过 filter 处理的 json 文档. 仍然是一种结构化的文本.
writer 是读取 AST(2)读取器, 它读入经过 filter 处理 (过滤) 的 json 文件,
并且按照目标格式进行转换, 最后输出到目标文档.
OUTPUT 是指目标文件, 比如 docx 文件, html 文件等.
下面是转换过程的另一种表示, 着重于格式描述.
source format
↓
(pandoc)
↓
JSON-formatted AST
↓
(filter)
↓
JSON-formatted AST
↓
(pandoc)
↓
target format
pandoc 将源文件转换为 AST 时, 将源文件中的文本按 pandoc-markdown
语法分成不同的元素, 各种元素都属于某种类型. 在编写自定义格式时, 一定是使用了
pandoc-markdown 的特殊语法,
并且在过滤时能够根据元素类型比较容易获取自定义格式文本.
主要元素类型包括:
Plain, 普通文本, 非段落
Para, 段落
LineBlock, 行快, 多个无换行符的行
CodeBlock, 代码块, 可以带属性
RawBlock
BlockQuote, 块引用
OrderedList, 有序列表
BulletList, 无序列表
DefinitionList, 定义列表
Header, 章节
HorizontalRule, 水平表尺
Table, 表格
所有的元素类型可以查看这里.
我在自定义格式时, 使用的类型为 代码块
, 一是代码块比较容易获取,
二是代码块还可以带一些属性. 具体的自定义格式如下:
~~~{.dzfimg path="" title=""}
~~~
其中:
.dzfimg 为代码块的语法着色类型
path 属性表示图片所在位置
title 属性表示图片的说明
比如
~~~{.dzfimg path="./convert_to_image.jpg" title="图 1 pandoc 转换目标文档格式"}
~~~
html 的图片居中语法:
<center><img src="img_path"><br>"img_title"center>
pdf 的图片居中语法:
\begin{center}
\{includegraphics[width=xxcm, height=xxcm]{img_path}}
img_title
\end{center}
docx 图片居中没有相应的语法, 采取的思路是: 先插入图片, 再加上 title (也即
caption).
可以使用多种脚本语言编写过滤器, 比如 Lua, Haskell, python 等. 这里使用 python
编写过滤器.
"""
由 pandoc 提供了 pandocfilters 模块, 方便用户编写过滤器.
"""
from pandocfilters import toJSONFilter, Str, Para, Image, attributes, \
get_value, RawBlock
"""
针对目标格式为 pdf 的过滤函数
"""
def latex(x):
return RawBlock('latex', x)
"""
针对目标格式为 html 的过滤函数
"""
def html(x):
return RawBlock('html', x)
"""
过滤函数. 关于目标格式为 docx 的过滤在此过滤函数中.
"""
def dzfimglink(key, value, fmt, meta):
if key == 'CodeBlock':
[[ident, classes, keyvals], code] = value
if 'dzfimg' in classes:
path, val = get_value(keyvals, 'path', '')
title, val = get_value(val, 'title', '')
if 'latex' == fmt:
return ([latex('\\begin{center}')] +
[latex('\\includegraphics{' + path + '}')] +
[latex(title)] +
[latex('\\end{center}')])
if 'html' == fmt:
link = '
' + title + ''
return ([html(link)])
if 'docx' == fmt:
image = Image(attributes({}), [], [path, title])
return (Para([image, Str('\n' + title)]))
"""
call dzfimglink
"""
if __name__ == "__main__":
toJSONFilter(dzfimglink)
通过过滤器, 在编写 pandoc 文档时可以自定义多种语法格式, 可以提高写作效率.
本来没有学过 python, 因为要编写这个过滤器, 所以才学了 python, 现在发现 python
挺有趣的.
另外, 还可以结合自定义语法, 方便处理 pandoc 中插入的 plantuml 代码, graphviz
代码 (-_-)
.