news 2026/9/14 17:41:21

用CSS排版Markdown:从书稿到印刷级PDF的完整工作流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用CSS排版Markdown:从书稿到印刷级PDF的完整工作流

写 Markdown 的时候我从来没觉得排版是问题,直到有一次我把一份十几万字的书稿丢给工具导 PDF,出来的文件像一份“带标题的纯文本打印稿”——没有目录页码,页眉像贴上去的,代码一断页就血肉模糊。那一刻我意识到:Markdown 擅长的是“写”,它压根不管“排”。后来我花了几周时间折腾出一条把 Markdown 排成一本真正书籍的完整工作流,这就是 Folio。它不是编辑器,也不是现成的转换器,而是一套“md 书稿 → 印刷级 PDF”的排版方案,核心思路是:继续用 Markdown 写,让 CSS 干排版的活。这篇文章我会把整个链路、关键 CSS 参数、遇到的坑和现在日常使用的方式全部摊开讲,适合那些写文档写到一定规模、开始追求出版质感的人。

1. 为什么非要把 Markdown “排成书”:写稿的人最懂这种痛

先说清楚 Folio 想解决什么问题。我见过太多人用 Markdown 写博客、写 README 都很顺手,可真到了要交付一份技术手册、课程讲义甚至一本小册子的时候,就抓瞎了。Markdown 本身只负责结构,标题是标题、段落是段落、代码是代码,它不管这页留白多少、这行会不会孤零零掉到下一页。而这些恰恰是“书”和“文档”的分界线。

1.1 我试过的三条弯路,每一条都值得说

最早我用的是 Pandoc 加 LaTeX 模板。Pandoc 从 Markdown 转 LaTeX 再出 PDF,这条路非常强大,但也很折磨人。LaTeX 对中文支持要配置 xelatex 和 ctex 宏包,字体、章节格式、页眉页脚全部要用另一套语言改模板,一行 CSS 都不认识。我只想调整一下标题字号,就得去翻.cls 文件,改完还得在终端里等半分钟编译。这种学习曲线对“以写作为主、不想整天跟模板搏斗”的人完全是负担。

然后我试过 Typora 直接导出 PDF。Typora 作为 Markdown 编辑器确实舒服,所见即所得,导出 PDF 的效果也比普通转换器好。但它解决的只是“从编辑器到 PDF”这一步,真正排版层面的控制力很弱:目录页码要靠第三方插件,封面和版本页没法优雅定制,代码块在跨页时经常把上一页露半行,奇偶页页眉也没法做“左页书名右页章节名”这种书刊规范。用来出差评报告行,用来做书不行。

市面上还有很多在线转换工具,把 Markdown 粘进去就能出 PDF。这类工具适合应急,不适合做书。它们对图片路径、公式渲染、字体嵌入的处理很不稳定,经常出现“本机预览正常、发给别人就乱码”的问题。更致命的是没有命令行接口,没法接入自动化构建流程。

1.2 Folio 给自己定的三条规矩

经过了上面这些折腾,我给这个项目定了几条非常明确的原则。

第一,稿件格式必须是纯 Markdown,不许为排版发明新的书写语法。写书的人只需要会最常用的那些语法:标题、段落、列表、表格、代码块、图片、公式。排版的事全部交给模板。

第二,版面效果必须能精细控制,因为“像书”和“是书”之间的差距全在细节里。页码放版心下面居中是文档,放天头外侧靠近切口才是书;标题左边顶格是网页,首行缩进两字两端对齐才是书。这些细节用 CSS 逐个抠,能做到像素级可控。

第三,构建流程必须可重复、可自动化、可以通过命令行一键执行。稿子改一个词,重新执行一次构建命令就能得到新的 PDF,而不是打开编辑器手动导出。

围绕这三条, Folio 的定位变成:一套“Markdown 编写 + HTML 中间层 + CSS 分页媒体渲染”的开源排版工作流。下面就是它的完整实现。

2. Folio 的转换链路:一个 md 文件是怎么一步步变成 PDF 的

