Skip to content

0001. Extract with pdftotext, not a document-understanding toolkit

  • Status: Accepted
  • Date: 2026-07

Context

Every claim must quote its source verbatim, so extraction quality is not a cosmetic concern: a garbled span cannot be matched and its claim is dropped.

Measured on this machine, Poppler processes a 62-page chapter at roughly 580 pages/second. Published figures for the alternatives, on comparable or better hardware: Marker (fast, no OCR) 23.7 pages/s, Docling 2.2–2.5 pages/s on an M3 Max, Unstructured 0.24 pages/s.

The quality gap runs the other way. On olmOCR-Bench a PDFium-class plain-text extractor scores 20.4% against Docling's 50.3% and Marker's 76.0%, because that benchmark rewards table structure, LaTeX maths and scanned pages.

Decision

Shell out to pdftotext in reading order (not -layout), and accept that tables, formulae and scans are out of scope.

Alternatives considered

  • Docling / Marker / MinerU. Two to three orders of magnitude slower, and each brings a Python and model-weight dependency that would end the "one static binary, no GPU, works offline" property the tool is built around.
  • A cgo PDF library. Removes the runtime dependency but reintroduces cgo, which costs cross-compilation and the static Linux binary.
  • pdftotext -layout. Rejected on evidence: preserving visual layout splices the two columns of a paper onto shared lines, so sentences merge with unrelated text and a quote can never match. Spliced lines across the corpus went from 158 and 59 on two papers to zero once reading order was used.

Consequences

  • The deterministic path is ~0.25 s, so essentially all of a run is model latency. That ratio is the product.
  • Recall, not precision, is what suffers. A claim drawn from a table cannot be quote-matched, so it is dropped. The draft stays honest and becomes thinner, and the run reports the loss only as a count — a user is likely to read it as the model being lazy.
  • Scanned PDFs are refused with an OCR suggestion rather than silently producing nothing.
  • A pluggable extractor interface, letting a user route table-heavy sources through Docling while keeping this as the default, is the open follow-up.