[转载]使用 Pandoc 与 Markdown 生成 PDF 文件
原文地址: https://jdhao.github.io/2017/12/10/pandoc-markdown-with-chinese/
For English version of this post, click here.
更新时间:2019-11-18
本文主要写如何使用 Pandoc 从 Markdown 文件生成格式精美的 PDF 文件,也包含了一些 遇到的问题的解决办法。
你需要什么
在正式开始文章之前,首先你需要安装下面的这些工具才能在后续正常进行操作:
-
首先当然是 Pandoc,下载最新的安装包 ,安装完以后,记得把 Pandoc 安装目录加入系统
PATH
变量。 -
TeX 发行版。请确保你的系统已经安装了 TeX 软件,你可以使用 TeX Live 或者 MiKTeX,安装完 成之后可能需要设置
PATH
,推荐安装 TeX Live。 -
一个强大的文本编辑器,当然首推 Sublime Text. 当然了,Visual Studio Code 和 GitHub Atom 也是不错的选择。
使用 Pandoc 从 Markdown 生成 PDF 文件
背景介绍
Pandoc 可以很方便地对不同 Markup 语言的文件进行格式转换 ,因此被誉为格式转换的「瑞士军刀」。使用 Pandoc 把 Markdown 文件转为 PDF 文件, 实际上包含两个步骤:
- 第一步, Markdown 文件被转为 LaTeX 源文件。
- 第二步,调用系统的
pdflatex
,xelatex
或者其他 TeX 命令,将.tex
文件转 换为最终的 PDF 文件 (见上图)。
由于我的文档是中文,并且包含引用,表格等比较复杂的格式,在文件转换过程中遇到了 一些问题,下面介绍具体解决办法。
如何处理中文
Pandoc 默认使用的 pdflatex
命令无法处理 Unicode 字符,如果要把包含中文的
Markdown 文件转为 PDF,在生成 PDF 的过程中会报错。我们需要使用 xelatex
来处理
中文,并且需要使用 CJKmainfont
选项指定支持中文的字体。 在 Windows 系统中,对
于 Pandoc 2.0 版本以上,可以使用以下的命令生成 PDF 文件:
pandoc --pdf-engine=xelatex -V CJKmainfont="KaiTi" test.md -o test1.pdf
CJKmainfont
后面跟的是支持中文的字体名称。那么如何找到支持中文的字体呢? 首先
,你需要知道你所使用的语言的 language
code, 例如,中文(即
Chinese)的 language code 是 zh
。 然后使用 fc-list
命令查看支持中文的字体(对
于 Windows 系统,fc-list
命令在安装 TeX Live 完整版以后可以使用, Unix 系统一
般会预装这个程序):
fc-list :lang=zh # zh 是中文的 「language code」
系统输出如下图所示:
字体的名称就是字体文件位置后面的字符串,由于字体名称可能会包含空格,因此在引用
的时候需要加上引号,例如 -V CJKmainfont="Source Han Serif CN"
。
在 Pandoc 2.0 版本中, --pdf-engine
命令取代了原有的 --latex-engine
命令
。对于
Linux 系统,Pandoc 版本可能比较老,上述生成 PDF 的命令可能并不适合,正确的命令
如下 (在 Pandoc 1.12.3.1 验证):
pandoc --latex-engine=xelatex -V mainfont='WenQuanYi Micro Hei' test.md -o test.pdf
在 Linux 系统上,找出支持中文的字体的方式与 Windows 系统是一样的。
使用问题以及技巧
block quote, table 以及 list 未能正确渲染
原因是在 Pandoc 中 block quote, list 以及 table 前需要空一行。 另外 block quote 中每一行渲染成 PDF 未能正确换行,所有行的文字都跑到了一行,可以通过强制在 原 block quote 的每一行后面加上空格来解决。
给 block code 加上 highlight
Pandoc 支持给 block code 里面的代码加上背景高亮,并提供了不同的高亮主题,支持非 常多的语言。要列出 Pandoc 提供的高亮方案,使用下面命令,
pandoc --list-highlight-styles
要列出所有支持的语言,使用下面命令,
pandoc --list-highlight-languages
要使用语法高亮,Markdown 文件中的 block code 必须指定语言,同时在命令行使用
--highlight-style
选项,例如:
pandoc --pdf-engine=xelatex --highlight-style zenburn test.md -o test.pdf
以上命令,使用了 zenburn
主题, 另外,也推荐使用 tango
, zenburn
或者
breezedark
高亮主题。
给 section 加上编号以及给文章加上目录
默认情况下,生成的 PDF 不含目录,同时各级标题不含编号,仅仅字体大小有变化,要给
各个 section 加上编号,可以用 -N
选项;加上目录,可以使用 --toc
选项。 一个
完整例子如下:
pandoc --pdf-engine=xelatex --toc -N -o test.pdf test.md
给链接加上颜色
根据 Pandoc user guide 的说明,我们可以通过 colorlinks
选项给各种链接加上颜色
,便于和普通文本区分开来:
colorlinks
add color to link text; automatically enabled if any of linkcolor, filecolor, citecolor, urlcolor, or toccolor are set
同时,为了精确控制不同类型链接颜色,Pandoc 还提供了对不同链接颜色的个性化设置选 项:
linkcolor, filecolor, citecolor, urlcolor, toccolor
color for internal links, external links, citation links, linked URLs, and
links in table of contents, respectively: uses options allowed by xcolor,
including the dvipsnames, svgnames, and x11names lists
例如,如果我们想给 URL 链接加上颜色,并且 urlcolor
要设为 NavyBlue
, 可以使
用下面的命令:
pandoc --pdf-engine=xelatex -V colorlinks -V urlcolor=NavyBlue test.md -o test.pdf
其他链接的颜色可以按照上述方式设置。
值得注意的是,urlcolor
选项并不能给文中直接展示的 raw URL link 加上颜色。 要
给直接展示的 URL link 加上颜色,可以用 <>
包围要展示的链接,例如
<www.google.com>
。
另外,也可以使用选项 -f gfm
,参考这里
。完整命令如下,
pandoc --pdf-engine=xelatex -f gfm -Vurlcolor=cyan -o test.pdf test.md
使用 -f gfm
的一个缺点是 gfm 不支持公式
,因此如果在 Markdown 中包含公式,
将不能正确渲染。解决办法是去掉 -f gfm
flag,或者使用 Pandoc 自带的 markdown
格式。
更改 PDF 的 margin
使用默认设置生成的 PDF margin 太大,根据 Pandoc 官方 FAQ, 可以使用下面的选项更改 margin:
-V geometry:"top=2cm, bottom=1.5cm, left=2cm, right=2cm"
完整命令为:
pandoc --pdf-engine=xelatex -V geometry:"top=2cm, bottom=1.5cm, left=2cm, right=2cm" -o test.pdf test.md
Markdown 文件中使用斜杠 backslash 会出错
原始的 Markdown 格式,支持在文件中使用 backslash,但是 Pandoc 把 backslash 以及
后面的内容理解成 LaTeX 命令,因此在编译包含 backslash 的文件时,可能会遇到
undefined command
错误或者更加奇怪的错误
。 参考这里以及这里
,解决办法是让 Pandoc 把 Markdown
文件当成正常的 Markdown,不要解读 LaTeX 命令,使用下面的 flag:
pandoc -f markdown-raw_tex
或者,也可以用两个 backslash 表示字面意义的 backslash,例如 \\columnwidth
或
者如果 backslash 及后面文字原本就是一个命令, 用 inline code block 包含起来。
给 inline code 加上背景色
将 Markdown 转为 PDF 时,由于 Pandoc 使用 LaTeX 中的 \textt
命令来表示 inline
code,所以 inline code 是没有背景色的。为了增加 inline code 的可读性,我们可以
修改 \texttt
命令,给文本添加背景色。首先建立 head.tex
文件,在其中加入以下
命令:
% change background color for inline code in
% markdown files. The following code does not work well for
% long text as the text will exceed the page boundary
\definecolor{bgcolor}{HTML}{E0E0E0}
\let\oldtexttt\texttt
\renewcommand{\texttt}[1]{
\colorbox{bgcolor}{\oldtexttt{#1}}
}
在使用 Pandoc 转换 Markdown 文件时,加上 -H
选项来引用 head.tex
文件,例如
pandoc --pdf-engine=xelatex -H head.tex test.md -o test.pdf
这样生成的 PDF 中,inline code 就会有灰色的背景色,背景颜色可以根据自己喜好修改。
更改引用 (block quote) 的风格
默认情况下,当我们把 Markdown 文件转为 PDF 时,Pandoc 使用 LaTeX 中的 quote
环境来表示 block quote。生成的 PDF 中,引用文字只是被缩进了,看起来很不明显。
我们可以自己定义一个 quotation 环境,给环境加上背景颜色和缩进。将下面的设置写入
到 head.tex
中:
\usepackage{framed}
\usepackage{quoting}
\definecolor{bgcolor}{HTML}{DADADA}
\colorlet{shadecolor}{bgcolor}
% define a new environment shadedquotation. You can change leftmargin and
% rightmargin as you wish.
\newenvironment{shadedquotation}
{\begin{shaded}
\quoting[leftmargin=1em, rightmargin=0pt, vskip=0pt, font=itshape]
}
{\endquoting
\end{shaded}
}
%
\def\quote{\shadedquotation}
\def\endquote{\endshadedquotation}
当你想把 Markdown 转为 PDF 时,可以使用下面的命令:
pandoc -H head.tex test.md -o test.pdf
生成的 PDF 中引用的效果如下:
参考
将设置放入 head.tex
文件中
由于将 Markdown 转为 PDF 需要诸多选项与设置,将这些设置都写在命令行上,既浪费时
间,也不利于小修小改,所以可以将常用的一些命令都放到 head.tex
文件中,然后在
转换 Markdown 文件的时候引用该文件即可。
例如,可以将设置页面宽度的命令,给 inline code 着色的命令,以及设置链接颜色的命
令统一放入 head.tex
中:
\usepackage{fancyvrb,newverbs}
\usepackage[top=2cm, bottom=1.5cm, left=2cm, right=2cm]{geometry}
% change background color for inline code in
% markdown files. The following code does not work well for
% long text as the text will exceed the page boundary
\definecolor{bgcolor}{HTML}{E0E0E0}
\let\oldtexttt\texttt
\renewcommand{\texttt}[1]{
\colorbox{bgcolor}{\oldtexttt{#1}}
}
%% color and other settings for hyperref package
\hypersetup{
bookmarksopen=true,
linkcolor=blue,
filecolor=magenta,
urlcolor=RoyalBlue,
}
下次使用的时候,直接用 -H
选项引用以上文件即可。
List 嵌套层次太多的问题
读者Karl Liu反应,如果使用 Markdown List 嵌套的层次太多(具体是超过六层),使用 Pandoc 生成的 PDF 文件的时候,会出 现错误
! LaTeX Error: Too deeply nested.
具体讨论见这个 issue,作者提出的解
决方法是,在 head.tex
文件中加上如下设置:
\usepackage{enumitem}
\setlistdepth{9}
\setlist[itemize,1]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,2]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,3]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,4]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,5]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,6]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,7]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,8]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\setlist[itemize,9]{label=\(</span><span class="nv">\bullet</span><span class="s">\)}
\renewlist{itemize}{itemize}{9}
\setlist[enumerate,1]{label=\(</span><span class="nv">\arabic</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,2]{label=\(</span><span class="nv">\alph</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,3]{label=\(</span><span class="nv">\roman</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,4]{label=\(</span><span class="nv">\arabic</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,5]{label=\(</span><span class="nv">\alpha</span><span class="o">*</span><span class="s">\)}
\setlist[enumerate,6]{label=\(</span><span class="nv">\roman</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,7]{label=\(</span><span class="nv">\arabic</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,8]{label=\(</span><span class="nv">\alph</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\setlist[enumerate,9]{label=\(</span><span class="nv">\roman</span><span class="o">*</span><span class="nb">.</span><span class="s">\)}
\renewlist{enumerate}{enumerate}{9}
在编译 PDF 的时候,加上 -H head.tex
选项。
使用 Sublime Text build system 生成 PDF
写完 Markdown,再切换到 terminal 使用 Pandoc 生成 PDF 以及预览,颇为麻烦,因此我 使用 Sublime Text 的 build system 来简化编译生成 PDF 以及预览的整个过程。PDF 预 览使用了轻量级的 Sumatra PDF reader。
一个示例的 build system 如下,
{
"shell_cmd": "pandoc --pdf-engine=xelatex --highlight-style=zenburn -V colorlinks -V CJKmainfont=KaiTi \"${file}\" -o \"${file_path}/${file_base_name}.pdf\" ",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"working_dir": "${file_path}",
"selector": "text.html.markdown",
"variants":
[
{
"name": "Convert to PDF and Preview",
"shell_cmd": "pandoc --pdf-engine=xelatex --highlight-style=zenburn -V colorlinks -V CJKmainfont=KaiTi \"${file}\" -o \"${file_path}/${file_base_name}.pdf\" &&SumatraPDF \"${file_path}/${file_base_name}.pdf\" ",
// "shell_cmd": "start \"$file_base_name\" call $file_base_name"
}
]
}
在命令中使用到了引号,必须使用 backslash escape。 可以从 这里 下载这个 build system。
系统无法识别 Pandoc 的问题
不知什么原因,上述把 Markdown 文件转为 PDF 的 build system 在编译 Markdown 文件 时,突然给出如下错误提示:
‘pandoc’ is not recognized as an internal or external command, operable program or batch file.
通过查询 sublime 文档
,发现我们可以在 build system 里面加上 path
变量。因此,我对上述的 build
system 进行了调整,新的 build system 如下:
{
"shell_cmd": "pandoc --pdf-engine=xelatex --highlight-style=zenburn -V colorlinks -V CJKmainfont=\"Source Han Serif SC\" \"${file}\" -o \"${file_path}/${file_base_name}.pdf\" ",
"path": "C:/Users/east/AppData/Local/Pandoc/;%PATH%",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"working_dir": "${file_path}",
"selector": "text.html.markdown",
"variants":
[
{
"name": "Convert to PDF and Preview",
"shell_cmd": "pandoc --pdf-engine=xelatex --highlight-style=zenburn -V colorlinks -V CJKmainfont=\"Source Han Serif SC\" \"${file}\" -o \"${file_path}/${file_base_name}.pdf\" &&SumatraPDF \"${file_path}/${file_base_name}.pdf\" ",
"path": "C:/Users/east/AppData/Local/Pandoc/;%PATH%",
// "shell_cmd": "start \"$file_base_name\" call $file_base_name"
}
]
}
使用新的 build system 以后,一切恢复正常。
笔记云同步
云同步可以选择坚果云。同步速度快,并且支持多个文 件夹同步,每个月有 1G 免费上传流量,对于文档同步来说,足够使用了。当然也可以使 用自己的网盘或者云存储,都是可以的,看自己喜好。
注:以后有更新的话,会优先更新文章开头提到的英文版本,由于精力有限,此版本更新 将不会很频繁,以修复错误为主。