Skip to content

Fix #1268: cache parsed fonts during content stream processing#1595

Open
andreasrosdalw wants to merge 2 commits into
LibrePDF:masterfrom
andreasrosdalw:fix-1268-text-extraction-memory
Open

Fix #1268: cache parsed fonts during content stream processing#1595
andreasrosdalw wants to merge 2 commits into
LibrePDF:masterfrom
andreasrosdalw:fix-1268-text-extraction-memory

Conversation

@andreasrosdalw

@andreasrosdalw andreasrosdalw commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1268.

Extracting text from the PDF attached to the issue used ~17 GB of memory on page 4. Reproduced locally: with a 1 GB heap, PdfTextExtractor.getTextFromPage(4) dies with OutOfMemoryError inside the CMap parser; pages 1–3 use ~30 MB.

Root cause

PdfContentStreamHandler$SetTextFont (the Tf operator) constructed a new CMapAwareDocumentFont for every font-selection operator in the content stream:

CMapAwareDocumentFont font = new CMapAwareDocumentFont((PRIndirectReference) pdfObject);

Constructing that font parses the embedded font program and the ToUnicode CMap from scratch. Page 4 of the issue's PDF contains 386 Tf operators across 12 fonts (vs ≤73 on other pages), so the same dozen fonts were fully re-parsed hundreds of times, allocating gigabytes of short-lived objects.

Fix

PdfContentStreamHandler now caches parsed fonts by the object number of their font dictionary; each font is parsed once per handler instead of once per Tf operator. This mirrors the font caching iText later added to its content stream processor.

Measured on the issue's PDF (all 7 pages, page-by-page extraction):

before after
page 4 OutOfMemoryError at 1 GB heap (~17 GB reported) 116 ms, extraction completes
whole document fails ~0.5 s within a 256 MB heap

Extracted text is unchanged for the pages that completed before the fix.

Tests

New PdfContentStreamHandlerFontCacheTest asserts that the handler returns the same parsed font instance for repeated lookups of the same font reference, and that text extraction output is unaffected. Full openpdf-core suite passes.

Every Tf operator in a content stream constructed a new
CMapAwareDocumentFont, which parses the embedded font program and the
ToUnicode CMap from scratch. A page that switches fonts hundreds of
times (the PDF attached to the issue has 386 Tf operators on page 4)
allocated gigabytes of short-lived objects during text extraction - the
reporter measured ~17 GB; with a 1 GB heap the extraction dies with
OutOfMemoryError inside the CMap parser.

PdfContentStreamHandler now caches parsed fonts by the object number of
their font dictionary, so each font is parsed once per handler instead
of once per Tf operator. With the fix, extracting all 7 pages of the
issue's PDF completes in ~0.5 s within a 256 MB heap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Jul 21, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 6 complexity · 0 duplication

Metric Results
Complexity 6
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

High memory usage when reading a specific PDF

1 participant