Skip to content
Back to Blog
how-to-convert

如何将 HTML 转换为 PDF(并保留 CSS 样式)

2026-05-17 9 min read

为什么 HTML 转 PDF 会出错

跟行为异常的 PDF 导出功能“斗智斗勇”过的人都懂那种痛苦。你转换一个 HTML 文件,结果却是一团糟:布局错乱、字体丢失,或者页面看起来跟你浏览器里的预览完全不一样。问题不在于这个想法本身,而在于执行过程。不同的 PDF 渲染器在解析 CSS 方面千差万别,而且大多数通用转换器,除非你强制要求,否则它们会直接剥离或忽略样式表。 根本的冲突在于 HTML 的流式布局模型和 PDF 的页面式模型。网页内容会伸缩以适应视口,而 PDF 页面有固定的尺寸。没有滚动条,也没有动态重排。要在这两个世界之间架起桥梁,需要一个能理解 CSS 页面规则、能妥善处理嵌入字体、并且尊重 flexbox 和 grid 等现代布局的渲染器。很多工具根本做不到。 常见的失败案例总是那么令人沮丧地可以预见:Google Fonts 加载不出来,因为转换器是离线运行的;背景色消失了,因为渲染器默认进入了“打印模式”,为了省墨而移除了它们;多列布局塌陷成孤零零的一列,惨不忍睹。了解这些坑,问题就解决了一半。本指南将带你一步步避开它们,无论你用的是 CocoConvert 的 [HTML 转 PDF 工具](/convert/html-to-pdf) 还是在构建自己的工作流。

“保留 CSS 样式”到底意味着什么

在深入研究解决方案之前,我们先来明确一下“保留 CSS”到底意味着什么。在 PDF 的语境下,样式不是一个单一的东西;它分为四个不同的类别,而且并非所有转换方法都能让它们全部幸存下来。 **视觉样式**是最脆弱的。这包括颜色、边框、盒子阴影和背景图片。打印渲染器通常会默认禁用背景以节省墨水。例如,在 Chrome 自带的打印对话框中,你必须深入到“更多设置”并勾选“背景图形”,否则你的背景就会直接消失。 **排版**——字体家族、字重、字号、行高——是死是活完全取决于字体是否可用。如果你的字体是通过远程 URL 引用的,任何不获取外部资源的转换器都会回退到系统默认字体。你那漂亮的 Inter 或 Lato 字体瞬间就变成了沉闷的 Times New Roman。唯一能确保字体保留的方法就是嵌入它们。 **布局**是事情变得真正复杂的地方。像 flexbox、CSS Grid 和绝对定位这些现代 CSS 特性,基于 Chromium 的渲染器处理得很好。但像 wkhtmltopdf(它使用的是 2013 年左右的 WebKit 引擎)这样的旧引擎,会以可预见的、令人抓狂的方式搞乱现代布局。 **页面专用 CSS**,比如 `@page` 规则、`page-break-before` 和 `break-inside: avoid`,实际上在 PDF 转换中的支持比在浏览器中更好。这些 CSS 本就是为分页媒体而生的。通过使用 `@media print` 块,你可以创建只应用于 PDF 版本的样式,从而在不弄乱网站外观的情况下进行精细控制。 了解你的样式问题属于哪个类别,就能告诉你哪些工具和技术可能解决它。

使用 CocoConvert 将 HTML 转换为 PDF

