pandoc 中编写图片居中过滤器

在写 pandoc 文档时, 有时候需要定义自已的格式,
或者在插入了图片, 而将 pandoc 文档转换为其它格式时, 图片居中的方式有所不同.
对我来说, 经常将 pandoc 文档转换为 docx, html 和 pdf 格式. 若 pandoc
中包含了图片, 通常将其居中显示. 对于 docx, html 和 pdf 来说,
图片居中的使用的格式语法不尽相同. 此时, 我的做法是定义一种插入图片的语法,
然后编写一个过滤器对这种语法进行解析, 根据目标文档格式来插入不同的居中语法.

什么是 pandoc

这里不过多描述 pandoc. 简单的说, pandoc 是一个文档转换工具. 源文档是 markdown
格式的文本文件, 或是符合 pandoc-markdown 语法格式的文本文件, 通过 pandoc
可以转换为其它格式的文档, 比如 html, pdf 等. pandoc
可以将源文档转换为数十种格式的文档, 如图 1 所示.


图 1
图 1 pandoc 转换目标文档格式

pandoc 文档转换过程

下面为 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 转换目标文档格式"}
~~~

docx, pdf, html 的居中语法

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 = '<center><img src=' + path \
                    + '><br>' + title + '</center>'
                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
代码 (-_-).

猜你喜欢

转载自blog.csdn.net/ding_yingzi/article/details/80140575