如何调试WeasyPrint生成的PDF在主流阅读器损坏无报错问题
我尝试用WeasyPrint通过HTML和CSS生成Python报表,整个过程耗时远超预期,还遇到了PDF损坏却找不到原因的问题,具体情况如下:
一开始我以为是Bootstrap冲突,所以所有样式都从零编写,基于官方仓库的代码修改。
我仅在HTML代码中复制部分行、修改ID新增了如下区块:
<article id="execution-results"> <h2 id="execution-results-title">Execution Results</h2> </article>
生成的PDF在Firefox中提示this document might not be displayed correctly,内容完全无法阅读;在Adobe Reader中提示There was an error processing a page. There was a problem readin the document(110);仅能在Foxit Reader中正常显示,无法满足多环境兼容需求。
我通过python -m weasyprint .\report.html .\report.pdf --debug开启调试模式,未添加上述代码时的运行日志如下(运行成功但有锚点错误):
INFO: Step 1 - Fetching and parsing HTML - .\report.html INFO: Step 2 - Fetching and parsing CSS - file:///.../static/styles/report.css INFO: Step 3 - Applying CSS INFO: Step 4 - Creating formatting structure ERROR: Content discarded: target points to undefined anchor "('string', '#execution-results')" ERROR: Content discarded: target points to undefined anchor "('string', '#execution-results')" INFO: Step 5 - Creating layout - Page 1 INFO: Step 5 - Creating layout - Page 2 INFO: Step 5 - Creating layout - Page 3 INFO: Step 5 - Creating layout - Page 4 INFO: Step 5 - Creating layout - Page 5 INFO: Step 5 - Creating layout - Page 6 INFO: Step 5 - Creating layout - Page 7 INFO: Step 5 - Creating layout - Repagination #1 INFO: Step 5 - Creating layout - Page 1 (up-to-date) INFO: Step 5 - Creating layout - Page 2 (up-to-date) INFO: Step 5 - Creating layout - Page 3 INFO: Step 5 - Creating layout - Page 4 (up-to-date) INFO: Step 5 - Creating layout - Page 5 (up-to-date) INFO: Step 5 - Creating layout - Page 6 (up-to-date) INFO: Step 5 - Creating layout - Page 7 (up-to-date) INFO: Step 5 - Creating layout - Page 8 (up-to-date) INFO: Step 6 - Drawing ERROR: No anchor #execution-results for internal URI reference ERROR: No anchor #execution-results for internal URI reference ERROR: No anchor #execution-results for internal URI reference
添加上述代码后运行日志无任何报错,但生成的PDF已损坏,日志如下:
INFO: Step 1 - Fetching and parsing HTML - .\report.html INFO: Step 2 - Fetching and parsing CSS - file:///C:/.../static/styles/report.css INFO: Step 3 - Applying CSS INFO: Step 4 - Creating formatting structure INFO: Step 5 - Creating layout - Page 1 INFO: Step 5 - Creating layout - Page 2 INFO: Step 5 - Creating layout - Page 3 INFO: Step 5 - Creating layout - Page 4 INFO: Step 5 - Creating layout - Page 5 INFO: Step 5 - Creating layout - Page 6 INFO: Step 5 - Creating layout - Page 7 INFO: Step 5 - Creating layout - Page 8 INFO: Step 5 - Creating layout - Page 9 INFO: Step 5 - Creating layout - Repagination #1 INFO: Step 5 - Creating layout - Page 1 (up-to-date) INFO: Step 5 - Creating layout - Page 2 (up-to-date) INFO: Step 5 - Creating layout - Page 3 INFO: Step 5 - Creating layout - Page 4 (up-to-date) INFO: Step 5 - Creating layout - Page 5 (up-to-date) INFO: Step 5 - Creating layout - Page 6 (up-to-date) INFO: Step 5 - Creating layout - Page 7 (up-to-date) INFO: Step 5 - Creating layout - Page 8 (up-to-date) INFO: Step 5 - Creating layout - Page 9 (up-to-date) INFO: Step 6 - Drawing INFO: Step 7 - Adding PDF metadata
我的HTML本身没有问题,在浏览器开发者工具中检查所有内容正常,控制台无报错,请问该如何调试为何多款PDF阅读器无法打开生成的文件?
更新:我尝试直接使用仓库的示例代码复现问题,发现官方示例生成的PDF也存在该问题,复现步骤如下:
git clone https://github.com/Kozea/WeasyPrint.git git checkout gh-pages cd \WeasyPrint\samples\report python -m weasyprint .\report.html .\report.pdf --debug
用Adobe Acrobat Reader打开生成的PDF会提示:
There was an error porcessing a page. There was a problem reading this document (110).
调试日志仅输出如下内容:
INFO: Step 1 - Fetching and parsing HTML - .\report.html INFO: Step 2 - Fetching and parsing CSS - file:///...WeasyPrint/samples/report/report.css WARNING: Ignored `text-decoration: inherit` at 153:11, invalid value. INFO: Step 3 - Applying CSS INFO: Step 4 - Creating formatting structure INFO: Step 5 - Creating layout - Page 1 INFO: Step 5 - Creating layout - Page 2 INFO: Step 5 - Creating layout - Page 3 INFO: Step 5 - Creating layout - Page 4 INFO: Step 5 - Creating layout - Page 5 INFO: Step 5 - Creating layout - Page 6 INFO: Step 5 - Creating layout - Page 7 INFO: Step 5 - Creating layout - Page 8 INFO: Step 5 - Creating layout - Repagination #1 INFO: Step 5 - Creating layout - Page 1 (up-to-date) INFO: Step 5 - Creating layout - Page 2 (up-to-date) INFO: Step 5 - Creating layout - Page 3 INFO: Step 5 - Creating layout - Page 4 (up-to-date) INFO: Step 5 - Creating layout - Page 5 (up-to-date) INFO: Step 5 - Creating layout - Page 6 (up-to-date) INFO: Step 5 - Creating layout - Page 7 (up-to-date) INFO: Step 5 - Creating layout - Page 8 (up-to-date) INFO: Step 6 - Drawing INFO: Step 7 - Adding PDF metadata
上述警告不影响运行。我花费1小时逐行注释/取消注释CSS文件,最终定位到问题是由如下样式触发的:
html body article#columns section p:first-of-type { font-weight: 700; }
修改为如下内容后PDF即可正常打开:
html body article#columns section p:first-of-type { font-weight: 200; }
现在我需要复制新增一个页面,操作如下:
HTML中新增内容:
<article id="chapter"> <h2 id="chapter-title">This is a chapter of a new section</h2> </article> <!-- 我新增的内容 --> <article id="chapter-2"> <h2 id="chapter-2-title">This is a chapter of a new section</h2> </article>
样式中新增内容:
@page chapter { background: #fbc847; margin: 0; @top-left { content: none; } @top-center { content: none; } @top-right { content: none; } } /*我新增的内容*/ @page chapter-2 { background: #fbc847; margin: 0; @top-left { content: none; } @top-center { content: none; } @top-right { content: none; } } html body article#chapter { align-items: center; display: flex; height: 297mm; justify-content: center; page: chapter; } /*我新增的内容*/ html body article#chapter-2 { align-items: center; display: flex; height: 297mm; justify-content: center; page: chapter-2; }
操作后PDF再次在Acrobat和Firefox中损坏,但日志中没有任何报错可以辅助排障。请问有没有对开发者更友好的方式可以排查这类问题?
第一步:用PDF校验工具直接定位结构错误
WeasyPrint内置日志仅会捕获渲染流程的异常,不会校验最终生成的PDF是否符合规范,用专门的校验工具可以直接拿到具体错误点:
- 安装qpdf后执行
qpdf --check 目标文件.pdf,会输出PDF结构里的所有异常,包括字体嵌入错误、页面对象损坏、资源引用非法这类导致阅读器报错的根因 - 也可以用poppler工具集的pdftotext尝试转换PDF内容,报错信息会直接指向损坏的页面位置
第二步:针对本次新增@page的场景缩小排查范围
- 首先删除重复的@page规则:相同配置的页面不需要单独定义chapter-2规则,直接给两个article都设置
page: chapter即可,重复的@page规则很容易触发WeasyPrint的页面对象生成bug - 注释掉@page的background属性,测试是否是背景色渲染时生成了非法的颜色对象
- 把article的硬编码高度
height:297mm替换为page-break-after: always,避免页面尺寸计算溢出产生异常对象
第三步:通用优化规则避免同类问题
- 显式声明font-family,不要依赖系统默认字体:之前遇到的font-weight修改解决问题,本质是默认字体缺少对应字重时,WeasyPrint嵌入了非法的字体对象,Adobe这类严格校验的阅读器会直接拒绝加载
- 尽量用类选择器替代伪类选择器:WeasyPrint对
:first-of-type这类伪类的渲染逻辑有未修复的兼容问题,直接给对应元素加类名设置样式更稳定 - 生成PDF时指定
--pdf-version 1.4参数,低版本PDF的兼容性更好,避免高版本特性不被旧版阅读器支持
内容的提问来源于stack exchange,提问作者nck