CocoConvert 的 [HTML 转 PDF 转换器](/convert/html-to-pdf) 运行在现代的、基于 Chromium 的渲染引擎上。这意味着它能高度保真地处理当代 CSS——包括 flexbox、Grid、CSS 变量和 `calc()` 值。下面就教你如何获得完美的效果。 **第 1 步:准备你的 HTML 文件。** 如果你的样式在外部样式表中,你有两个选择:要么用像 Juice 这样的库将它们内联,要么确保样式表路径是相对的,并包含在你上传的文件中。CocoConvert 一次只处理一个 HTML 文件,并且不会获取远程 URL。Google Fonts 或 CDN 托管的样式表将无法加载。 **第 2 步:嵌入你的字体。** 对于自定义排版来说,这一点没得商量。将你的字体文件转换为 base64,并使用 `@font-face` 直接嵌入到 `<style>` 块中。是的,这会增加文件大小,但这是保证字体正确渲染的唯一方法。对于一个典型的正文字体(包含常规和粗体字重),这可能会给你的 HTML 文件增加 80–150 KB。 **第 3 步:上传并设置页面选项。** 上传后,CocoConvert 会提供页面尺寸(A4、Letter、Legal 或自定义尺寸)、方向和边距的选项。A4 尺寸、四边各 15mm 边距是一个可靠的默认设置。如果你要转换一个很宽的仪表盘或表格,请切换到横向。 **第 4 步:启用背景图形。** 这是最最常见的一个错误。在 CocoConvert 的选项面板中,你必须将“打印背景”切换为“开”。如果你不这样做,任何带有 `background-color` 或 `background-image` 的元素都会被渲染成纯白色。 **第 5 步:下载并验证。** 不要只是在浏览器里扫一眼就完事了。用像 Adobe Acrobat Reader 这样的专业阅读器打开它,然后通过“文件 > 属性 > 字体”来检查字体是否已正确嵌入。列表中的每种字体旁边都应该显示“Embedded Subset”。

处理局限性:CocoConvert 做不到什么

我们得坦诚一点,聊聊 CocoConvert 不是什么。它是一个功能强大的通用转换器,但某些任务超出了它的范围。提前了解这些局限性,可以让你避免在死胡同里排查问题。 **JavaScript 渲染的内容。** 如果你的页面是由 React 或 Vue 这样的框架构建的,上传源 HTML 文件是行不通的。转换器看到的是静态 HTML,而不是由 JavaScript 构建出的最终页面。对于重度依赖 JS 的页面,最好的办法是先从浏览器中保存完全渲染好的 HTML(右键 > 另存为 > 网页,全部),然后上传那个文件。或者,你就需要一个像 Puppeteer 这样的无头浏览器工具。 **交互式元素。** 表单、下拉菜单和悬停状态在 PDF 中是不可交互的。转换过程捕捉的是元素默认状态的静态快照。如果页面渲染时一个折叠面板是关闭的,那么在 PDF 里它也会是关闭的。这一点无法绕过;这是 PDF 的本质决定的。 **非常大的文件。** CocoConvert 的免费套餐有 50 MB 的文件限制,付费套餐是 200 MB。这听起来很慷慨,但一个包含大量 base64 编码图片的 HTML 文件会出人意料地很快达到这些限制。一张高分辨率的 PNG 图片就能轻易给文件增加 3-5 MB 的大小。 **复杂的 SVG 动画。** 静态 SVG 渲染得非常漂亮。但是,SVG 内部的任何 CSS 或 SMIL 动画都会定格在它们的第一帧。 CocoConvert 是为直接的、基于文件的转换而构建的。当你有一个完成的 HTML 文档,需要一个可靠的 PDF 时,它就是合适的工具。对于需要执行 JavaScript 的复杂服务器端流程,你就需要考虑像 Playwright 或 Puppeteer 这样的编程工具了。

让 PDF 输出更清晰的 CSS 技巧

如果你从一开始就考虑着 PDF输出来编写 CSS,能让你省去无数的痛苦。事后修复问题总是更难。 **使用 `@media print` 进行 PDF 专属的样式覆盖。** 这是你的秘密武器。将任何只用于 PDF 的规则包裹在 `@media print` 块中,以隐藏导航栏之类的元素,移除在打印时看起来很脏的盒子阴影,或者调整字体大小以提高页面可读性。例如: ```css @media print { nav, .sidebar { display: none; } body { font-size: 11pt; } .card { box-shadow: none; border: 1px solid #ddd; } } ``` **明确地控制分页。** 不要让渲染器自己决定在哪里分页。在一个主要章节前使用 `break-before: page` 来强制分页,并对表格、图表等元素应用 `break-inside: avoid`,以防止它们被尴尬地拆分到两页上。对于制作看起来专业的报告来说,这绝对是必不可少的。 **设置明确的 `@page` 尺寸。** 如果你知道目标页面大小,就在 CSS 中声明它。这可以防止你的样式和转换器设置之间出现不匹配,从而导致意外的文本裁剪。 ```css @page { size: A4 portrait; margin: 15mm 20mm; } ``` **在打印布局中避免使用视口单位。** 像 `vw` 和 `vh` 这样的单位对于固定的 PDF 页面来说毫无意义。对于需要跨越页面的元素,应改用 `mm`、`pt` 或百分比。 **先用 Chrome 内置的打印预览进行测试。** 在上传任何东西之前,按 Ctrl+P(或 Cmd+P)并选择“另存为 PDF”。这将使用与 CocoConvert 相同的 Chromium 引擎为你提供即时预览。这是迭代 CSS 最快的方法,不用把时间浪费在反复上传上。

