Skip to content

Document conversion fails systematically with invalid glyphs encountered when custom fonts present in pdf #3965

Description

@clauder53

Bug
On some PDFs (Franchise Disclosure Documents with embedded/custom font encodings), Docling extracts text as a systematic character shift rather than readable Unicode. The garbled text is recoverable by adding 0x1D (29) to most byte values (e.g. %ODFN ? Black, ,WHP ? Item, )5$1&+,6( ? FRANCHISE).

Corruption starts on page 1 and affects nearly the entire document (not a single-page cliff). Layout/structure nodes are produced, but downstream labelers that match Item N / English headings fail because titles never appear as plaintext.

Reproduced with:

default PdfFormatOption backend (DoclingParseV4DocumentBackend)
explicit PyPdfiumDocumentBackend
So this does not appear to be fixed by switching PDF backends alone.

Example (page 1 header text as extracted vs decoded):

Extracted After ord(c)+0x1D
)5$1&+,6(…
FRANCHISE DISCLOSURE DOCUMENT
%ODFN…
Black Rifle Coffee Company LLC
,WHP… (later “Item” headers)
Item 1 …
OCR was off (do_ocr=False).

Steps to reproduce
Install Docling (see versions below).
Convert an affected PDF with OCR disabled, using either default backend or pypdfium2:
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.backend.pypdfium2_backend import PyPdfiumDocumentBackend
options = PdfPipelineOptions()
options.do_ocr = False
options.do_table_structure = True
converter = DocumentConverter(
allowed_formats=[InputFormat.PDF],
format_options={
InputFormat.PDF: PdfFormatOption(
pipeline_options=options,
backend=PyPdfiumDocumentBackend, # also fails with default DoclingParseV4
)
},
)
result = converter.convert("affected.pdf")
print(result.document.texts[0].text) # expect shifted garbage, not "FRANCHISE…"
Observe cover/heading text is not readable ASCII/Unicode; applying +0x1D per character recovers English.
Confirm the same pattern with the default backend (omit backend=…).
If useful for triage: failure is document-wide from page 1; “Item N” headings are present in the PDF and decode cleanly after the shift (e.g. first Item 1 around page 6 on one sample). I can attach a minimal redacted page sample if needed (avoid posting full commercial FDDs in public).

Docling version
CE worker / production path where we hit this:

docling==2.66.0
docling-core==2.57.0
docling-parse==4.7.2
Local env (also has Docling installed; CLI noisy with pydantic warnings):

docling 2.73.0
docling-core 2.64.0
docling-parse 4.7.2
pypdfium2 4.30.0
(docling --version / python -m docling --version does not print a clean one-liner in this env; versions above from importlib.metadata.)

Python version
Python 3.12.10
(CE image: python:3.11-slim)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions