Markdown转中文PDF不再乱码:md2pdf双引擎搞定字体嵌入


md2pdf 是一个将 Markdown 文档转换为排版精良的 PDF 的命令行工具,尤其针对中文、日文、韩文(CJK)文字的排版进行了深度优化!

Markdown转PDF,中文不配好好排版吗!

你费了半天劲写好的中文Markdown文档,转成PDF那一刻,字体不对、行距挤成一团、表格被拦腰砍断——这事儿你就说气不气人吧!



明明写的时候好好的,怎么一导出就全乱了?中文字体在PDF里要么变成方块,要么干脆消失。表格跨页直接断开,代码块被截成两半。你以为是软件不行,换了一个又一个,从浏览器打印到Pandoc加LaTeX,折腾一圈发现——不是工具不行,是这条路本身就走不通。

问题出在哪?出在大多数Markdown转PDF工具,本质上干的是同一件事:把Markdown变成HTML,再用浏览器引擎打印成PDF。

这套流程对付英文还行,一到中文就露馅了。行距太紧、文字对齐出现难看的“河流”状空隙、表格被整个推到下一页。

更狠的是,你在屏幕上看到的字体和最终PDF里嵌入的字体,根本不是同一个东西。最离谱的是macOS:系统自带的中文字体PingFang,任何第三方PDF引擎都读不了、嵌不进去。因为它的字形存放在苹果私有的hvgl表里,第三方引擎根本不认。你对着屏幕调了半天,导出那一刻全白费。



你看到的字和你拿到的字,是两码事

先搞清楚一个基本事实:PDF里的字体和屏幕上的字体,不是一回事。你在编辑器里看到的是渲染引擎临时拼出来的画面,浏览器或者文本编辑器调用系统字体库,把字形画在屏幕上给你看。但PDF不一样,PDF要把字体数据打包进文件里——这叫字体嵌入。不嵌入的话,别人打开你的PDF,电脑里没装这个字体,显示出来的就是另一副模样。

大多数Markdown转PDF工具走的路线是:Markdown → HTML → 浏览器引擎打印 → PDF。这条路在英文世界跑得很顺,因为英文字体到处都有、格式标准统一。但中文不一样。中文字体动辄几万个字形,字体文件巨大,嵌入成本高。很多工具干脆不嵌,或者嵌一半丢一半。更麻烦的是字体授权和技术格式——苹果的PingFang字体,字形数据放在一个叫hvgl的私有表里,Adobe的PDF规范不认识这个表。所以任何第三方PDF生成库调用PingFang,结果都是——拿不到字形数据,PDF里要么空着,要么回退成别的字体。

这就怪了。你电脑上明明装了PingFang,屏幕上也显示得好好的,但转出来的PDF就是不认。因为屏幕显示用的是操作系统渲染引擎,PDF生成用的是另一套字体解析逻辑。两套系统不互通。你在屏幕上看到的“PingFang”,和PDF引擎试图读取的“PingFang”,根本是两回事。

两条路,都不好走

面对中文字体嵌入这个死结,Markdown转PDF的工具链通常走两条路。

第一条路:继续用浏览器引擎,但想办法把字体塞进去。这条路的问题是——你得搞定字体文件。PingFang你搞不定,因为苹果不让第三方读hvgl表。那换别的中文字体行不行?行,但得让用户自己下载安装。Noto Sans CJK,谷歌开源的中文字体,几万个字形,文件大小上百兆。让每个用户自己去下载安装,再配置到工具里——门槛直接拉满。

第二条路:绕过浏览器引擎,直接用排版引擎生成PDF。这条路的问题是——你得有一个能处理中文混排的排版引擎。LaTeX能搞定中文,但得装XeLaTeX、配CJK宏包、指定字体路径。一套下来四五个GB的依赖,配置过程堪比考级。Typst是新出来的排版语言,语法像Markdown,能力接近LaTeX,但问题是——它和Pandoc之间还缺一座桥。你得先把Markdown转成Typst语法,再用Typst编译成PDF。

两条路都走得通,但都走得累。

md2pdf干了件什么事:把两条路都铺了一遍

md2pdf这个工具,做的事情就是:把上面两条路各做一遍,让你自己选。

默认引擎走Typst路线。Pandoc把Markdown转成Typst标记语言,Typst再用一套专门调过中文混排的模板编译成PDF。速度快、文件小、结果可重现。不需要装LaTeX那套庞然大物,只需要Pandoc和Typst两个命令行工具。Typst本身对中文的支持是原生的——它不像LaTeX那样需要额外装CJK宏包、配字体映射。直接指定一个中文字体就能跑。

但这个路线有个局限:它用的是你系统里已安装的字体。如果你在macOS上想用PingFang——Typst也读不了hvgl表。所以md2pdf提供了第二条路:CoreText引擎。这个引擎只在macOS上能用,原理是:Pandoc把Markdown转成HTML,然后一个轻量的WebKit程序用系统CoreText框架渲染页面,用真正的PingFang字体画出文字,再按段落、列表项、表格行的边界裁切成A4页面。这是唯一能把真PingFang塞进PDF的办法。