Folio 的全套管线用一句话概括就是:Markdown 先转成语义化的 HTML,再用 CSS 分页媒体引擎渲染成 PDF。这个过程分四步:清洗、切章、渲染、装配。

2.1 为什么选 HTML 加 CSS 分页媒体,而不是 LaTeX

核心原因是“用最低的学习成本拿到最强的版面控制力”。写网页的人对 CSS 已经很熟,即使不熟,CSS 也比 LaTeX 模板好懂得多。现代浏览器和渲染引擎支持一套叫 CSS Paged Media 的分页媒体规范,它把网页的盒模型扩展到了多页场景:可以用 @page 规则控制纸张大小和页边距,可以用 string-set 捕捉章节标题塞进页眉,可以用 target-counter 把目录里的条目和真实页码关联起来,这些都是做书必需的底层能力。

相比 LaTeX,这套方案有肉眼可见的优势:改动即时反馈,CSS 文件的某个属性改一个值,重新构建就是一份新样式的 PDF;技术栈通用,社区里和 CSS 排版相关的资料远比 LaTeX 模板多。LaTeX 在学术排版领域依然是王者,但如果你做的是技术手册、小说、小册子、产品说明书这类内容,HTML 加 CSS 这套组合拳效率更高。

2.2 管线四步:清洗、切章、渲染、装配

具体到 Folio 的实现,整个构建脚本大概处理这么几件事。

第一步是清洗原始 Markdown。作者写稿时往往很随意,Windows 换行符和 Unix 换行符混用,段落中间有零散空格,表格里有复制 Excel 带过来的隐藏制表符。清洗脚本要统一换行符,去除行尾空格,规范表格分隔符,把手写的 HTML 标签清理干净。这一步最容易被忽略,但它是后面所有步骤稳定性的基础。

第二步是切章。Folio 规定一本书放在一个 manuscript 目录下,每章是一个独立的 Markdown 文件,文件名就是排序依据。构建脚本读取一个轻量级配置文件,里面声明了书名、作者、章节目录顺序,然后把各个文件按顺序读进来,为每一章生成包含 chapter 类的 HTML 片段。切章的意义在于:每一章可以独立做分页控制,比如新章必须从奇数页开始。

第三步是渲染。这一步把 Markdown 转成 HTML。我用的是 markdown-it 这个解析器,并挂上了几个插件:一个负责把数学公式从 $...$、$$...$$ 转成 KaTeX 的 HTML 输出,一个负责代码高亮,一个负责把相对图片路径改写为构建目录下的真实路径。转出来的 HTML 是干净的语义标签,h1、h2、p、pre、table、figure,一个 CSS 类都没加,类和作用全部由后面那张样式表决定。

第四步是装配。脚本把每一章的 HTML 拼接到一个完整的 HTML 模板里,前面插入封面页、版权页、目录页,然后把全套 print.css 以 link 标签方式引入。模板里还会注入文档元信息,比如 title 标签用于 PDF 元数据,meta 标签可以控制 PDF 书签层级。最终这个 HTML 文件交给渲染引擎,输出 PDF。

2.3 渲染引擎选型:Prince 和 WeasyPrint 的实际差异

这块是整个管线里最影响成品质量的一环。我实际对比了两款引擎:Prince 和 WeasyPrint。Prince 是商业软件,个人和非商业用途免费下载,对 CSS 分页媒体规范的支持是目前最完整的。Paged.js 那些高级特性,比如运行页眉 target-counter、leader 点线,在 Prince 里都直接可用。WeasyPrint 是纯开源实现,由 Python 驱动,日常写作和简单书籍足够,但某些高级特性支持不上,比如复杂的 running header 需要绕路实现,表格断页表头重复在部分版本上还会失效。

我在 Folio 里把两款引擎都做成了可选后端,默认推荐 Prince。不是说开源不行,而是当你需要把排版抠到“每个细节都符合书籍规范”时,Prince 能少折腾几个晚上。但有一点必须说清楚:无论是 Prince 还是 WeasyPrint,都用同样的 HTML 输入和 CSS 样式表,所以前端那套知识是完全通用的,换引擎不换写法。唯一要注意的是,不同引擎对 CSS 属性的支持程度有差异,我会在第五节专门讲踩过的坑。

