# Resume PDF Parse Benchmark

Generated 2026-09-22 17:24 UTC · 6 fixtures · reproduce with `npm run benchmark:parse`

## What this measures

Whether text a resume PDF *displays* is text a machine can *recover*. Each fixture isolates
one construction and is rendered by headless Chromium, then read back through the same
extraction path this product uses to import a resume.

## What this does not measure

Any specific applicant tracking system. Workday, Greenhouse, Taleo and the rest are closed
products, and no sourced evidence about their internals exists here. Everything below is
limited to the tested extraction pipeline. Another parser or an OCR system may recover different text.

All content is fabricated. Addresses use `example.com` (RFC 2606) and phone numbers the
`555-01xx` fictional range, so no real person appears in these results.

## Results

| Fixture | Text recall | Contact fields intact | Reading order | Type 3 fonts |
|---|---|---|---|---|
| `baseline-single-column` | 100% | 3/3 | yes | no |
| `contact-run-tight` | 100% | 1/3 | n/a | no |
| `synthetic-bold-type3` | 100% | 0/0 | n/a | **yes** |
| `two-column-sidebar` | 100% | 3/3 | yes | no |
| `table-layout` | 100% | 0/0 | n/a | no |
| `typographic-substitution` | 100% | 0/0 | n/a | no |

## Detail

### `baseline-single-column`

Hypothesis (not a finding): Control. A single-column document in an embedded weight-400 face should round-trip completely. Any loss here invalidates the other fixtures rather than indicting layout.

Observed outcome: **control-passed**. Signal: control.

- Text recall: **100%** (607 chars extracted)
- Contact fields recoverable: **3/3**
- Reading order preserved: **yes**
- Type 3 fonts present: **no**

### `contact-run-tight`

Hypothesis (not a finding): Contact fields separated only by whitespace-free inline boxes. The primary extractor concatenates a line's positioned text items without inserting a space at the gap, so four adjacent runs can extract fused into one unsearchable token.

Observed outcome: **reproduced**. Signal: contact-boundaries.

- Text recall: **100%** (418 chars extracted)
- Contact fields recoverable: **1/3**
- Fused in extraction: `06) 555-0142Seattle, WAexample.com/da` · `2Seattle, WAexample.com/dana
EXPERIENCE
No`
- Type 3 fonts present: **no**

### `synthetic-bold-type3`

Hypothesis (not a finding): A face declared at weight 400 only, rendered at 700. Chromium synthesizes the bold and Skia may write Type 3 glyphs. This fixture asks whether required text is lost; font type alone is not an extraction failure.

Observed outcome: **not-reproduced**. Signal: text-recall.

- Text recall: **100%** (378 chars extracted)
- Contact fields recoverable: **0/0**
- Type 3 fonts present: **yes**

### `two-column-sidebar`

Hypothesis (not a finding): A sidebar beside the main column. Extraction follows content order in the PDF, which a float or grid can reorder — interleaving skills into the middle of experience.

Observed outcome: **not-reproduced**. Signal: reading-order.

- Text recall: **100%** (474 chars extracted)
- Contact fields recoverable: **3/3**
- Reading order preserved: **yes**
- Type 3 fonts present: **no**

### `table-layout`

Hypothesis (not a finding): Experience laid out in a table. Table cells serialize by row, so a two-column date/description table can extract with dates detached from their roles.

Observed outcome: **not-assessed**. Signal: reading-order.

- Text recall: **100%** (238 chars extracted)
- Contact fields recoverable: **0/0**
- Type 3 fonts present: **no**

### `typographic-substitution`

Hypothesis (not a finding): Curly quotes, en dashes, ligatures and a non-breaking hyphen. These extract as different codepoints than typed, so a keyword match on the plain-ASCII form misses.

Observed outcome: **not-reproduced**. Signal: unicode-substitution.

- Text recall: **100%** (183 chars extracted)
- Contact fields recoverable: **0/0**
- Type 3 fonts present: **no**

## Font matrix

Identical text, identical renderer, identical extractor — only the embedded face varies.

| Face | Round-trips intact | Characters lost |
|---|---|---|
| system Helvetica (control) | yes | — |
| vendored inter-400 | **no** | `:` `+` `(` `)` `-` |
| vendored lato-400 | yes | — |

Probe: `PLAIN: +1 (206) 555-0142 / a-b (c) [d] {e} 50% $9.99`

- `vendored inter-400` extracted: `PLAIN 1 206 5550142 / a-b (c) [d] {e} 50% $9.99`

These faces are vendored for DOCX embedding, not for PDF export. The result is a property
of the font file; it is not a claim about this product's PDF exports.

## Method

1. Render each fixture with Chromium (`page.pdf({ format: 'Letter' })`), after awaiting
   `document.fonts.ready` so faces are resident and font behaviour is real.
2. Extract with the product's own PDF extractor.
3. Score recall on Unicode-NFKC-folded text — what a tolerant parser would see — while
   detecting field fusion in the raw text, since fusion is destroyed by folding.
4. A contact field counts as *intact* only when it is delimited on both sides. A field
   present but fused to its neighbour fails this boundary check. This does not establish what every parser could recover.

Fixtures and runner: `backend/src/scripts/parse-benchmark/`.