两条引擎,覆盖两种场景。默认引擎跨平台、轻量、快速;CoreText引擎专供macOS用户追求PingFang原汁原味。不是二选一,是都给你备好了。

字体预设:你不用自己调

中文字体配置最烦人的地方是什么?是你要自己去查字体名字、写配置参数、调试半天发现名字写错了一个字母。md2pdf把这事儿做成了预设。

默认预设用Noto Sans CJK SC配Helvetica Neue。Noto是谷歌开源的中文字体,覆盖中日韩所有常用汉字,字形干净、可读性强。在macOS上一条brew命令就能装。hiragino预设用macOS自带的Hiragino Sans GB配Helvetica Neue。songti预设用macOS自带的宋体配Libertinus Serif。wenkai预设用LXGW WenKai配Libertinus Serif。pingfang预设专门留给CoreText引擎用。

每个预设都是一套完整的字体搭配方案——中文字体管中文、拉丁字体管英文、各司其职。你不用去想“正文用什么、标题用什么、英文用什么”。一条命令搞定。

跟竞品比,到底差在哪

市面上Markdown转PDF的工具一抓一大把,md2pdf跟它们比到底特别在哪?

md-to-pdf是Node.js工具,底层走Chromium无头浏览器渲染HTML再打印PDF。优点是代码高亮好看、支持Mermaid图表。缺点是——它本质上还是HTML打印那套逻辑,分页会切断表格和代码块。而且Chromium对中文字体的处理依赖于系统字体回退机制,你没法精确控制PDF里嵌入的是哪个字体。

Pandoc加LaTeX是另一条路。排版质量天花板很高,但代价是一整套LaTeX工具链——四五GB的安装包、XeLaTeX引擎配置、CJK宏包、字体映射文件。你花一个周末调通模板,换来的是专业级的排版效果。但这个过程对普通用户来说太痛苦了。

mdxport-cli跟md2pdf思路接近——也是Pandoc加Typst的路线。但它需要单独安装中文字体,不像md2pdf那样内置了多套字体预设一键切换。

md2pdf的核心差异在两点。第一点是双引擎设计:Typst引擎跨平台轻量快速,CoreText引擎专供macOS用户解决PingFang嵌入问题。别的工具没有这个二选一——你要么接受浏览器引擎的局限性,要么硬扛LaTeX的复杂度。第二点是字体预设:五套预设覆盖了从开源免费到macOS自带的各种场景,一条命令切换。你不用去翻字体配置文档。

表格跨页、代码块截断:为什么别人搞不定

Markdown转PDF最让人抓狂的两个场景:表格跨页被拦腰斩断,代码块在页面底部被截掉一半。这两个问题在浏览器打印路线里几乎无解。因为浏览器打印PDF的逻辑是“把一整页HTML内容按A4尺寸切分”——它不考虑表格行的完整性,也不管代码块能不能完整放下。切到哪算哪。

Typst引擎不一样。Typst是专业的排版系统,它的分页逻辑会考虑元素的完整性。表格行作为一个整体,不会被拆到两页。代码块如果一页放不下,会整体挪到下一页。这是排版引擎和打印引擎的本质区别——一个是“把内容排进页面”,一个是“把页面切出内容”。

md2pdf的Typst模板还专门针对中文混排做了优化。行距、字距、标点挤压、中西文间距——这些在浏览器打印里要么没有、要么靠CSS硬调的参数,在Typst里是排版引擎原生支持的能力。

但事情没那么简单

md2pdf解决了问题,但不是没有代价。

Typst引擎需要Pandoc 3.1.3以上版本。Linux发行版自带的Pandoc往往很旧,你得自己去下载新版。Typst本身需要单独安装。虽然是两个轻量级工具,但毕竟不是开箱即用。

CoreText引擎第一次运行需要从源码编译渲染器。macOS上需要Xcode Command Line Tools。而且CoreText引擎需要图形会话——在纯SSH或者无头CI环境里跑不了。换句话说,自动化构建流水线里只能用Typst引擎。

字体预设虽然方便,但预设之外的字体你得自己指定。指定方式是通过--font参数传入字体家族名称。你得知道自己系统里安装的字体叫什么名字——这个查找过程对不熟悉字体管理的用户来说依然是个门槛。

你真正该问的问题是

Markdown转PDF这事儿,本质问题不是“工具有没有”,而是“你到底要什么”。如果你只是要把一份英文技术文档转成PDF发给同事看,浏览器打印或者md-to-pdf够用了。但如果你要处理的是中文文档、日文文档、韩文文档——混排文本——那你就得面对字体嵌入、行距控制、标点挤压、表格跨页这一整套排版问题。

md2pdf的价值在于:它把Typst这个新排版引擎和Pandoc这个老牌转换工具捏在了一起。Typst处理排版质量,Pandoc处理Markdown解析。中间加一层模板专门处理中文混排。你不用去学Typst语法,也不用折腾LaTeX配置。写你的Markdown,跑一条命令,出来一份排版合格的PDF。

但有一个细节你可能没注意到:md2pdf的PyPI包名叫md2pdf-cjk。cjk三个字母说明了一切——这个工具从设计之初就把中日韩文字当成头等公民。不是“顺便支持中文”,是“专门为中文做的”。