{"record":{"id":"5f9b2e879be3c908","repo":"Stirling-Tools/Stirling-PDF","slug":"no-glyphs-provided-in-input-json","errorCode":null,"errorMessage":"No glyphs provided in input JSON","messagePattern":"No glyphs provided in input JSON","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scripts/type3_to_cff.py","lineNumber":348,"sourceCode":"            if ttf_glyph is not None:\n                ttf_glyph.width = width\n        if bounds is not None:\n            global_y_min = min(global_y_min, bounds[1])\n            global_y_max = max(global_y_max, bounds[3])\n        results.append(\n            GlyphBuildResult(\n                name=glyph.name,\n                width=width,\n                charstring=charstring,\n                ttf_glyph=ttf_glyph,\n                unicode=glyph.unicode,\n                char_code=glyph.char_code,\n                bounds=bounds,\n            )\n        )\n\n    if not results:\n        raise RuntimeError(\"No glyphs provided in input JSON\")\n\n    ascent = global_y_max if math.isfinite(global_y_max) else units_per_em * 0.8\n    descent = global_y_min if math.isfinite(global_y_min) else -units_per_em * 0.2\n    ascent = otRound(ascent)\n    descent = otRound(descent)\n    if ascent <= 0:\n        ascent = otRound(units_per_em * 0.8)\n    if descent >= 0:\n        descent = -otRound(units_per_em * 0.2)\n\n    glyph_order = [\".notdef\"] + [result.name for result in results]\n    horizontal_metrics = {result.name: (result.width, 0) for result in results}\n    horizontal_metrics[\".notdef\"] = (default_width, 0)\n\n    cmap: dict[int, str] = {}\n    next_private = 0xF000\n    for result in results:\n        code_point = result.unicode","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/scripts/type3_to_cff.py#L330-L366","documentation":"type3_to_cff.py synthesises an OpenType CFF font from a glyph JSON emitted by the backend. After iterate_glyphs(data) builds the glyph list, it raises RuntimeError at line 348 if no usable glyph was produced, rather than building an empty font (which fontTools would reject downstream). iterate_glyphs (line 138) skips non-dict records, so results is empty when the `glyphs` key is absent, an empty array, or contains only non-object entries. It signals the input is not a real font dump.","triggerScenarios":"Passing a JSON with no `glyphs` key, an empty `glyphs` array, or a schema variant where glyphs live under a different key; the backend emitted a font record for a Type3 font that contained zero glyphs; pointing --input at a summary/inventory JSON instead of a raw glyph dump.","commonSituations":"Schema drift between the backend extractor and this script; testing with a stub JSON fixture; a PDF whose Type3 font legitimately has no glyphs.","solutions":["Inspect the input to confirm a non-empty glyphs array: `python -c \"import json;d=json.load(open('<file>'));print(len(d.get('glyphs',[])))\"`","Verify the input is a raw glyph dump (has top-level `glyphs` and `fontMatrix`) and not a different JSON artifact such as the inventory produced by summarize_type3_signatures.py","If the backend key changed, remap it before running, or regenerate the dump from the current backend version"],"exampleFix":"# before (type3_to_cff.py main, calls synthesise_fonts blindly)\ndata = load_json(input_path)\nsynthesise_fonts(data=data, otf_output=otf_output, ...)\n\n# after -- validate glyph presence before the heavy build\ndata = load_json(input_path)\nrecords = data.get(\"glyphs\")\nif not isinstance(records, list) or not records:\n    raise SystemExit(f\"{input_path} has no non-empty 'glyphs' array; nothing to synthesise\")\nsynthesise_fonts(data=data, otf_output=otf_output, ...)","handlingStrategy":"validation","validationCode":"data = load_json(input_path)\nrecords = data.get(\"glyphs\")\nif not isinstance(records, list) or not records:\n    raise SystemExit(f\"{input_path} has no non-empty 'glyphs' array; nothing to synthesise\")","typeGuard":"def has_glyphs(data: dict[str, object]) -> bool:\n    records = data.get(\"glyphs\")\n    return isinstance(records, list) and any(isinstance(r, dict) for r in records)","tryCatchPattern":"try:\n    synthesise_fonts(data=data, otf_output=otf_output, ...)\nexcept RuntimeError as exc:\n    print(f\"ERROR: {exc}\", file=sys.stderr)\n    sys.exit(1)","preventionTips":["Validate the input is a raw glyph dump (top-level `glyphs` and `fontMatrix`) before invoking","Regenerate dumps with the current backend version to avoid schema drift","If a font legitimately has no glyphs, skip it at the caller rather than feeding an empty dump"],"tags":["fonttools","cff","glyph","json","type3"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}