Skip to content

fix: merge per-glyph CID text and extract Form XObject content#76

Merged
kolkov merged 2 commits into
mainfrom
fix/cid-text-spacing
May 21, 2026
Merged

fix: merge per-glyph CID text and extract Form XObject content#76
kolkov merged 2 commits into
mainfrom
fix/cid-text-spacing

Conversation

@kolkov

@kolkov kolkov commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up fix for #74 — two additional issues discovered by testing with real-world shipping label PDFs from @iv7dev:

  • Per-glyph text merging: wkhtmltopdf emits one Tj per glyph with Td repositioning, producing "D O M I C I L I O". New mergeAdjacentElements() collapses single-glyph TextElements into words, and AssembleText() replaces naive concatenation with spatial gap detection.
  • Form XObject extraction: TCPDF places all page content inside Form XObjects via Do operator. New processFormXObject() with resource stack push/pop and 8-level recursion guard extracts text from nested XObjects.

Fixes #74

Test plan

  • go fmt ./... clean
  • go vet ./... clean
  • go test ./... — 23 packages, ALL pass (0 FAIL)
  • golangci-lint run — no new issues
  • 39 new tests: canMerge decision table, mergeAdjacentElements behavior, AssembleText boundaries, XObject processing (simple/CID/nested/depth guard/resource stack), 4 Andreani + 3 Correo integration tests with real PDFs
  • Andreani PDF: "DOMICILIO\nESTANDAR\nN° de seguimiento: 360002865586280..." (was "D O M I C I L I O")
  • Correo PDF: all 5 pages extract text (was empty)
  • All existing text extraction tests still pass
  • CHANGELOG, ROADMAP updated

kolkov added 2 commits May 21, 2026 20:17
wkhtmltopdf emits one Tj per glyph with explicit Td kern moves, producing
one TextElement per character. Callers concatenating elements with a space
separator produce 'D O M I C I L I O' instead of 'DOMICILIO ESTANDAR'.

Changes:
- Add mergeAdjacentElements(): collapses same-line, positionally-adjacent
  TextElements into word-level runs using glyphMergeGapFactor=1.5x threshold
- Add canMerge(): predicate encoding same-font, same-line, gap constraints
- Add AssembleText(): spatial word-gap detection replaces naive 'elem + space'
- Apply mergeAdjacentElements at end of ExtractFromPage
- Update document.go ExtractTextFromPage and page.go ExtractText to use
  AssembleText instead of naive concatenation with trailing space
- Add Form XObject (Do operator) support: processFormXObject, resolveXObject,
  getXObjectResources with 8-level recursion guard
- Add test PDFs: andreani_shipping_label.pdf, correo_argentino_label.pdf
- Add glyph_merge_test.go: 28 tests for canMerge, mergeAdjacentElements,
  AssembleText, and integration tests against real shipping label PDF
- Add form_xobject_test.go: 14 tests for Form XObject extraction
@kolkov kolkov merged commit bff6bc2 into main May 21, 2026
7 checks passed
@kolkov kolkov deleted the fix/cid-text-spacing branch May 21, 2026 18:01
@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.08824% with 23 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/extractor/text_extractor.go 82.83% 13 Missing and 10 partials ⚠️

📢 Thoughts on this report? Let us know!

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.

ExtractTextFromPage returns CID glyph IDs (or empty) for PDFs with embedded CID TrueType fonts and ToUnicode CMap

1 participant