Skip to content

fix(pdf): preserve visual layout when extracting PDF text - #336

Open
duncanmcqueen wants to merge 1 commit into
Open-Legal-Products:mainfrom
duncanmcqueen:pdf-layout-text
Open

fix(pdf): preserve visual layout when extracting PDF text#336
duncanmcqueen wants to merge 1 commit into
Open-Legal-Products:mainfrom
duncanmcqueen:pdf-layout-text

Conversation

@duncanmcqueen

Copy link
Copy Markdown

Problem

extractPdfText joined every pdfjs text item on a page with a blanket space. For real legal documents that mangles the text:

  • wrapped clauses lose their line structure (the whole page becomes one flowing string)
  • kerning fragments gain spurious spaces (constitut + econstitut e)
  • indentation and column layout (signature blocks, simple tables) is destroyed

Every downstream consumer — chat document context, citation quote matching, anything built from extracted PDF text — inherits that mangled text.

Fix

Rebuild each page's text from the positioned pdfjs items (transform, width, hasEOL) instead of the raw string list:

  • Lines are reconstructed from y-coordinates and hasEOL markers, with a y-jump fallback for producers that don't set hasEOL reliably; lines are ordered top-to-bottom, items left-to-right.
  • Word spacing is driven by measured x-gaps: kerning fragments join without a space, ordinary word gaps get exactly one, and only large gaps become multiple spaces (capped at 16) so signature blocks and simple tables keep their columns without exaggerating justified text.
  • Paragraph breaks: a vertical gap above ~1.7× the line height becomes a blank line.
  • Indentation is preserved relative to the page's left margin (capped at 24 spaces).

The page-marker format ([Page N]) and the unreadable-buffer `` fallback are unchanged.

Tests

New documentOps.test.ts with a mocked pdfjs covers: line reconstruction from positions, paragraph breaks, column-gap preservation, page markers/ordering, and the failure fallback. Full backend suite passes (607 tests), tsc --noEmit clean.

extractPdfText joined every pdfjs text item with a blanket space, which
merged line fragments across the whole page: wrapped clauses lost their
line structure, kerning splits gained spurious spaces, and indentation
and column layout (signature blocks, simple tables) were destroyed.
Downstream consumers — chat document context, citation quote matching,
and any text built from extracted PDF content — all inherited that
mangled text.

Rebuild each page's text from the positioned pdfjs items instead:

- lines are reconstructed from y-coordinates and hasEOL markers (with a
  y-jump fallback for producers that don't set hasEOL reliably)
- words are joined or split based on measured x-gaps, so kerning
  fragments like 'constitut' + 'e' join without a space while real word
  gaps keep one
- paragraph-scale vertical gaps become blank lines
- indentation is kept relative to the page's left margin, and large
  x-gaps are preserved as columns (capped so justified text isn't
  exaggerated)

Adds unit tests with a mocked pdfjs covering line reconstruction,
paragraph breaks, column gaps, page markers, and the unreadable-buffer
fallback.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

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.

3 participants