Original research

What actually breaks resume PDF extraction.

We generated 108 resume PDFs across 12 formatting conditions and read every one back with the same library the X-Ray uses. This page publishes the whole thing: the results, the exact versions, the code, and what it does not show.

Last updated 2026-07-31

The short version

Bad formatting does not usually delete your words. It moves them. In every condition except a scanned image, at least 96.9% of the intended words came back. What collapsed was reading order: in the narrower two-column layout, 33.1% of output lines were fused out of two unrelated source lines — a phone number welded onto a job bullet.

108

PDFs generated and read back

12

formatting conditions

33.1%

lines welded, narrow two-column

3

conditions that changed nothing

The part most advice gets backwards: 3 of the 12 conditions caused no measurable damage at all, including decorative bullet glyphs and very tight line spacing — two things resume guides warn about constantly.

Method

Every document was built from the same 9 synthetic resume profiles, so the intended text is known exactly rather than inferred. Each profile was rendered once per condition with pdf-lib, then read back with PDF.js using the identical line-grouping thresholds that ship in the ATSPrep X-Ray: a new line when the vertical gap exceeds 2.5 units, and a space when the horizontal gap exceeds max(1.1, 0.22 × glyph height).

Four things were measured against that known ground truth:

  • Recovery — share of intended words that came back at all.
  • Order — longest-common-subsequence overlap with the intended reading order.
  • Welding — share of output lines fused from two unrelated source lines. Measured only over tokens appearing exactly once in the resume, so attribution is unambiguous.
  • Block integrity — whether each bullet stayed under the role it belongs to.

Welding exists because recovery could not see the failure. A two-column resume scores 97.9% recovery and still reads as nonsense, because the words are all present and in the wrong places. Recovery alone would have called that condition healthy.

Full results

Every condition, most damaged first. Percentages are means across the 9 profiles.

ConditionRecoveryOrderWeldedBlocks
Scanned or exported as an image0%0%0%0%
Letter-spaced headings96.9%96.9%0%100%
Whole document letter-spaced96.9%96.9%0%100%
Table-style grid97.6%93.4%24.1%100%
Two columns, tight gutter97.9%83%33.1%100%
Two columns97.9%83.6%30.5%100%
Words positioned without spaces99.8%99.8%0%100%
Contact detail in the page margins100%93.7%0%100%
Single column, plain textcontrol100%100%0%100%
Very tight line spacing100%100%0%100%
Rotated sidebar100%100%0%100%
Decorative bullet glyphs100%100%0%100%

A scanned image scores 0% on everything because there is no text layer to read. It is the only condition here that loses the document outright.

The result we did not expect

Letter-spaced headings lose text. The obvious explanation is that drawing a heading glyph by glyph breaks it, so we tested that directly: one condition letter-spaced only the headings, another drew the entire document glyph by glyph with no extra tracking.

Both scored 96.9%. If splitting glyphs were the cause, the whole-document version should have been far worse. It was not — so the damage comes from the tracking, not the splitting. Because the space threshold scales with glyph height, larger letter-spaced text is more fragile than small text, which makes a spaced-out section heading the most vulnerable line on a resume.

Conditions compared: per-character-tracking and per-character-body.

Get the data

The complete per-document results — all 108 rows, not only the aggregate above — are published as a single JSON file.

  • Download the full dataset (JSON)
  • Written by pdf-lib@1.17.1, read by pdfjs-dist@6.1.200, on node@24.13.0. Extraction behaviour changes between PDF.js releases, so the exact versions are part of the result.
  • Generated 2026-07-31. The run is deterministic: the same code reproduces the same table.

The corpus generator and the measurement runner live in the repository under scripts/research/. Rebuild every document and recompute every number with one command:

node scripts/research/run-study.mjs

You are welcome to reuse these figures, including commercially, with attribution to atsprep.com/research. The dataset is published under CC BY 4.0. If you rerun it and get different numbers, we want to know.

What this does not show

These limits are the reason the study is worth anything, so they are stated here rather than buried at the bottom of a PDF.

  • The corpus is synthetic. All 108 files were generated rather than collected from real applicants. Every variable is controlled and no personal data is involved, but real resumes are messier than generated ones.
  • One reader was tested. PDF.js is the library the X-Ray runs. Employer systems use different extractors and can behave differently.
  • This measures text extraction, not hiring. It cannot tell you how any employer screens applications, and a clean extraction is not a prediction of an interview — see the methodology.
  • Conditions were applied one at a time. A real resume combining three of them was not tested.

Written up in full