转换完整网页(URL) vs. 转换 HTML 文件

转换本地 HTML 文件的方法与转换一个在线网页 URL 的方法有着本质的不同。你需要对症下药,选对工具。 **转换本地 HTML 文件**是 CocoConvert 的 [HTML 转 PDF 工具](/convert/html-to-pdf) 的强项。这种方法要求文档是自包含的。所有 CSS 都必须内联或放在 `<style>` 块中,所有图片都必须是 base64 编码或通过 ZIP 文件中的相对路径引用,字体也必须嵌入。把它想象成邮寄一个密封的包裹:渲染器需要的一切从一开始就必须在里面。这使得整个过程非常可靠。 **通过 URL 转换一个在线网页**则完全是另一码事。它需要一个能启动真实浏览器会话、导航到该 URL、等待 JavaScript 执行和网页字体下载,然后再打印成 PDF 的工具。像 Puppeteer、Playwright 或 Browserless.io 这样的服务就是为此设计的。代价是陡增的复杂性。你必须处理身份验证、Cookie 弹窗、懒加载内容以及不可预测的网络速度。 对于大多数常见任务——从模板生成 PDF 发票、导出一份报告或归档一份带样式的文档——基于文件的方法更简单,也更容易预测结果。你完全控制了输入,所以输出也是一致的。 如果你正在构建一个从实时用户数据生成 PDF 的系统,最佳实践是采用混合方法:在你的服务器上用用户数据填充一个 HTML 模板,然后将这个完全渲染好的 HTML 字符串传递给一个转换 API。CocoConvert 的 API 支持这种工作流程,可以直接在 POST 请求中接受 HTML。

验证输出:一个 PDF 质量检查清单

PDF 在你屏幕上看起来没问题,不代表它就真的万事大吉了。在你把文件发给客户或发布之前,快速过一遍这个简短但至关重要的检查清单。 **字体嵌入了吗?** 在 Adobe Acrobat Reader 中打开 PDF,然后导航到“文件 > 属性 > 字体”。你用到的每一种字体都应该列出来,并带有“Embedded Subset”标识。如果某个字体没有被嵌入,那么在任何没有安装该字体的电脑上,它都会被系统默认字体替换,从而毁掉你的设计。 **颜色准确吗?** 网页 CSS 使用的是 RGB 色彩空间。大多数用于屏幕显示的 PDF 使用 RGB 也没问题。但是,如果 PDF 是用于商业印刷的,它可能需要 CMYK 色彩空间,这是一个在初始 HTML 转 PDF 过程之后进行的独立转换步骤。 **文本可选且可搜索吗?** 试着点击并拖动来选择一行文字。如果你能选中,说明文本是真正的矢量文本,这对可搜索性和可访问性都有好处。如果你选不中,那可能是转换器把整个页面栅格化成了一张扁平的图片——对于复制粘贴、屏幕阅读器和文本搜索来说简直是一场灾难。 **页数和分页正确吗?** 快速滚动浏览每一页。有没有标题被单独留在页面底部(孤行)?表格或图表是否在不合适的地方被拆分?插图是否与它们的说明文字分开了? **文件大小合理吗?** 一份十页、以文字为主的 PDF 应该远小于 1 MB。如果它有 15 MB,那肯定有问题。罪魁祸首通常是未经压缩的图片或整个页面被意外栅格化了。 **超链接能用吗?** 点击文档中的任何链接。一个好的转换器会把 `<a href>` 标签保留为最终 PDF 中可点击的链接。CocoConvert 默认会这样做,但检查一下总没错。 花三分钟过一遍这六项检查,就能在 95% 的转换问题给别人造成麻烦之前发现它们。这最后的润色,是区分专业出品和业余作品的关键。

Ready to convert?

Try it now — fast, secure, and private.

Convert Now →