3. 真正像“书”的排版细节,全在 CSS 里

HTML 负责结构,CSS 负责让它“像书”。这一节我拿出 Folio 里真正在用的核心参数,一条条说明它们为什么存在。这些都是我在浏览器默认样式基础上一点点磨出来的。

3.1 页面规格与中文版心

做书先定纸张和版心。Folio 默认设置是 A5 开本,也就是 148mm 乘 210mm,这个尺寸对技术手册、小说、诗歌集都合适,拿着不累,打印成本也低。页面规则长这样:

@page { size: 148mm 210mm; margin: 24mm 18mm 22mm 20mm; }

这里的 margin 上下左右故意不一样。上下是 24mm 和 22mm,左右是 20mm 内侧、18mm 外侧。为什么内侧要比外侧宽?因为装订会吃掉一部分版面空间,内边距不给够,靠近书脊的文字就容易被夹进阴影里。单页文档不需要考虑这个问题,书必须要。

真正讲究的书还会区分左右页,让奇数页和偶数页的边距镜像对称。Prince 和 WeasyPrint 都支持 @page:left 和 @page:right 规则:

@page:left { margin: 24mm 18mm 22mm 20mm; } @page:right { margin: 24mm 20mm 22mm 18mm; }

这样翻开书,左边页和右边页的版心在视觉上是居中的,像摊开的书本而不是两页独立的文档。

正文的中文排版参数我放在根元素上:

html { font-size: 10.5pt; } body { font-family: "Source Han Serif SC", "Noto Serif CJK SC", serif; line-height: 1.75; text-align: justify; text-justify: inter-ideograph; } p { margin: 0; text-indent: 2em; }

正文用宋体类字体,10.5pt 是传统书刊的五号字大小,行高 1.75 比网页常用的 1.6 到 1.7 略松一点,因为纸面阅读密度和屏幕不一样。段落之间不加垂直间距,而是靠首行缩进两字来区分段落,这也是中文书籍的标准排版方式。读者一眼就能看出,这排的是书,不是网页长文。

3.2 标题、目录和页码的联动

标题在书里不只是字号更大的文字,它是整个文档的导航骨架。Folio 里每章只有一个 h1,表示章标题;h2 是节标题,h3 是小节标题。章标题必须另起一页,并且从右页开始:

h1 { string-set: chaptitle content(text); break-before: page; break-after: page; }

string-set 这行非常重要,它把 h1 的文本内容捕捉成一个字符串变量,后面页眉要用它。加上 break-before: page 和 break-after: page,确保每一章的标题独占一页,且下一节内容从新页开始。如果你希望书籍更紧凑,也可以把 break-after 去掉,让章标题页之后正文直接开始。

目录页是 Folio 里“由代码自动生成”的部分。构建脚本扫描所有 h1、h2 的文本和 id,生成一段目录 HTML,然后用这段 CSS 把条目和真实页码关联起来:

.toc a { text-decoration: none; color: inherit; } .toc a::after { content: leader(".") target-counter(attr(href), page); }

target-counter 会去计算目录里每个链接指向的那个元素最终落在第几页,然后把页码填进去。leader(".") 生成一长串点线,把标题和页码连接起来,这就是书里常见的点线目录样式。这个能力几乎只有分页媒体引擎才有,浏览器里做不到,也是我坚持用 Prince 做默认后端的原因之一。

3.3 页眉页脚的奇偶页处理

页眉是书和文档的另一条分界线。Folio 采用传统书籍的页眉方案:左右页眉不一样,偶数页放书名,奇数页放当前章节名,页码放在页脚外侧。相关 CSS 长这样:

