使用技巧约 3 分钟阅读

用代码把 HTML 转成 PDF:哪些 HTML 和 CSS 会被打印出来

实测 HTML 转 PDF 能打印什么:网格、弹性盒、@page 页面尺寸、网页字体和重复表头都行,JavaScript 不会运行。附 curl 示例。

PDF123 团队 · 更新于 2026-11-05

HTML 转 PDF用 WeasyPrint 打印你的 HTML 和 CSS,用的不是浏览器。测试中,网格、弹性盒、@page 的尺寸和页边距、来自公开地址的网页字体、重复的表头和页码都排出来了;JavaScript 完全没有运行。可以发一个 .html 文件,也可以发一个 ZIP,里面放 HTML 和它的样式表、图片、字体。页面在一个公开地址上时,网址转 PDF直接从地址完成同样的事。

HTML 转 PDF 到底会打印哪些 HTML 和 CSS?

调用就是一个 multipart 请求,文件作为 fileInput:

curl -X POST https://pdf123.xyz/api/v1/convert/html/pdf \
  -F [email protected] \
  -o page.pdf

用命令行也一样:

pdfx html-to-pdf page.html -o page.pdf

两条命令我都于 2026-10-23 在本机运行的一份服务器上执行过,测试页里写了 @page { size: A5; margin: 12mm }。curl 返回 HTTP 200,pdfx 退出码为 0,pdfinfo 报告 1 页,419.528 x 595.276 点,即 A5。

下表来自一组很小的测试页,每个都用同样的方式转换,再用 pdfinfo、pdftotext、pdffonts 和 pdfimages 检查,排版问题则看渲染出的图片。表里写的是实际转出来的结果,不是规范的承诺。

功能 我发送的内容 转出来的结果
用 CSS 定页面尺寸 @page { size: A5; margin: 12mm } 419.528 x 595.276 pt(A5)
自定义页面尺寸 @page { size: 210mm 99mm } 595.276 x 280.63 pt
没有 @page 规则 一个裸 HTML 文件 595.276 x 841.89 pt(A4)
CSS 网格 grid-template-columns: 1fr 2fr 1fr; gap: 10mm,六个单元格 三列,宽度比 1:2:1,第 4 到 6 个单元格在下一行
弹性盒 display: flex; justify-content: space-between,三个项目 项目从行的左边缘分布到右边缘
来自公开地址的网页字体 @font-face 指向公共 CDN 上的 .woff 文件 以 @font-face 里的名字嵌入 PDF
打包在 ZIP 里的字体 @font-face 用相对 url() 指向压缩包里的文件 嵌入 PDF
机器上安装的字体 font-family: "Times New Roman" 在我的测试机上嵌入了;取决于服务器有哪些字体
哪里都没有的字体 font-family: "NoSuchFontAnywhere", monospace 通用的 monospace 回退成机器上的某个字体
强制分页 break-before: page 和 page-break-before: always 3 页:第一次分页前的内容,之后每次分页一页
表格跨多页,表头在 <thead> 里 100 行的表格,A5 4 页,每页顶部都有表头行
表格跨多页,表头是普通行 同一个表格,去掉 <thead> 4 页,只有第 1 页有表头行
页码和页眉文字 @page { @bottom-center { content: "Page " counter(page) " of " counter(pages) } } Page 1 of 3、Page 2 of 3、Page 3 of 3,以及页眉文字
@media print 一个 print-only 块和一个 screen-only 块 打印块打印了,屏幕块没有
较新的 CSS 自定义属性、calc()、linear-gradient、border-radius、transform: rotate() 全部画出来了
filter 和 position: sticky filter: blur(3px) 和 position: sticky 模糊被忽略;粘性定位的块按普通块打印
JavaScript 一段设置 textContent 并调用 document.write 的脚本 两段文字都没有出现;静态文字出现了
<noscript> <noscript> 里的文字 打印了
公开地址上的图片 <img src="https://..."> 嵌入了
私有地址上的图片 地址是环回地址的 <img> 没有加载;打印了替代文字;调用仍返回 200

从代码生成 HTML 时,有两行最要紧。只有表头放在 <thead> 里,每一页才会重复表头行。另外,脚本写进页面的任何内容都到不了 PDF,在浏览器里才构建内容的页面,打印出来的是它最初的空壳:单页应用发来一个空的 <div id="root">,得到的就是一份空 PDF。

怎样把 HTML 连同样式表、图片和字体一起发过去?

