故障排查:字体、子集化与标记
适用范围
标题为“适用范围”的章节这些条目涵盖引擎通过 NextPDF\Exception\FontNotFoundException 与 NextPDF\Exception\FontParsingException 抛出的字体 resolve(解析)失败和字体解析失败。也涵盖 CJK 涵盖范围诊断,以及影响标记输出的结构树问题。每个条目都会指出对应的异常或测试,帮助你确认成因。
条目:找不到字体
标题为“条目:找不到字体”的章节- 症状。 抛出
FontNotFoundException,消息格式为Font "<name>" not found. Searched: [<paths>]。 - 可能成因。 请求的字体家族或文件路径不存在或无法读取,或者配置的字体目录无法访问。字体数据本身可能有效,但引擎无法访问它。
- 佐证/诊断。
getContext()会返回font_name、search_paths与fallback_attempted。读取search_paths,即可看到引擎尝试过的每个位置。读取fallback_attempted,即可看出是否已尝试过后备字体。 - 解法。
- 比对要求的
font_name与实际存在的文件。 - 把存放字体的目录加入配置的字体目录,或修正你传入的路径。
- 确认运行时用户对该文件有读取权限。
- 重新执行该调用。
- 比对要求的
- 相关。 异常参考。
条目:字体文件解析失败
标题为“条目:字体文件解析失败”的章节- 症状。 抛出
FontParsingException,消息格式为Failed to parse font file "<file>": <reason>。 - 可能成因。 已找到字体文件,但内容无法使用:文件头被截断、表目录无效,或缺少必要的表,例如
head、hhea或OS/2。 - 佐证/诊断。
getContext()会返回font_file与parse_error。parse_error字段会说明结构性问题所在。 - 解法。
- 读取
parse_error,找出结构性缺陷。 - 用同一字体的、已知正常的副本替换该字体文件。
- 重新执行该调用。
- 读取
- 相关。 异常参考。
条目:字体表损坏导致子集化失败
标题为“条目:字体表损坏导致子集化失败”的章节- 症状。 抛出
FontParsingException,其font_file值为font-subset,而parse_error例如Invalid head table: too short、Invalid hhea table: too short、Invalid maxp table: too short或Failed to unpack font data。 - 可能成因。 字体通过了初始加载,但子集化所需的某个表被截断或无法解包。子集化器会拒绝该字体,而不是输出损坏的子集。
- 佐证/诊断。 当
head、hhea或maxp表过短或解包失败时,src/Typography/FontSubsetter.php会抛出FontParsingException,并将字面 tokenfont-subset作为文件名。这个 token 会告诉你失败来自子集化阶段,而非初始加载。 - 解法。
- 用同一字体的、未被截断的副本替换来源字体。
- 如果字体是由构建工具生成的,请重新生成并验证
head、hhea与maxp表是否完整。 - 重新执行构建。
- 相关。 PDF/A 与 PDF/UA 验证。
条目:CJK 文字渲染时缺少字形
标题为“条目:CJK 文字渲染时缺少字形”的章节- 症状。 中文、日文或韩文文字渲染时出现空白方框或缺字,字体的 CJK 涵盖范围可能不足。
- 可能成因。 所选字体未涵盖该文字系统所需的 Unicode 区块。除共用的表意文字区块外,每种 CJK 文字系统还需要各自特定的区块。
- 佐证/诊断。
src/Typography/CjkFontValidator.php提供validateCoverage(FontInfo $font, CjkScript $script)。它会返回一个CjkCoverageResult,其中包含涵盖百分比,以及低于 50% 报告门槛的区块。验证器会抽样码点;它是诊断工具,不会更改字体加载。 - 解法。
- 针对该字体与目标文字系统执行
CjkFontValidator::validateCoverage()。 - 读取
missingRanges,查看哪些区块未涵盖,例如繁体中文的注音符号、日文的平假名与片假名、韩文的谚文音节。 - 选用一个涵盖那些区块的字体,或加入能涵盖它们的后备字体。
- 重新渲染并重新检查涵盖范围。
- 针对该字体与目标文字系统执行
- 相关。 异常参考。
条目:预定义的 CMap 与 CJK 文字系统不匹配
标题为“条目:预定义的 CMap 与 CJK 文字系统不匹配”的章节- 症状。 CJK 文字对应到错误的字形,或选到了与文件语言不符的预定义 CMap。
- 可能成因。 检测到的文字系统会决定 Adobe 预定义的 CMap 名称。若字体只涵盖共用的表意文字区块、没有任何文字系统专属区块,按设计会被检测为简体中文。
- 佐证/诊断。
CjkFontValidator::detectScript()会返回检测到的文字系统,而resolvePredefinedCMapName()会将它映射:简体中文映射到UniGB-UTF16-H、繁体中文映射到UniCNS-UTF16-H、日文映射到UniJIS-UTF16-H、韩文映射到UniKS-UTF16-H。在没有任何文字系统专属区块的情况下,检测结果会回退为简体中文。 - 解法。
- 确认字体随附文字系统专属区块——繁体中文的注音符号、日文的平假名或片假名、韩文的谚文。
- 如果文件是繁体中文,但字体不含注音符号区块,请选用包含该区块的字体,让检测能解析出你预期的文字系统。
- 重新渲染。
- 相关。 异常参考。
条目:标记内容未产生可用的结构树
标题为“条目:标记内容未产生可用的结构树”的章节- 症状。 标记构建未产生任何结构,或下游无障碍检查报告结构树为空或缺失。
- 可能成因。 内容输出时未经过标记路径,因此没有建立任何结构元素。结构树会保持为空。
- 佐证/诊断。
src/Accessibility/StructureTree.php与src/Accessibility/TaggedContentEmitter.php会从标记内容构建结构树。测试tests/Integration/Accessibility/EmptyTaggedPdfDoesNotAdvertisePdfUa2Test.php确认空的结构树不会声明为 PDF/UA-2。 - 解法。
- 确认内容是通过标记路径输出,才能建立结构元素。
- 验证每个标记内容序列都映射到一个结构元素。
- 重新执行构建并检查结构树。
- 相关。 PDF/A 与 PDF/UA 验证。
条目:结构元素的语言标签遭拒
标题为“条目:结构元素的语言标签遭拒”的章节- 症状。 构建失败,因为结构元素或文件上的某个语言值不是有效的标签。
- 可能成因。 提供的语言不是有效的 BCP-47 标签。验证器会拒绝格式错误的标签,而不会将其输出。
- 佐证/诊断。
src/Accessibility/Bcp47Validator.php会验证标签。无效标签会引发src/Accessibility/InvalidBcp47TagException.php。严格的 PDF/UA-2 语言要求由tests/Unit/Conformance/PdfUa2Section844LangStrictTest.php验证。 - 解法。
- 把语言值换成有效的 BCP-47 标签,例如
en-US、de-DE或zh-Hant-TW。 - 当某段文字的语言与文件语言不同时,在对应的结构元素上设置语言。
- 重新执行构建。
- 把语言值换成有效的 BCP-47 标签,例如
- 相关。 PDF/A 与 PDF/UA 验证。
边界情况与陷阱
标题为“边界情况与陷阱”的章节FontNotFoundException与FontParsingException是两个不同的异常。找不到表示引擎无法访问该文件。解析失败表示已经访问到文件,但它的字节无法使用。读取异常类别即可知道是哪一种。- 出现的
font-subset值(位于font_file中)是子集化阶段刻意设置的标记,并非实际路径。不要去找名为font-subset的文件。 CjkFontValidator是抽样码点而非逐一检查每个码点,因此它的涵盖率是估计值,足以用于字体选择,但不是逐字节的精确审核。- 空的结构树按设计会被报告为非 PDF/UA-2。这是引擎已记录的行为,而非缺陷。