@page:left { @top-center { content: string(booktitle); font-size: 8pt; color: #666; } @bottom-right { content: counter(page); font-size: 9pt; } } @page:right { @top-center { content: string(chaptitle); font-size: 8pt; color: #666; } @bottom-left { content: counter(page); font-size: 9pt; } }

这里 string(booktitle) 是在模板里预设的静态字符串,string(chaptitle) 用的就是上一节提到的 string-set 捕获的章节名。页码 counter(page) 会自动递增。页眉字号比正文小一号,颜色用灰色而不是纯黑,避免干扰阅读。这就是“书”和“普通打印文档”的差距:普通文档的页眉是重复的文档名,书的页眉会跟着章节变化。

3.4 断页、孤行寡行的强制约束

这一段是排版细节里最“硬核”的,也是普通 Markdown 转换器最不愿意处理的部分。做书必须控制好哪些元素不能跨页断开,哪些元素之间不能断开。Folio 的核心约束如下:

h2, h3 { break-after: avoid; } h2, h3 { break-before: auto; } p { orphans: 2; widows: 2; } pre, table, figure, blockquote { break-inside: avoid; } table { break-inside: auto; }

逐条解释。break-after: avoid 的意思是标题后面至少要跟着一行正文,如果标题恰好落在页面最底部,后面没有正文空间,那就把标题一起推到下一页,避免出现“标题孤零零悬在页脚”的情况。orphans 和 widows 控制段落的孤儿行和寡行:orphans 是段落被断到下一页时,留在上一页的最少行数;widows 是段落被断页时,留在当前页底部的最后一行至少要有几行。设为 2 的话,就不会出现一页底部只有一行文字的情况。

表格和代码块尽量整体搬动到下一页,不拆得七零八落。但如果表格特别长,一行都放不下,有时候必须允许它跨页,否则前面会空出大块白。这些约束在浏览器里不痛不痒,在分页媒体里却是是否“专业”的关键区分点。

4. 表格、代码、公式和图片:Markdown 里的四个老大难

Markdown 最大的优点是把内容约束在语义层面,但表格、代码、公式、图片这四样东西会狠狠考验排版方案。Folio 对这个问题的处理,是我觉得最值得拿出来分享的部分。

4.1 表格:别让宽表毁掉版面

Markdown 表格写起来很爽,但排进书里经常会出问题。最典型的是表格列太多、内容太长,超过版心宽度,直接溢出页面。Folio 的思路是“先瘦身,再固化”。

瘦身环节发生在清洗阶段。我加了一个检测脚本,扫描每张表格的列数和每列的最大字符数,如果发现超过版心宽度上限,就在构建时输出警告,提醒作者考虑拆表、转列表或者精简列项。这个警告在命令行里是黄色高亮,作为构建流程的一部分,能第一时间约束内容。

如果表格宽度合适但仍可能跨页,就在 CSS 里固定它的断页行为:

table { width: 100%; border-collapse: collapse; font-size: 9pt; } thead { display: table-header-group; } tr { break-inside: avoid; } td, th { border-top: 0.5pt solid #ccc; border-bottom: 0.5pt solid #ccc; padding: 3pt 6pt; vertical-align: top; }

thead 加 display: table-header-group 的作用是:当表格跨页时,表头会在下一页自动重复一遍。长表格跨页后读者仍能看到列名,不至于对着两列数字发懵。tr 的 break-inside: avoid 保证同一行的记录不会被劈成两半。单元格边框用 0.5pt 细线,比网页常见的 1px 在打印纸上更干净。另外,如果你习惯在 Excel 里排好表格再贴进 Markdown,Folio 的清洗脚本允许直接粘贴制表符分隔的纯文本,它会自动转成标准 Markdown 表格语法,省去手动对齐的工作。

4.2 代码块:断行、缩进和高亮

技术类书稿最头疼的就是代码。引用别人的代码还好说,真正的难题是代码太长放不进一行,或者代码块跨页时从中间断开,下一行代码只剩个右括号,对不上上下文。

Folio 对代码采用双管齐下。一是在预处理阶段用 highlight.js 给代码做语义高亮,生成带 span 的 HTML;二是在 CSS 里对代码块做严格的断页控制:

pre { break-inside: avoid; font-family: "JetBrains Mono", "Source Code Pro", monospace; font-size: 8.5pt; line-height: 1.5; background: #f7f7f7; padding: 8pt 10pt; border-radius: 2pt; } code { font-family: inherit; }

pre 的 break-inside: avoid 让代码块作为一个整体不被拆断,如果当前页剩余空间不够,整个块就移到下一页。这是一个简单粗暴但有效的策略,适合大多数情况。短代码块效果好,超长代码块确实会留下大块空白。所以我还加了一个“行内代码折叠”的工具函数:处理超长行时,优先在运算符和逗号位置插入断行机会,并缩进续行,而不是让代码溢出页面边界。

实际项目中,一份几百页的技术手册,代码块的占比往往在三分之一以上。这个环节稳了,整本书的质量就稳了一大半。

4.3 数学公式:KaTeX 与引擎的配合

数学公式在 Markdown 里写作 $...$ 或 $$...$$,但在 HTML 层面有两套渲染路线。MathJax 输出的是 SVG 或 HTML 加 CSS,KaTeX 输出的是 HTML 加 CSS 加少量字体。Prince 内置的 MathML 支持并不完美,所以我要求在预处理阶段直接把公式转成 KaTeX 的 HTML 输出,这样 Prince 只需要渲染普通 HTML 和字体,不需要懂 MathML。

具体操作是在 markdown-it 挂上 katex 插件:

const md = require("markdown-it")({ html: true, linkify: true, typographer: true }).use(require("markdown-it-katex"), { throwOnError: false, output: "html" });

这样源文件里写的:

质能方程 $E = mc^2$ 是现代物理的基石。

在构建出来的 HTML 里会变成带 KaTeX 类和内联样式的 span,CSS 里给这类元素设置字体不变色即可。实测下来,KaTeX 的排版质量和 LaTeX 非常接近,而渲染速度比 MathJax 快不少,对构建体验很有帮助。需要注意 KaTeX 依赖的特定字体文件在 Prince 里要能被正确加载,否则公式会显示为空白或回退字体,建议把 KaTeX 的字体目录一并复制进构建目录。

4.4 图片路径:从相对路径到资源装配

图片路径问题是最烦人的一个。作者写稿时用的是相对路径,比如images/architecture.png,但如果构建脚本在另一个目录运行,或者图片放在子文件夹里,路径就全对不上了。Folio 的清洗脚本会统一处理这个事:它会读取出 Markdown 里所有的图片引用,将相对路径解析后复制到构建目录下的 assets 文件夹,然后把 Markdown 里的路径改写为 assets/ 下的新路径。这样即使原稿移动了图片文件,只要 manuscript 目录整体迁移,构建就不会断。

CSS 层面,我给图片设置了自适应版心的约束:

img { max-width: 100%; height: auto; break-inside: avoid; } figure { text-align: center; break-inside: avoid; } figcaption { font-size: 8.5pt; color: #666; margin-top: 4pt; }

图片宽度最大不超过版心,高度自动等比缩放。figure 和 figcaption 整体不允许跨页断开,否则图注会孤零零跑到下一页,读者对不上号。如果你有特别宽的图想横着放,可以给 img 单独加一个 rotate 类,用 CSS transform 旋转 90 度并调整页面方向,这在 Folio 里也有现成模板,不过我更建议直接把这种图拆出来放附录,别打断正文阅读节奏。

5. 踩坑复盘:头两版样书里的真实事故

Folio 不是一次写成的,中间有两版样书排出来效果不堪入目。这一节我按真实排查顺序复盘几个典型事故,这些坑可能会出现在任何想用 CSS 做书的人面前。

5.1 目录页码对不上:target-counter 不是万能的

第一版样书的目录页一片混乱:有的章节后面没有页码,有的页码少了一页。我一开始以为是 markdown-it 生成的链接 id 有重复,后来逐条检查发现,问题出在 target-counter 的 attr(href) 解析上。Prince 里必须写成attr(href url)才能确保 href 被当作 URL 解析,只写attr(href)在某些版本里会拿到一串原始字符串,导致目标匹配失败。

修正方式是把目录 CSS 改成:

.toc a::after { content: leader(".") target-counter(attr(href url), page); }

另外还有一个隐蔽问题:构建脚本生成的目录链接 href 是#chapter-1这样的片段,但如果页面模板里有多个同名 id 的锚点,Prince 会匹配第一个,导致页码偏差。现在的构建脚本会在切章时为每个 h1、h2 生成带唯一序号的 id,彻底杜绝同名冲突。

5.2 中文标点悬挂:浏览器默认行为与打印的矛盾

做书的人都希望正文两端对齐时标点能悬挂在行尾,这样右边不会出现难看的锯齿。浏览器和分页引擎对 Chinese 标点悬挂的支持都不太完整。Prince 提供了 text-hanging-punctuation 属性,但实测它对中文全角引号、逗号、句号的支持并不稳定,某些组合下会导致整行间距被拉得很开。

我最终的妥协方案是:不追求严格的标点悬挂,而是通过两端对齐加合理的断行规则让标点尽量自然落在行尾。对于左右对齐后某些行间距特别大的情况,我在清洗脚本里加了一个可选的“标点挤压”后处理,把行尾是全角标点的行稍微压缩字距。这个后处理不是百分百保险,但它让样书从“一眼看出是浏览器渲染”变成了“接近出版社的标准”。

5.3 表格跨页后表头丢失:display 属性的兼容性

前面提到 thead 的 display: table-header-group 可以重复表头,但这个属性在 WeasyPrint 的某些版本里失效。我实际遇到的情况是:表格跨页后,下一页直接出现数据行,列名全部丢失。排查后确认是 WeasyPrint 对 table-header-group 的重复渲染支持不完整。

如果你必须用 WeasyPrint 后端,我的建议是尽量把表格列数控制在 3 到 4 列以内,减少跨页概率;或者干脆给大表加一个禁止跨页的类,让它整体挪到下一页。Prince 在这块的兼容性就好很多,所以我在 Folio 的配置文档里明确标注:默认引擎用 Prince,只有轻量文档才建议切 WeasyPrint。

5.4 超链接变蓝变下划线:打印样式要重置

这套书稿里有很多交叉引用和外部链接,第一版样书里它们全部变成浏览器经典的蓝色加下划线,打印出来既脏又分散注意力。原因很简单:Prince 基于 WebKit 内核,链接默认样式没有被重置掉。修复方法是把所有链接在打印样式表里统一重置:

a { color: inherit; text-decoration: none; }

保留链接的可点能力,但剥掉视觉上的“网页感”。如果某些链接真的是需要读者访问的网址,可以在排版时用 ::after 把 URL 以小字号打印出来,作为脚注信息。这样既不破坏正文排版,又保留了可追溯性。

5.5 封面和版权页的“页面边界”问题

最后还有一个容易忽视的坑:封面页、版权页和正文页不应该有相同的页眉页脚。第一版样书里,封面页居然也带上了页码和页眉,看起来非常业余。解决方法是给封面和版权页的容器手动加一个 @page 命名规则:

.cover { page: cover; } @page cover { margin: 0; @top-center { content: none; } @bottom-center { content: none; } }

原理是 CSS 分页媒体允许命名页面,然后通过元素上的 page 属性把特定元素分配到那个页面上下文。封面页用无页眉页脚、无边距的独立页面规则,正文再回到默认的书籍页规则。这个功能浏览器里没有,但分页媒体引擎都支持,是 Folio 成书质感的重要一环。

6. 把 Folio 接进日常写作流水线

工具做得再好,如果接不进写作流程就会被闲置。Folio 落地的最后一公里,是和编辑器、版本管理、持续构建的整合。

6.1 VS Code 里保存即编译

写作时最舒服的姿势是:改完一个字,保存,PDF 自动更新。我用 VS Code 的 Tasks 功能接入 Folio。在 .vscode/tasks.json 里定义一个监听任务:

{ "version": "2.0.0", "tasks": [ { "label": "folio build", "type": "shell", "command": "folio build", "group": "build", "problemMatcher": [] }, { "label": "folio watch", "type": "shell", "command": "folio watch", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [] } ] }

folio watch 会监听 manuscript 目录下的所有 .md 文件,文件一变就重新构建。配合 VS Code 的 PDF 预览插件,左边写 Markdown,右边实时看排好的 PDF,体验和 Typora 的所见即所得不一样,但更接近“印刷厂原稿”的感觉。Prince 的可执行文件名是 prince,安装时把它加进 PATH,Folio 就能直接调用。如果你用 WeasyPrint 后端,也可以把命令换成 python -m weasyprint,不过效果差异在第五节已经说过了。

6.2 与 Typora 等编辑器的互补使用

有人问我:Folio 会不会抢 Typora 的活?恰恰相反,两者是互补关系。Typora 是一个优秀的 Markdown 编辑器,它的强项是“写”,让你专心写内容,实时看格式。Folio 的强项是“排”,把写完的稿子变成一本像样的书。我的日常流程是:在 Typora 里写作和初稿修改,因为它的编辑体验最顺滑;稿子定稿后回到 VS Code 跑 Folio 构建 PDF。Typora 也能导出 PDF,但那是“文档级”的 PDF,Folio 出的是“书籍级”的 PDF,两者定位不同,不冲突。

如果你习惯在 VS Code 里用 Markdown 插件写作,也是一样的逻辑。Folio 是一个构建层工具,它不关心你用哪个编辑器写稿,反正它只读取 manuscript 目录下的 .md 文件。

6.3 配合 Git 做版本管理

用 Git 管理书稿比管理代码还要有价值。Markdown 是纯文本,Git 能精确看到每一个字的变化,这对写长文档来说太关键了。我在 Folio 里把 manuscript 目录和 styles 目录纳入 Git 仓库,build 目录放进 .gitignore,因为 PDF 是生成产物,不该进版本库。每次定稿一个章节就 commit 一次,tag 一个版本号。等整本书完成时,回看提交记录,能清楚看到每一章的演化轨迹。

6.4 还能往哪里扩展

Folio 目前的输出是 PDF,但中间的 HTML 中间层意味着它天然可以扩展出其他格式。Word 方向可以接 Pandoc 把 HTML 转成 docx,适合需要交出版社做后续编辑的场景;ePub 方向可以用同一套 HTML 源文件配合另一套 screen.css 输出电子书;甚至可以直接把 HTML 中间层发到 WordPress 作为文章内容,实现 Markdown 书稿到博客的无缝转换。我在 Folio 的脚本里预留了一个 export 参数,后面可以按folio export --format epub的形式扩充插件。

不过要泼一盆冷水:不要指望一个命令解决所有格式的美观问题。ePub 的排版引擎和 PDF 完全不同,Word 的排版逻辑更不一样。Folio 的价值不在于“一次转换,处处完美”,而在于“一份源稿,多端可控”——你只需要维护内容,每个输出端各有自己的模板,这才是文档工程的正确姿势。

根据我这段时间的实际体验,Folio 最大的回报不是那个漂亮的 PDF,而是让我重新敢写长文了。以前一想到要排版就犯懒,现在只管写,写完了跑一条命令,剩下的交给 CSS。最后分享一个小技巧:在打印样书之前,先拿一小章节跑一遍构建,把 CSS 里的断页、孤行问题全部调好,再放开整本书。否则几百页的一次性构建如果版面出错,修改后重新构建的等待时间会让人非常烦躁。排版这件事,和写代码一样,先让小规模跑通,再上全量。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 17:40:47

Zola 主题实战:tilde 极简博客主题的安装、配置与定制指南

Zola 主题实战:tilde 极简博客主题的安装、配置与定制指南 【免费下载链接】zola A fast static site generator in a single binary with everything built-in. https://www.getzola.org 项目地址: https://gitcode.com/GitHub_Trending/zo/zola 本指南以 Z…

作者头像 李华
网站建设 2026/9/14 17:40:43

React Native鸿蒙适配:useEffect定时器优化方案

1. React Native与鸿蒙跨平台开发背景解析在移动应用开发领域,跨平台技术已经成为提升开发效率、降低维护成本的关键解决方案。React Native作为Facebook推出的跨平台框架,通过JavaScript桥接原生组件的方式,实现了"一次编写&#xff0c…

作者头像 李华