Skip to content

Troubleshooting: fonts, subsetting, and tagging

These entries cover font resolution and parsing failures raised through NextPDF\Exception\FontNotFoundException and NextPDF\Exception\FontParsingException. They also cover Chinese, Japanese, and Korean (CJK) coverage diagnostics and structure-tree issues that affect tagged output. Each entry names the exact exception or test so you can verify the cause.

  • Symptom. FontNotFoundException with a message of the form Font "<name>" not found. Searched: [<paths>].
  • Likely cause. The requested font family or file path does not exist, is not readable, or lives in a fonts directory the runtime cannot access. The font data may be valid, but the engine still cannot reach it.
  • Evidence / diagnosis. getContext() returns font_name, search_paths, and fallback_attempted. Use search_paths to review every location the engine tried. Use fallback_attempted to confirm whether a fallback already ran.
  • Resolution.
    1. Compare the requested font_name with the font file that is actually present.
    2. Add the directory that contains the font to the configured fonts directory, or correct the path you passed.
    3. Verify that the runtime user can read the file.
    4. Re-run the call.
  • Related. Exception reference.
  • Symptom. FontParsingException with a message of the form Failed to parse font file "<file>": <reason>.
  • Likely cause. The engine found the font file, but cannot use its contents: a truncated header, an invalid table directory, or a missing mandatory table such as head, hhea, or OS/2.
  • Evidence / diagnosis. getContext() returns font_file and parse_error. parse_error names the structural problem.
  • Resolution.
    1. Read parse_error to identify the structural defect.
    2. Replace the font file with a known-good copy of the same face.
    3. Re-run the call.
  • Related. Exception reference.

Entry: subsetting fails on a malformed font table

Section titled “Entry: subsetting fails on a malformed font table”
  • Symptom. FontParsingException with a font_file value of font-subset and a parse_error such as Invalid head table: too short, Invalid hhea table: too short, Invalid maxp table: too short, or Failed to unpack font data.
  • Likely cause. The font passed initial loading, but a table needed for subsetting is truncated or cannot be unpacked. The subsetter rejects the font instead of emitting a broken subset.
  • Evidence / diagnosis. When a head, hhea, or maxp table is too short or an unpack fails, src/Typography/FontSubsetter.php raises FontParsingException with the literal token font-subset as the file name. The token tells you the failure happened during subsetting, not initial loading.
  • Resolution.
    1. Replace the source font with a complete, non-truncated copy of the same face.
    2. If a build tool generates the font, regenerate it and verify that the head, hhea, and maxp tables are complete.
    3. Re-run the build.
  • Related. PDF/A and PDF/UA validation.

Entry: CJK text renders with missing glyphs

Section titled “Entry: CJK text renders with missing glyphs”
  • Symptom. Chinese, Japanese, or Korean text renders as blank boxes or missing characters, and the font’s CJK coverage is uncertain.
  • Likely cause. The selected font does not cover the Unicode blocks the script requires. Each CJK script requires specific blocks in addition to the shared ideograph blocks.
  • Evidence / diagnosis. src/Typography/CjkFontValidator.php provides validateCoverage(FontInfo $font, CjkScript $script). It returns a CjkCoverageResult with the coverage percentage and any blocks below the 50% reporting threshold. The validator samples codepoints. It is a diagnostic and does not modify font loading.
  • Resolution.
    1. Run CjkFontValidator::validateCoverage() for the font and the target script.
    2. Read the missingRanges to see which blocks are uncovered, for example, Bopomofo for Traditional Chinese, Hiragana and Katakana for Japanese, and Hangul Syllables for Korean.
    3. Select a font that covers those blocks, or add a fallback font that does.
    4. Re-run rendering and re-check coverage.
  • Related. Exception reference.

Entry: predefined CMap does not match the CJK script

Section titled “Entry: predefined CMap does not match the CJK script”
  • Symptom. CJK text maps to the wrong glyphs, or the document uses a predefined CMap that does not match its language.
  • Likely cause. The detected script determines the Adobe predefined CMap name. A font that covers only the shared ideograph block, with no script-specific block, is detected as Simplified Chinese by design.
  • Evidence / diagnosis. CjkFontValidator::detectScript() returns the detected script, and resolvePredefinedCMapName() maps it: Simplified Chinese to UniGB-UTF16-H, Traditional Chinese to UniCNS-UTF16-H, Japanese to UniJIS-UTF16-H, and Korean to UniKS-UTF16-H. If no script-specific block is present, detection falls back to Simplified Chinese.
  • Resolution.
    1. Confirm that the font ships the script-specific block: Bopomofo for Traditional Chinese, Hiragana or Katakana for Japanese, and Hangul for Korean.
    2. If the document is Traditional Chinese but the font has no Bopomofo block, select a font that includes one so detection resolves to the intended script.
    3. Re-run rendering.
  • Related. Exception reference.

Entry: tagged content does not produce a usable structure tree

Section titled “Entry: tagged content does not produce a usable structure tree”
  • Symptom. A tagged build produces no structure, or a downstream accessibility check reports an empty or missing structure tree.
  • Likely cause. The build emitted content outside the tagging path, so it created no structure elements. The structure tree stays empty.
  • Evidence / diagnosis. src/Accessibility/StructureTree.php and src/Accessibility/TaggedContentEmitter.php build the structure tree from tagged content. The test tests/Integration/Accessibility/EmptyTaggedPdfDoesNotAdvertisePdfUa2Test.php confirms that an empty structure tree does not advertise PDF/UA-2.
  • Resolution.
    1. Confirm content is emitted through the tagging path so structure elements are created.
    2. Verify each marked-content sequence maps to a structure element.
    3. Re-run the build and inspect the structure tree.
  • Related. PDF/A and PDF/UA validation.

Entry: structure-element language tag is rejected

Section titled “Entry: structure-element language tag is rejected”
  • Symptom. A build fails because a language value on a structure element or on the document is not a valid tag.
  • Likely cause. The supplied language is not a valid BCP 47 tag. The validator rejects malformed tags instead of emitting them.
  • Evidence / diagnosis. src/Accessibility/Bcp47Validator.php validates the tag. An invalid tag raises src/Accessibility/InvalidBcp47TagException.php. tests/Unit/Conformance/PdfUa2Section844LangStrictTest.php exercises the strict PDF/UA-2 language requirement.
  • Resolution.
    1. Replace the language value with a valid BCP 47 tag, such as en-US, de-DE, or zh-Hant-TW.
    2. Set the language on the specific structure element when a passage differs from the document language.
    3. Re-run the build.
  • Related. PDF/A and PDF/UA validation.
  • FontNotFoundException and FontParsingException report different failures. Not-found means the file could not be reached. Parsing means the file was reached, but its bytes are not usable. Read the class to identify which failure occurred.
  • font-subset in font_file is a deliberate marker for the subsetting stage, not an actual path. Do not look for a file named font-subset.
  • CjkFontValidator samples codepoints rather than checking every one, so its coverage figure is a font-selection estimate, not a byte-exact audit.
  • An empty structure tree is reported as not PDF/UA-2 by design. This is the documented engine behavior, not a defect.

Glossary: font subsetting · CJK coverage · structure tree