How OfflineCV reads your resume

You drop in a PDF and get back two things: the text a generic extractor pulled out of it, and a score built from that text. This page describes what happens in between, including where it goes wrong.

The point is the gap, not the score

A resume PDF looks like a document to you. To a parser it is a bag of positioned glyphs with no reliable notion of “this line is a job title” or “these bullets belong to that role”. Section headers, two-column layouts, text drawn inside a graphic, and dates rendered as an image all have to be inferred, and the inference is sometimes wrong.

What OfflineCV shows you is one generic parser’s reading of your file. It is not a simulation of any particular applicant tracking system — those are closed, they differ from one another, and we have not measured them. Treat a field that comes back empty or garbled as a signal that the information is fragile in this file, not as a verdict from a specific employer’s software.

Step 1 — the extraction cascade

Text extraction runs as a cascade of tiers. Each tier only handles what the one before it left behind, so a clean single-column PDF is finished early and a difficult one keeps falling through.

Tier What it does
Tier 0 Pull raw text items and page geometry out of the PDF with pdf.js, plus the link annotations — a mailto: or tel: href often survives when the drawn text of the same email or phone number does not. Layout probes run here too and flag a scanned or two-column file.
Tier 1 A heuristic parser turns those items into lines, finds section headers, and segments the experience and education regions into entries with titles, employers, dates and bullets.
Tier 1.5 A regex pass fills in individual fields the heuristic parser missed — most often contact details that sit outside any recognised section.
On device AI Optional, and only where your browser supports WebGPU. A small language model runs locally to critique the result and propose rewrites. Where its reading disagrees with the heuristic parser, the disagreement is shown rather than silently resolved.

The cascade reports a per-field confidence alongside the values, so a field it guessed at is distinguishable from one it read cleanly.

Step 2 — the score

The score is deterministic and anonymous: the same text always produces the same number, and nothing about it is personalised or sent anywhere. It combines three dimensions.

Dimension Weight What it looks for
Specificity 0.4 Bullets carrying a concrete quantity — a number, a percentage, a currency amount, a duration — rather than a description of responsibilities.
Structure 0.3 Bullets that are actually bullet-shaped and in a workable length band, under entries that have a title, an employer and a date range.
Completeness 0.3 Whether the fields a reader expects came through at all: name, contact details, a summary, a skills list, education, experience.

Note what the weights imply. Completeness is scored on what the parser recovered, so a resume that contains everything but hides it in a layout the parser cannot follow loses points it would keep in a plainer file. That is the intended behaviour, and it is the whole reason to look at the extracted text next to the number.

Step 3 — the layout penalty

Layout is applied as a multiplier over the combined dimensions rather than as a fourth additive dimension, because a layout problem degrades every other signal at once instead of costing a fixed slice.

The rounded result lands in one of three bands:

The scoring algorithm is versioned, so a score you recorded under an earlier version is not silently comparable to a new one.

Step 4 — fix it and export

Every extracted field is editable in place. Your edits are stored as overrides on top of the parse rather than written back over it, so the original reading stays visible. From there you can download a plain, single-column PDF built from the corrected fields.

That export is tested against the parser that produced it: the exported PDF has to re-parse to the same fields it was built from. It is the one round trip we can actually verify, and we verify it in CI.

Two more lanes

Both work from the parsed resume, never from the PDF bytes. What leaves your browser, and when, is itemised on the privacy page.

Try it on your own resume