target-counter 在 iTextPdf 和 WeasyPrint 中不工作,因其非标准 CSS 属性,依赖运行时 DOM 导航能力,而 PDF 渲染引擎为静态解析器,不执行 JS 也不维护目标引用上下文。

target-counter 在 PDF 生成中基本不可用——它不是标准 CSS 属性,而是 CSS Generated Content for Paged Media(CSS GCPM)草案里的实验性特性,目前仅被少数浏览器(如旧版 Prince XML、部分版本的 Chrome 打印预览)有限支持,iTextPdf、weasyprint、pdfkit 等主流 PDF 工具链完全不解析该函数。
为什么 target-counter 在 iTextPdf / WeasyPrint 中不工作
PDF 生成工具对 CSS 的支持集中在 CSS 2.1 和部分 CSS 3 布局模块(如 @page、counter()),但 target-counter() 依赖运行时 DOM 导航能力:它需要查找目标元素(如 id="sec2")、读取其计数器值、再插入到伪元素中。而大多数 PDF 渲染引擎是静态 HTML 解析器,不执行 JS,也不维护跨元素的“目标引用”上下文。
常见表现:
- 生成的 PDF 中对应位置空白,或显示
target-counter(section)字符串原样 - 日志无报错,但样式规则被静默忽略(因非标准函数)
- 即使在 Chrome 打印预览里能短暂显示,导出为 PDF 后也丢失
替代方案:用 counter() + 语义化结构手动模拟
真正可行的是放弃 target-counter,改用标准 counter() 配合明确的嵌套结构。例如生成目录页码,关键不是“反向查目标”,而是让标题和目录项共享同一计数上下文。
实操要点:
- 给目录容器设
counter-reset: toc;,每个<li class="toc-item">写counter-increment: toc; - 标题(如
<h2 id="sec2"></h2>)必须在同一计数作用域内,且顺序在目录项之后(否则编号未递增) - 用
::before插入编号:content: counter(toc) ".";,而非尝试target-counter(sec2) - 若需跳转链接,用标准
<a href="#sec2"></a>——iTextPdf和weasyprint都支持锚点跳转,但页码需靠后端逻辑补全
iTextPdf 场景下必须由 Java 层补足页码
iTextPdf 不解析 CSS 计数器生成的页码,它只渲染静态 HTML 输出。所谓“目录页码”,实际是 Java 代码在生成 PDF 后遍历 Anchor 对象,调用 getDestinationPage() 获取真实页码,再回填到目录项中。
典型流程:
- 先用
XMLWorkerHelper或Converter把 HTML 转成Document,此时目录项只有文字,无页码 - 遍历所有
Anchor,用pdfDoc.getPageNumber(anchor.getReference())查页码 - 用
ColumnText或Paragraph手动重写目录行,拼接 “第 3 页” 字符串 - 注意:必须等全部内容写入完成、页码确定后才能执行此步,否则
getPageNumber()返回 0
容易被忽略的兼容细节
很多人卡在“目录生成了但页码全是 0”,问题往往不在 CSS,而在 PDF 构建阶段的时序错误:iText 的页码获取必须发生在 document.close() 之后,或至少所有章节已触发分页。另外,target-counter 的诱惑在于“写一次 CSS 全自动”,但现实是 PDF 生成里没有“自动”——页码本质是布局结果,只能后置计算,无法前置声明。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











