Commit Graph
16 Commits
Author SHA1 Message Date
Abimael MartellandClaude Opus 4.6 b6764e7ca9 feat(tables): extract tables from tagged PDF structure tree (#17)
When a PDF has a well-formed structure tree with /Table > /TR > /TD|TH
elements linked to MCIDs, build tables directly from the semantic
hierarchy. Runs as highest-priority detection (step 0) before rect-based,
line-based, and heuristic strategies.

- Add StructTree::extract_tables() to walk the tree and collect table
  descriptors with row/cell/MCID info
- Add detect_tables_from_struct_tree() to match MCIDs to TextItems
- Reject tables with <30% MCID cell coverage (stale structure trees)
- Update 2013-app2 snapshot (struct-tree gives valid but different
  column ordering)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:33:39 -07:00
Abimael MartellandClaude Opus 4.6 8c4181434f fix(text): handle Tc/Tw character and word spacing (#11)
* fix(text): handle Tc/Tw character and word spacing in text width computation

PDFs using Tc (character spacing) and Tw (word spacing) operators for text
justification had words incorrectly split across TextItems. The computed
advance width didn't account for these spacing parameters, causing spurious
spaces mid-word (e.g. "deve lopers" instead of "developers").

- Add Tc/Tw operator handling and graphics state save/restore
- Incorporate char_spacing and word_spacing into compute_string_width_ts
- Add adaptive merge threshold: tighter for lowercase→lowercase junctions,
  wider before joining punctuation
- Add unit tests for Tc/Tw width computation and merge behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(fonts): add large Tc width computation test

Verifies that large character spacing values are applied in full
without any artificial cap, matching PDF spec behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(text): guard against Tc/Tw-inflated widths in merge and join paths

Two targeted fixes to prevent character-spacing (Tc) and word-spacing
(Tw) inflation from causing data quality regressions:

1. should_join_items: reject large negative gaps (< -font_size) that
   arise when Tc/Tw inflate item widths past adjacent items. Fixes
   FY_2015 merged numbers (e.g. "239.696.0" → "239.69 6.0").

2. merge_text_items: cap effective width for gap computation when Tw
   inflates space-containing items beyond 0.85× font_size per char.
   Prevents column-level gaps from collapsing into merge range,
   recovering table detection for Baldwin-Edwards and similar PDFs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 17:30:57 -07:00
Abimael MartellandClaude Opus 4.6 6c005ef4c2 fix(fonts): CID-as-Unicode passthrough and subscript merging (#8)
* fix(fonts): CID-as-Unicode passthrough and subscript/superscript merging

Add smart CID-as-Unicode passthrough for Identity-H fonts without
ToUnicode maps. Uses /W array median CID heuristic to distinguish
Unicode-CID PDFs (Chromium-generated) from GID-based subsets.

Add merge_subscript_items() pass that merges small-font items (<75%
of dominant font size, ≤4 chars, tightly adjacent) into parent items.
Fixes chemical formulas (NH3, H2O, KClO3), footnote references, and
subscript notation (vf, Hfg, m3/kg) that were previously orphaned
as separate text items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(subscripts): restrict merge to purely numeric text only

Tighten subscript merging to only merge items containing ASCII digits
(0-9). This avoids false positives with ordinal indicators (º), letter
subscripts (sol, vf), and small bullet characters (▶) that caused
table restructuring regressions.

Numeric-only keeps the primary wins: chemical formulas (NH3, H2O),
footnote references, and unit notation (m2, m3).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(subscripts): restrict merge to parent text ending with a letter

Only merge numeric subscripts when the parent item's text ends with
an alphabetic character. Prevents false merges like "33" + "1" in
fractions (33 1/3%), table credit numbers after spaces, and footnote
refs after punctuation (land.1 → land. 1). Chemical formulas (NH3,
H2O, KClO3) still merge correctly since parent ends with a letter.

Reduces pdf-eval regressions from 13 to 2 (both are correct reversions
of over-aggressive footnote merging from the prior commit).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 09:41:10 -07:00
Abimael Martell 672be2e9e5 fix(preprocess): keep first occurrence of repeated headers/footers
Document titles, column headers, and newsletter mastheads that repeat
on every page were being fully stripped by strip_repeated_lines. Now
the first page's occurrence is preserved so the content appears once.

Fixes missing titles like "VOICE OF SOUTH MARION" in tax certificate
PDFs and column headers in IRS forms.
2026-03-20 16:13:57 -07:00
Abimael MartellandClaude Opus 4.6 258210a821 fix(postprocess): collapse consecutive spaces in extracted text
OCR text layers and some PDF producers emit trailing spaces on each
text item, which combine with gap-based joining to produce double
spaces ("Vice  President"). Now collapses runs of 2+ spaces to single
space within lines, preserving leading indentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:49:43 -07:00
Abimael MartellandClaude Opus 4.6 80a3b81ff9 perf(tables): remove cosmetic padding from markdown tables
Drop column-width padding from table output. Saves ~37% bytes on
table-heavy PDFs. Markdown renders identically — padding was purely
cosmetic. Optimizes token efficiency for AI agent consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 11:19:30 -07:00
Abimael MartellandClaude Opus 4.6 f2e3d51e49 fix(tables): cap column alignment width at 40 chars to reduce whitespace bloat
Tables with very wide cells (e.g. 230+ chars from merged schedule columns)
caused massive whitespace padding in every row. Capping alignment at 40
characters reduces output size significantly (franklin: 77KB→24KB,
Closed-Business: 571KB→502KB) without affecting content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:02:49 -07:00
Abimael Martell b126d4b39f fix: remove white text color filtering, extract all visible text
Only skip text with rendering mode 3 (truly invisible). Removes
fill color tracking that was incorrectly hiding white-on-dark text
in presentations and brochures.
2026-03-05 12:06:31 -08:00
Abimael MartellandClaude Opus 4.6 8adfacd3e3 feat: enhance header/footer stripping with Y-band coalescing and page number normalization
Strip leading/trailing digit sequences (page numbers) before frequency
comparison so headers like "Page 5" and "Page 6" are treated as identical.
Group TextLines at the same Y position into Y-bands and propagate removal
to all siblings when any member is stripped. Increase EDGE_LINE_COUNT from
4 to 5 to cover 5-row form column headers (e.g., IRS p1244).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 09:46:51 -08:00
Abimael MartellandClaude Opus 4.6 eca2db119c fix: increase edge line count to catch form column headers as repeated content
Bumps EDGE_LINE_COUNT from 3 to 4 to strip repeated form column headers
that sit just inside the page margin (4th-from-edge Y position).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:14:47 -08:00
Abimael MartellandClaude Opus 4.6 7b6bb69feb feat: strip repeated headers/footers from markdown output
Add strip_repeated_lines() preprocessing step that removes running
headers and footers before markdown conversion. Uses multiple guard
rails to avoid false positives:

- Edge-line detection: only considers lines among the first/last 3
  distinct Y positions on each page (not percentage-based margins)
- Y-position consistency: requires low variance across pages (stddev
  < 5% of page span) to distinguish headers from scattered table content
- Position-aware removal: only strips instances at page edges, preserving
  body content that happens to match a header/footer text
- Skips structural lines (headings, list items), short lines (<10 chars),
  and decorative separators (repeated single characters)
- Frequency threshold: >= max(3, page_count * 30%) distinct pages

New strip_headers_footers option on MarkdownOptions (default: true).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:04:17 -08:00
Abimael MartellandClaude Opus 4.6 1bacf34bd0 Add tests for row-stripe table detection
Unit test verifying short sub-header rows (e.g. month names) are not
merged as continuation rows in table formatting. Snapshot test for
2013_app2.pdf pinning the full row-stripe table output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:43:23 -08:00
Abimael Martell b23d073553 Update real-estate-pricing snapshot 2026-02-20 19:45:09 -08:00
Abimael MartellandClaude Opus 4.6 0cf80025f6 fix(tables): Prevent graph labels from merging into adjacent tables
When a PDF page has a graph directly above a table (e.g. Figure 2 above
Table II in real-estate-pricing.pdf), the heuristic table detector would
merge graph axis labels and legend items into the table, producing a
corrupted result.

Add rect hint regions: when a small cluster of cell-border rects (4-6)
fails full grid validation (e.g. only row borders, no column dividers),
extract their Y bounding box as a "hint region". The heuristic detector
then runs separately on items inside vs outside hint regions, preventing
unrelated content from being merged into tables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 18:03:25 -08:00
Abimael MartellandClaude Opus 4.6 b6828fff8c fix(tables): Handle vertically-merged cells and protect header from continuation merge
Rects spanning multiple grid rows (e.g. classification labels) now have
their text consolidated into the first sub-row via propagate_merged_cells().
Also prevent continuation-row merge from folding body data into the header.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:40:57 -08:00
Abimael MartellandClaude Opus 4.6 28313e1f2d test: Add snapshot regression tests with PDF fixtures
Add 5 stripped public-domain PDF fixtures and golden markdown snapshots
for CI regression testing. Fix non-deterministic output caused by
HashMap iteration order in font stats, table heuristics, and rect
clustering by adding deterministic tie-breaking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:13:55 -08:00