Markdown 转 PDF:我为什么选择浏览器原生打印方案?
3 min
把 Markdown 文档转成 PDF 格式看起来很简单,因为使用 html2pdf.js 配置非常方便,几十行代码就搞定了,转换成 PDF 也非常快,但是实际测试下来发现问题不少。 我在开发 Markdown 转 PDF 功能时折腾了好几天,反复修改了很多版代码,最后还是从 html2pdf.js 迁移到了浏览器原生打印方案。
Markdown 转 PDF 为什么选浏览器原生打印方案?
纯前端(无后端)转 PDF 基本就三个方案:
- 服务端渲染(Puppeteer/Playwright)- 静态站点不适用
- 客户端 JS 生成(html2pdf.js, jsPDF + html2canvas)- 位图输出
- 浏览器原生打印(
window.print())- 矢量 PDF,分页由浏览器引擎处理
我最开始是使用了 html2pdf.js ,但是它的主要问题有:
- 位图输出:PDF 其实是图片,选不了字也搜不了
- 分页不稳:长代码块、嵌套列表容易被硬切(这是最大的问题)
- 性能压力:长文档要渲染很多 Canvas,移动端很容易卡
后来改用浏览器原生打印,就完美解决了文字被硬切的问题,另外文字能选中能搜索,链接不丢,文件也更小,分页还更稳定。
核心架构:Overlay + iframe
直接 window.print() 会把整页 UI 都打印出来,我们使用 iframe 把内容分离出来,步骤如下:
- 点击“下载 PDF”
- 生成全屏 overlay 和工具栏
- 按模板生成打印样式
- 把带样式的 HTML 写入 iframe
- 用户点击打印,触发
iframe.contentWindow.print()
这样既能预览,又不会影响主页面样式。
CSS 分页关键规则
@media print {
tr, pre, blockquote, img, .katex-display {
page-break-inside: avoid;
break-inside: avoid;
}
h1, h2, h3, h4, h5, h6 {
page-break-after: avoid;
break-after: avoid;
}
table { page-break-inside: auto; }
thead { display: table-header-group; }
body {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}这些规则可以避免截断、重复表头、保留背景色。
打印对话框引导
建议在 UI 里直接提示用户这样设置:
- 目标:另存为 PDF
- 背景图形:勾选
- 页眉页脚:取消
- 纸张:A4 或 Letter
- 页边距:默认
总结
只要能接受弹出打印对话框,浏览器原生打印就是目前最稳、最省心、质量也最好的方案。 如果大家有更好的方案或相关的问题,欢迎留言讨论。
相关链接: