Every "ATS-optimized" resume tool makes the same implicit claim: that it knows what your resume looks like once it's rendered. The failure isn't that they don't check — most of them do, eventually, by showing you the page. It's what happens next: when the layout comes back broken — an orphaned line stranded alone on a second page, a bullet that wraps one word too far — the tool doesn't fix it. You do, back in its own builder, rephrasing the same ATS keywords it just injected, trying to shave a line off text you didn't choose, because the tool has no better debugger for its own layout than you.
That's the bar reflekt.me's generation loop is built around: a resume has to be as compact and dense as a human resume writer would leave it — page-tight, no orphaned lines, no half-filled pages — so it reads like something a person actually typeset, not a template that happened to fit. No amount of careful wording gets you there by itself. The only way to know a page is that dense and that clean is to measure the actual rendered page. Not the HTML. Not the DOM. The PDF a human would actually open.
The loop
Generating a resume isn't one LLM call. It's a draft → render → measure → decide loop:
- The model calls a tool with a structured content model — the sections, headings, and bullet text it wants on the page.
- We render that model to HTML, then to a real PDF.
- We measure the PDF: page count, how much of each page is covered by text, and where every line landed.
- If the measurements don't clear the bar, we hand the model a report — not vague feedback, actual numbers — and it tries again.
- This repeats for up to six rounds. If it still doesn't converge, the run fails loudly instead of shipping something wrong.
The interesting part is step 3. "Measure the PDF" sounds like it should mean something like "count the characters" or "check the div heights." We do neither. We convert every page of the finished PDF to a real rendered image and look at the pixels.
Why pixels, not markup
A layout engine's opinion about where content ended up and the truth are two different things. Margins collapse. Fonts substitute. A "single page" resume template can silently overflow by four points and get rendered as two pages, with the second page containing exactly one orphaned line. None of that is visible from the DOM — it's only visible in the output.
So our render service takes the finished PDF, rasterizes each page at a fixed 2 pixels-per-point scale, and scans it for content the same way a human eye would: is there ink here or not. Specifically, for each page:
for each row of pixels, scanning from the bottom of the page upward:
if any pixel in this row is non-white:
this is the lowest row containing content
stopThe first non-white row found scanning from the bottom gives us two numbers directly: what percentage of the page height is actually covered by content, and how many points of blank space are left below the last line. Both get converted from raw pixels back into PDF points, using the same scale factor the page was rasterized at, so the number the model sees ("47 points of unused space on the final page") means something concrete it can act on — "write about one more line."
We hold a real bar on that number: a resume's final page has to be at least 90% covered before we'll accept it. Below that, the page reads as sparse — a resume that clearly had more room and didn't use it — and the run isn't done.
Cross-checking the layout engine against itself
Coverage tells you how much content there is. It doesn't tell you whether the layout engine's model of the page matches reality. We build an internal reconstruction of how each section's text wrapped across lines — word by word, read directly off the live DOM withgetClientRects immediately before the page prints. It's useful for two things: deciding whether a bullet point fits in the remaining space, and catching widows before they ship. But a reconstruction is a prediction, not a fact.
The widow check works line against line. For every multi-line block, we compare its last line to the lines above it — word count and pixel width. A last line with one or two words at a fraction of the width of everything above it is a widow: the kind of line a human typesetting the page by hand would immediately notice and either tighten away or pull back onto the line above. We flag it per section (orphanedRow) and hand it to the model as its own issue, separate from the page-level coverage number — "this bullet's last line is orphaned" is a far more specific instruction than "the page is 84% full."
So we validate the reconstruction against the one thing that isn't a prediction: the PDF's own extracted text. We parse the real PDF, pull out every line of real text, and check that our reconstruction's predicted line-by-line wrapping actually shows up, in order, in what the PDF engine really produced. It's an ordered-subsequence match rather than an exact position match — PDF text extraction doesn't preserve DOM structure, so we're checking "did these words end up on lines in this order," not "at these exact coordinates." When a multi-line element's predicted wrap doesn't show up where expected, that's a bug in the reconstruction, not the resume — and it's one we can catch automatically instead of finding out from a user's screenshot.
What the model actually sees
After each render, the tool result handed back into the conversation includes: the page count, the coverage percentage for every page, the unused space in points on the final page, the first few lines of text on every page (so the model can sanity-check what actually printed, not just what it asked for), and a plain-language list of issues if the render didn't pass — "the rendered resume has 2 pages; it must have exactly 1." The model doesn't get an image. It doesn't need one — the geometry has already been reduced to the specific numbers that matter for the specific thing it's allowed to change next.
That constraint turns "make the resume look right" from a vague aesthetic request into something closer to a control loop: measure, compare against a target, adjust, remeasure. It converges most of the time in one or two rounds. When it doesn't converge inside the round budget, the run fails explicitly rather than shipping a resume that silently missed the page target — a design choice worth its own post.
Why this matters more than it sounds like
None of this is exotic computer vision — it's a bottom-up pixel scan, a line-by-line word-geometry check, and a substring search. What makes it worth doing is what it replaces: trusting that a template renders the way you think it does and hoping any breakage is small enough for you to fix by hand. We'd rather measure the actual page than assume the layout engine got it right, because the layout engine has been wrong before, and the only way to know is to look.