From 4071f240e7c85813fa76ec17bf174117f28df2f5 Mon Sep 17 00:00:00 2001 From: Ainur T <1525421+nurikk@users.noreply.github.com> Date: Fri, 18 Sep 2026 16:05:55 +0000 Subject: [PATCH] fix: fall back from null cmap mappings Signed-off-by: Ainur T <1525421+nurikk@users.noreply.github.com> --- src/parse/pdf_resources/page_font.h | 1 + tests/test_unit_nul_mapping.py | 53 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/test_unit_nul_mapping.py diff --git a/src/parse/pdf_resources/page_font.h b/src/parse/pdf_resources/page_font.h index 2d28feff..090d32ef 100644 --- a/src/parse/pdf_resources/page_font.h +++ b/src/parse/pdf_resources/page_font.h @@ -2566,6 +2566,7 @@ namespace pdflib return cmap_initialized and cmap_numb_to_char.count(numb)==1 and cmap_numb_to_char.at(numb).size()>0 + and cmap_numb_to_char.at(numb)!=std::string(1, '\0') and cmap_numb_to_char.at(numb)!=replacement_char; }; diff --git a/tests/test_unit_nul_mapping.py b/tests/test_unit_nul_mapping.py new file mode 100644 index 00000000..fd95bf0b --- /dev/null +++ b/tests/test_unit_nul_mapping.py @@ -0,0 +1,53 @@ +from io import BytesIO + +import pytest + +from docling_parse.pdf_parser import DecodeConfig, DoclingPdfParser +from tests.pdf_builder import content_stream, simple_page_pdf + + +def _extract_character(mapping: str, glyph_name: str) -> str: + cmap = ( + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\nbegincmap\n" + "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n" + "/CMapName /Test-UCS def\n/CMapType 2 def\n" + "1 begincodespacerange\n<00> \nendcodespacerange\n" + f"1 beginbfrange\n<41> <41> <{mapping}>\nendbfrange\n" + "endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend" + ) + pdf = simple_page_pdf( + "BT /F1 24 Tf 20 100 Td (A) Tj ET", + resources="/Font << /F1 5 0 R >>", + extra_objects=[ + "<< /Type /Font /Subtype /Type1 /BaseFont /XXXXXX+FakeSubset " + "/FirstChar 65 /LastChar 65 /Widths [600] " + f"/Encoding << /Differences [65 /{glyph_name}] >> " + "/ToUnicode 6 0 R >>", + content_stream(cmap), + ], + ) + parser = DoclingPdfParser(loglevel="fatal") + doc = parser.load( + BytesIO(pdf), + decode_config=DecodeConfig(keep_glyphs=True), + ) + page = doc.get_page(1) + assert len(page.char_cells) == 1 + return page.char_cells[0].text + + +def test_authoritative_minus_overrides_conflicting_identity_before_normalization(): + assert _extract_character(mapping="2212", glyph_name="plus") == "-" + + +@pytest.mark.parametrize(("glyph_name", "expected"), [("minus", "-"), ("plus", "+")]) +def test_nul_mapping_recovers_from_known_glyph_identity(glyph_name: str, expected: str): + assert _extract_character(mapping="0000", glyph_name=glyph_name) == expected + + +def test_nul_mapping_with_unknown_identity_remains_unresolved(): + assert ( + _extract_character(mapping="0000", glyph_name="gid00043") + == "GLYPH" + )