页码必须用 css @page 规则配合 counter(page) 实现,Puppeteer 需启用 displayHeaderFooter: true,且 @page 必须位于顶层 CSS;分节页码需结合 counter-reset 与 counter-increment 控制。

html 转 pdf 时页码只能靠 CSS `@page` 实现
原生 HTML 不支持 `第1页` 这类写法在 PDF 中自动更新页码——浏览器打印或工具(如 wkhtmltopdf、WeasyPrint、Puppeteer)生成 PDF 时,页码必须由 CSS 的 @page 规则控制,且仅对块级内容生效。javaScript 动态插入的页码文本在 PDF 渲染阶段早已失效。
用 @page + counter(page) 插入基础页码
这是最通用、兼容性最好的方式,适用于 Puppeteer、wkhtmltopdf、WeasyPrint 等主流工具(注意:部分旧版 wkhtmltopdf 对 counter() 支持不完整,建议用 0.12.6+)。
@page { @bottom-center { content: "第 " counter(page) " 页"; } }
-
counter(page)是唯一被广泛支持的页码变量,不能写成page或pages -
@bottom-center可替换为@top-right、@bottom-left等,但位置关键词必须严格匹配(大小写敏感) - 不能在
@page内使用 class、id 或 js 表达式;所有样式需内联或通过@page { margin: 2cm; }控制
Puppeteer 中需显式启用 printBackground 和 displayHeaderFooter
很多人写了 @page 却没显示页码,根本原因是 Puppeteer 默认禁用页眉页脚渲染。即使 CSS 正确,也必须传参开启:
await page.pdf({ path: 'output.pdf', format: 'A4', displayHeaderFooter: true, printBackground: true, headerTemplate: '', footerTemplate: '第 页' });
-
displayHeaderFooter: true是硬性前提,否则@page中的@top/@bottom完全不生效 -
footerTemplate和headerTemplate是字符串 HTML,其中class="tuc-19bc10f7-67e190-0 pageNumber tuc-19bc10f7-67e190-0"和class="tuc-19bc10f7-67e190-0 totalPages tuc-19bc10f7-67e190-0"会被 Puppeteer 自动替换(仅限这两个 class 名) -
printBackground: true确保背景色、边框等正常输出,否则页脚可能空白
页码起始值与分节控制要用 counter-reset 和 counter-increment
封面、目录、正文需要不同页码格式(如封面无页码、目录用罗马数字、正文用阿拉伯数字),纯靠 @page 不够,得结合 HTML 结构和 CSS 计数器:
立即学习“前端免费学习笔记(深入)”;
body { counter-reset: page 0; } .section-cover { counter-reset: page; } .section-toc { counter-reset: page; } .section-main { counter-reset: page 1; } @page { @bottom-center { content: counter(page, decimal); } } .section-toc::before { counter-increment: page; content: ""; }
-
counter-reset: page N重置页码计数器,N是起始值(0表示第一页显示为 1) - 必须配合
counter-increment: page才能推进计数,否则页码始终为重置值 - 分节页码在 WeasyPrint 中效果稳定;Puppeteer 的
footerTemplate不支持动态计数器,只能靠服务端预生成 HTML 分节
页码不是“插”进去的,是 PDF 渲染引擎在分页时按规则注入的。最容易忽略的是:displayHeaderFooter: true 这个开关,以及 @page 规则必须放在顶层 CSS(不能套在 .container 里),否则整段规则静默失效。