放进 ZIP。压缩包根目录有 index.html 就用它,否则用第一个名字以 .html 结尾的文件;压缩包里的 .htm 文件不会被选中。相对路径从 HTML 文件自己所在的文件夹算起。一个含 index.html、css/style.css、img/logo.png 和 fonts/roboto.woff 的 ZIP,这样转换:

curl -X POST https://pdf123.xyz/api/v1/convert/html/pdf \
  -F [email protected] \
  -o c.pdf

结果是一页,样式表生效,打包的字体以 @font-face 里的名字嵌入,300 x 120 的图片也在。第二个 ZIP 根目录没有 index.html,只有 site/page.html 和 site/logo.png,转换器用了 site/page.html,旁边的图片加载成功。完全没有 .html 文件的 ZIP 返回 HTTP 400,reason 是 unsupported_file_type:The ZIP archive must contain an .html file. Markdown 转 PDF 的做法不同,它的图片路径从压缩包根目录算起,见不装 Pandoc 和 LaTeX,把 Markdown 转成 PDF。

你自己电脑上的文件无法按路径访问。页面链接到的任何东西,要么在 ZIP 里,要么在公开地址上。公开的 http 或 https 地址上的资源,会在渲染时抓取。抓取器每次转换最多允许 500 次抓取,每个资源最大 64 MiB,每个请求 15 秒。私有或内部网络的地址会被拦截,所以上面那张环回地址上的图片打印的是替代文字。渲染默认在 180 秒后停止。

字体是不同机器之间结果会不同的地方。服务的 Docker 镜像安装了 DejaVu、Liberation、Noto 和 Noto CJK 字体包。CSS 里写的字体名,服务器上没有就会换成回退字体,所以在你机器上看着正常的 PDF,在服务器上可能用了另一种字体。用 @font-face 加载字体,无论来自 ZIP 还是公开地址,都能避开这个问题。

背后是哪个渲染器、哪个版本?

转换器是 WeasyPrint,由 Python 运行,没有无头浏览器。服务的 Docker 镜像安装 weasyprint==70.0,路径上是别的版本时构建会直接中止;我这台测试服务器产出的 PDF,Producer 字段里是 WeasyPrint 70.0(pdfinfo 会打印它)。WeasyPrint 的文档说它不基于 WebKit 或 Gecko 这样完整的渲染引擎,它的 CSS 支持页面写着弹性盒模块“works for simple use cases but is not deeply tested”,网格模块“works for simple cases, but has some limitations”。复杂的布局在依赖它之前请先测试,上面的表只覆盖了我试过的情形。

如果 HTML 只能通过一个地址拿到呢?

网址转 PDF接收 urlInput,自己抓取页面,再用同一个引擎渲染,所以同样不会运行 JavaScript。在浏览器里才构建内容的网站,打印出来没有那些内容。

curl -X POST https://pdf123.xyz/api/v1/convert/url/pdf \
  -F urlInput=https://example.com \
  -o output.pdf

它返回 HTTP 200 和一份 1 页 A4 的 PDF,Producer 字段是 WeasyPrint 70.0;下载的文件名固定是 output.pdf。只接受公开的 http 和 https 地址:把环回地址作为 urlInput,返回 HTTP 400,reason 为 url_not_allowed,信息是 Private, local and internal network addresses are not allowed. 页面需要登录,或者你要自己控制确切的字节,就把 HTML 存下来,改用 HTML 转 PDF。

常见问题

HTML 转 PDF 会运行 JavaScript 吗?

不会。测试里,一段设置元素文字并调用 document.write 的脚本,没有往 PDF 里添加任何内容。引擎只读 HTML 和 CSS,所以请发送脚本本来会生成的那份 HTML。

怎样设置纸张大小和页边距?

在你自己的 CSS 里写 @page 规则,例如 @page { size: A5; margin: 12mm }。这个工具没有纸张或页边距的选项,也不添加默认样式表,没有规则的页面转出来是 A4。

为什么字体和我浏览器里的不一样?

CSS 里的字体名是在服务器上查找的,不是在你的电脑上,服务器没有的名字会回退成别的字体。把字体打包进 ZIP,或用 @font-face 从公开地址加载,它就会嵌入 PDF。

能打印需要登录的页面吗?

网址转 PDF 不行,它不发送 cookie。登录状态下把 HTML 存下来,连同资源放进 ZIP,再用 HTML 转 PDF。

打开工具
在浏览器里完成处理,不加水印,文件处理完就会删除。
打开工具