跳转到内容

故障排查:字体、子集化与标记

这些条目涵盖引擎通过 NextPDF\Exception\FontNotFoundExceptionNextPDF\Exception\FontParsingException 抛出的字体 resolve(解析)失败和字体解析失败。也涵盖 CJK 涵盖范围诊断,以及影响标记输出的结构树问题。每个条目都会指出对应的异常或测试,帮助你确认成因。

  • 症状。 抛出 FontNotFoundException,消息格式为 Font "<name>" not found. Searched: [<paths>]
  • 可能成因。 请求的字体家族或文件路径不存在或无法读取,或者配置的字体目录无法访问。字体数据本身可能有效,但引擎无法访问它。
  • 佐证/诊断。 getContext() 会返回 font_namesearch_pathsfallback_attempted。读取 search_paths,即可看到引擎尝试过的每个位置。读取 fallback_attempted,即可看出是否已尝试过后备字体。
  • 解法。
    1. 比对要求的 font_name 与实际存在的文件。
    2. 把存放字体的目录加入配置的字体目录,或修正你传入的路径。
    3. 确认运行时用户对该文件有读取权限。
    4. 重新执行该调用。
  • 相关。 异常参考
  • 症状。 抛出 FontParsingException,消息格式为 Failed to parse font file "<file>": <reason>
  • 可能成因。 已找到字体文件,但内容无法使用:文件头被截断、表目录无效,或缺少必要的表,例如 headhheaOS/2
  • 佐证/诊断。 getContext() 会返回 font_fileparse_errorparse_error 字段会说明结构性问题所在。
  • 解法。
    1. 读取 parse_error,找出结构性缺陷。
    2. 用同一字体的、已知正常的副本替换该字体文件。
    3. 重新执行该调用。
  • 相关。 异常参考
  • 症状。 抛出 FontParsingException,其 font_file 值为 font-subset,而 parse_error 例如 Invalid head table: too shortInvalid hhea table: too shortInvalid maxp table: too shortFailed to unpack font data
  • 可能成因。 字体通过了初始加载,但子集化所需的某个表被截断或无法解包。子集化器会拒绝该字体,而不是输出损坏的子集。
  • 佐证/诊断。headhheamaxp 表过短或解包失败时,src/Typography/FontSubsetter.php 会抛出 FontParsingException,并将字面 token font-subset 作为文件名。这个 token 会告诉你失败来自子集化阶段,而非初始加载。
  • 解法。
    1. 用同一字体的、未被截断的副本替换来源字体。
    2. 如果字体是由构建工具生成的,请重新生成并验证 headhheamaxp 表是否完整。
    3. 重新执行构建。
  • 相关。 PDF/A 与 PDF/UA 验证
  • 症状。 中文、日文或韩文文字渲染时出现空白方框或缺字,字体的 CJK 涵盖范围可能不足。
  • 可能成因。 所选字体未涵盖该文字系统所需的 Unicode 区块。除共用的表意文字区块外,每种 CJK 文字系统还需要各自特定的区块。
  • 佐证/诊断。 src/Typography/CjkFontValidator.php 提供 validateCoverage(FontInfo $font, CjkScript $script)。它会返回一个 CjkCoverageResult,其中包含涵盖百分比,以及低于 50% 报告门槛的区块。验证器会抽样码点;它是诊断工具,不会更改字体加载。
  • 解法。
    1. 针对该字体与目标文字系统执行 CjkFontValidator::validateCoverage()
    2. 读取 missingRanges,查看哪些区块未涵盖,例如繁体中文的注音符号、日文的平假名与片假名、韩文的谚文音节。
    3. 选用一个涵盖那些区块的字体,或加入能涵盖它们的后备字体。
    4. 重新渲染并重新检查涵盖范围。
  • 相关。 异常参考

条目:预定义的 CMap 与 CJK 文字系统不匹配

标题为“条目:预定义的 CMap 与 CJK 文字系统不匹配”的章节
  • 症状。 CJK 文字对应到错误的字形,或选到了与文件语言不符的预定义 CMap。
  • 可能成因。 检测到的文字系统会决定 Adobe 预定义的 CMap 名称。若字体只涵盖共用的表意文字区块、没有任何文字系统专属区块,按设计会被检测为简体中文。
  • 佐证/诊断。 CjkFontValidator::detectScript() 会返回检测到的文字系统,而 resolvePredefinedCMapName() 会将它映射:简体中文映射到 UniGB-UTF16-H、繁体中文映射到 UniCNS-UTF16-H、日文映射到 UniJIS-UTF16-H、韩文映射到 UniKS-UTF16-H。在没有任何文字系统专属区块的情况下,检测结果会回退为简体中文。
  • 解法。
    1. 确认字体随附文字系统专属区块——繁体中文的注音符号、日文的平假名或片假名、韩文的谚文。
    2. 如果文件是繁体中文,但字体不含注音符号区块,请选用包含该区块的字体,让检测能解析出你预期的文字系统。
    3. 重新渲染。
  • 相关。 异常参考

条目:标记内容未产生可用的结构树

标题为“条目:标记内容未产生可用的结构树”的章节
  • 症状。 标记构建未产生任何结构,或下游无障碍检查报告结构树为空或缺失。
  • 可能成因。 内容输出时未经过标记路径,因此没有建立任何结构元素。结构树会保持为空。
  • 佐证/诊断。 src/Accessibility/StructureTree.phpsrc/Accessibility/TaggedContentEmitter.php 会从标记内容构建结构树。测试 tests/Integration/Accessibility/EmptyTaggedPdfDoesNotAdvertisePdfUa2Test.php 确认空的结构树不会声明为 PDF/UA-2。
  • 解法。
    1. 确认内容是通过标记路径输出,才能建立结构元素。
    2. 验证每个标记内容序列都映射到一个结构元素。
    3. 重新执行构建并检查结构树。
  • 相关。 PDF/A 与 PDF/UA 验证
  • 症状。 构建失败,因为结构元素或文件上的某个语言值不是有效的标签。
  • 可能成因。 提供的语言不是有效的 BCP-47 标签。验证器会拒绝格式错误的标签,而不会将其输出。
  • 佐证/诊断。 src/Accessibility/Bcp47Validator.php 会验证标签。无效标签会引发 src/Accessibility/InvalidBcp47TagException.php。严格的 PDF/UA-2 语言要求由 tests/Unit/Conformance/PdfUa2Section844LangStrictTest.php 验证。
  • 解法。
    1. 把语言值换成有效的 BCP-47 标签,例如 en-USde-DEzh-Hant-TW
    2. 当某段文字的语言与文件语言不同时,在对应的结构元素上设置语言。
    3. 重新执行构建。
  • 相关。 PDF/A 与 PDF/UA 验证
  • FontNotFoundExceptionFontParsingException 是两个不同的异常。找不到表示引擎无法访问该文件。解析失败表示已经访问到文件,但它的字节无法使用。读取异常类别即可知道是哪一种。
  • 出现的 font-subset 值(位于 font_file 中)是子集化阶段刻意设置的标记,并非实际路径。不要去找名为 font-subset 的文件。
  • CjkFontValidator 是抽样码点而非逐一检查每个码点,因此它的涵盖率是估计值,足以用于字体选择,但不是逐字节的精确审核。
  • 空的结构树按设计会被报告为非 PDF/UA-2。这是引擎已记录的行为,而非缺陷。

词汇表:字体子集化 · CJK 涵盖范围 · 结构树