# What actually breaks resume PDF extraction.

> We generated 108 resume PDFs in 12 layouts and read each one back with the X-Ray's own code. The results, the data and the code are public.

Last updated 2026-07-31

|  |  |
| --- | --- |
| Rendered page | https://atsprep.com/research |
| This document | https://atsprep.com/research.md |

## The short version

Bad formatting rarely deletes your words. It moves them.

Outside a scanned image, at least 96.9% of words always came back. What broke was **order**: in the tight two-column layout, 33.1% of lines fused two unrelated lines into one. And 3 of the 12 conditions changed nothing, including fancy bullets and very tight line spacing.

## Method

Each PDF was built with pdf-lib from one of 9 synthetic profiles, so the intended text is known exactly. PDF.js read it back with the X-Ray's own line rules.

### The four measures, defined

- **Recovery**: the share of intended words that came back at all.
- **Order**: longest-common-subsequence overlap with the intended reading order.
- **Welding**: the share of output lines fused from two unrelated source lines, counted only over words that appear once in the resume.
- **Blocks**: whether each bullet stayed under its own role.

Welding exists because recovery misses the failure: a two-column resume recovers 97.9% of its words and still reads as nonsense.

Line rules: a new line when the vertical gap exceeds 2.5 units, a space when the horizontal gap exceeds `max(1.1, 0.22 × glyph height)`.

## Full results

Most damaged first. Means across the 9 profiles.

| Condition | Recovery | Order | Welded | Blocks |
| --- | --- | --- | --- | --- |
| Scanned or exported as an image | 0% | 0% | 0% | 0% |
| Letter-spaced headings | 96.9% | 96.9% | 0% | 100% |
| Whole document letter-spaced | 96.9% | 96.9% | 0% | 100% |
| Table-style grid | 97.6% | 93.4% | 24.1% | 100% |
| Two columns, tight gutter | 97.9% | 83% | 33.1% | 100% |
| Two columns | 97.9% | 83.6% | 30.5% | 100% |
| Words positioned without spaces | 99.8% | 99.8% | 0% | 100% |
| Contact detail in the page margins | 100% | 93.7% | 0% | 100% |
| Single column, plain text (control) | 100% | 100% | 0% | 100% |
| Very tight line spacing | 100% | 100% | 0% | 100% |
| Rotated sidebar | 100% | 100% | 0% | 100% |
| Decorative bullet glyphs | 100% | 100% | 0% | 100% |

A scanned image has no text layer, so it is the only condition that loses the whole document.

## The result we did not expect

Letter-spaced headings lose words. Drawing every line glyph by glyph lost no more (both 96.9%), so the extra spacing is the cause, not the splitting. Bigger spaced-out text is more fragile, which makes a spaced section heading the riskiest line on a resume.

- `per-character-tracking`: Headings drawn one glyph at a time with extra tracking, which is how design tools apply letter spacing.
- `per-character-body`: Every line, not only headings, drawn glyph by glyph. The space threshold scales with glyph height, which is why larger tracked text breaks first.

## Get the data

[Download the dataset (JSON)](https://atsprep.com/research/extraction-study.json)

All 108 rows, under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Reuse it, commercially too, and credit [atsprep.com/research](https://atsprep.com/research).

Written by `pdf-lib@1.17.1`, read by `pdfjs-dist@6.1.200`, on `node@24.13.0`, 2026-07-31. Extraction changes between PDF.js releases, so the versions are part of the result.

The run is deterministic. The generator and runner live in `scripts/research/`; one command rebuilds every document and number: `node scripts/research/run-study.mjs`

If you rerun it and get different numbers, we want to know.

## What this does not show

- The 108 resumes are **synthetic**: controlled, with no personal data, but tidier than real ones.
- **One reader**, PDF.js. Employer systems use others and can behave differently.
- It measures **extraction, not hiring**. A clean read predicts no interview (see the [methodology](https://atsprep.com/methodology)).
- Conditions were tested **one at a time**, never combined.

## Written up in full

10 articles work through the data condition by condition, null results included.

- [We generated 108 resume PDFs to find what breaks text extraction](https://atsprep.com/blog/resume-pdf-extraction-study) ([markdown](https://atsprep.com/blog/resume-pdf-extraction-study.md))
- [What happens when a PDF reader hits a two-column resume](https://atsprep.com/blog/two-column-resume-extraction-test) ([markdown](https://atsprep.com/blog/two-column-resume-extraction-test.md))
- [Resume tables under extraction: same problem, milder form](https://atsprep.com/blog/resume-tables-ats-extraction) ([markdown](https://atsprep.com/blog/resume-tables-ats-extraction.md))
- [Contact details in page margins: nothing lost, everything moved](https://atsprep.com/blog/resume-header-footer-contact-details) ([markdown](https://atsprep.com/blog/resume-header-footer-contact-details.md))
- [Letter-spacing shreds resume headings](https://atsprep.com/blog/letter-spacing-resume-headings) ([markdown](https://atsprep.com/blog/letter-spacing-resume-headings.md))
- [Resume bullet characters and ATS parsing](https://atsprep.com/blog/resume-bullet-characters-ats) ([markdown](https://atsprep.com/blog/resume-bullet-characters-ats.md))
- [How PDF text extraction actually works](https://atsprep.com/blog/how-pdf-text-extraction-works) ([markdown](https://atsprep.com/blog/how-pdf-text-extraction-works.md))
- [Why a scanned resume returns nothing at all](https://atsprep.com/blog/scanned-resume-pdf-text-layer) ([markdown](https://atsprep.com/blog/scanned-resume-pdf-text-layer.md))
- [Resume line spacing: what to use, and does ATS care?](https://atsprep.com/blog/resume-line-spacing-ats) ([markdown](https://atsprep.com/blog/resume-line-spacing-ats.md))
- [Resume formatting myths, measured](https://atsprep.com/blog/resume-formatting-myths-tested) ([markdown](https://atsprep.com/blog/resume-formatting-myths-tested.md))

## About ATSPrep

ATSPrep (https://atsprep.com) is a free resume and application workspace. Match experience to a posting, strengthen claims with evidence, and track the next action. Its PDF check shows text recovered by ATSPrep's extractor, not an employer's proprietary ATS or hiring decision.

- **Price:** Free plan forever: your ATS score, every fix, job match, 5 AI edits a day and PDF downloads, with no card or watermark. Optional ATSPrep Pro ($5/month, or $24 once for 6 months) adds 300 AI edits a day, Fix all with AI, AI tailoring, Word downloads, all 7 templates and unlimited practice with Pip.
- **Account:** none needed to import or edit a resume, or for its first check and 3 rechecks a day. A free sign-in is needed for PDF downloads (Word files come with ATSPrep Pro), to sync, and for more than 1 AI edit.
- **Your file:** parsed and exported inside the browser. The resume file itself is never uploaded.
- **Sent:** matching a job by meaning, on by default once you add a job (the lines involved). Only on a button press: AI edits (the lines involved), a job link (the link) and Pip voice calls (your speech). Never your contact details, and none of it is stored.
- **Connection:** some tools load on first use and need a connection to download. Local storage is not a cloud backup.
- **Accepts:** PDF, DOCX, TXT, Markdown, or pasted plain text.

Start here: https://atsprep.com/studio
