Skip to content
Anderson F. González
Back to work

Talento Rosimar: reading résumés in the browser

An HR system for a food distributor. The hard part is the reader: it turns PDF, Word, scans and photos into forms that a person only has to review.

Client
Distribuciones Rosimar S.A.S., Barranquilla
My part
Development of the system, in a team of two. 50 of the repository's 119 commits1 are mine.
Period
2026, ongoing
Stack
React, TypeScript, pdf.js, Tesseract.js, mammoth.js, Express, MariaDB
Code
Private

The problem

Rosimar receives résumés in every format: PDFs with text, scans, photos taken with a phone and Word files. Copying names, contact details, experience and education by hand is slow and error-prone. The system had to read those documents without paying for each one and without relying on an external language model.

It also had to keep working offline. That's why text recognition runs inside the browser (it's a PWA) and the Tesseract files are served from the application itself, not from a CDN.

How it works

The reader is a deterministic four-stage process. It uses no language model and has no cost per document.

  1. 1

    Extraction

    pdf.js for PDFs with text, Tesseract.js (WASM) for photos and scans, mammoth.js for Word.

  2. 2

    Layout

    Rebuilds lines and detects columns from the empty vertical gutter, so a sidebar is never mixed with the content.

  3. 3

    Segmentation

    Recognizes headings with a Spanish and English lexicon that tolerates OCR errors. With no headings, it classifies by content.

  4. 4

    Extractors

    One per field: names, contact, ID, dates, experience, education, languages. They use the DANE municipality list and a job-title dictionary.

All three extraction routes produce the same thing: words with their bounding boxes.

What I measured, and what I hadn't

The first number I had was 165 of 165 fields correct2. It was true but misleading: that bench is ten PDFs generated by code, all with a perfect text layer, and that is not the path Rosimar uses. What arrives in practice is scans and photos, and that path hadn't been measured.

I built a second bench with 40 synthetic Colombian résumés turned into degraded scans (noise, skew, poor lighting, recompression), a third with 17 real photos and a fourth with 12 contracts. Latest documented measurement: September 30, 2026.

BenchDocumentsResult
Digital PDFs, with a text layer10165 of 165 fields2
Synthetic scans4089.8%3 (63.4%4 on the first measurement)
Contracts1290.0%3
Real photos1775.9%3

Only 8 of the 17 photos are résumés; the rest are contracts, certificates and handwritten forms.

5/14

Classifier regression: 19.6%

Six of every nine résumés came out as an unstructured document and reached the form empty.

Chart data
Overall accuracy of the reader on 40 synthetic scans, after each change
#Overall accuracy (%)%
1First measurement63.4%
2Local threshold (Sauvola)68.5%
3Polarity detection72.5%
4Email reconstruction73.9%
5Classifier regression19.6%
6Classifier fixed64.1%
7Preprocessing by text68.4%
8Phone by fragment69.1%
9Orientation detection73.3%
10Résumés without headings75.5%
11Fourth round76.0%
12Sixth round75.8%
13Sixteenth, baseline78.2%
14Sixteenth, final89.8%
Each point is a measurement taken after a change. The four on the right are milestones from later rounds on the same bench.

What showed up when I measured the real path

  • pdf.js 6 uses a JavaScript function that Chrome 141 and earlier don't have, so scanned PDFs couldn't be read. A twelve-line fallback fixed it.
  • A global Otsu threshold wiped out the hard scans. With a local Sauvola threshold, one document went from 8 to 2,528 characters read4.
  • Tesseract has trouble with the at sign: 20 of the 40 documents4 ended up with no email until the value was rebuilt.
  • A change in the document classifier dropped overall accuracy from 73.9% to 19.6%4. It was caught by running the bench again.
  • An average of 83% similarity on names in the photos hid that only two of the 175 had a name to check, and on those two the reader failed. It was fixed afterwards.

Limits

  • Real photos (75.9%) are still far from the synthetic scans (89.8%3).
  • Handwritten forms don't read well. There is a TrOCR pilot that hasn't been tested against that bench yet.
  • On a laptop with a Ryzen 7, each document takes between 16 seconds (photos) and 58 seconds (contracts)6.
  • The scans are synthetic: they resemble real ones but don't replace them.

One idea stays with me from this project: a 100% on the wrong bench is worse than an honest 75.9%, because the first one says there's nothing to fix.

Sources for the figures

  1. 1

    Commit authorship in cv-parser

    git shortlog of rosimar1/cv-parser (private) · October 9, 2026

    Fabian Gonzalez (my git user) 50, another team developer 41, Claude 27 and ImgBot 1: 119 in total.

  2. 2

    Reader accuracy bench on digital PDFs

    rosimar1/cv-parser, README and docs/MEDICION_LECTOR.md (private) · August 2026

    Ten PDFs generated with jsPDF, all with a perfect text layer: this measurement does not go through OCR. It is compared against 165 fields of a versioned ground truth.

  3. 3

    Reader measurement on scans, contracts and real photos

    rosimar1/cv-parser, docs/MEDICION_LECTOR.md, sixteenth round (private) · September 30, 2026

    The 89.8% on scans was measured with parallel reads that were dropped afterwards; on contracts, the serial version gave 90.0% and the parallel one 89.9%. Of the 17 real photos, only 8 are résumés.

  4. 4

    Accuracy over time on 40 synthetic scans

    rosimar1/cv-parser, docs/MEDICION_LECTOR.md, Resultado section (private) · August and September 2026

    Includes the classifier regression (19.6%) and the later milestones on the same bench. The characters read (8 and 2,528) are from document CV_04, hard profile. The 20 documents with no email come from the section on the at sign.

  5. 5

    The averaging mirage on names in the photos

    rosimar1/cv-parser, docs/MEDICION_LECTOR.md, sixth round (private) · September 2026

    Only 2 of the 17 documents declare names in their ground truth; on those two the reader failed (0% and 20% similarity) until it was fixed.

  6. 6

    Time per document

    rosimar1/cv-parser, docs/MEDICION_LECTOR.md, sixteenth round (private) · September 30, 2026

    15.7 s per photo and 57.8 s per contract, on a laptop with a Ryzen 7 5700U. With the same code, a contract took 48.8 s in the morning and 56.9 s in the afternoon.