{
 "kind": "feature",
 "id": "feature:pdf-text",
 "url": "https://headlesson.com/features/pdf-text",
 "title": "Getting text out of a PDF: which recorded tool for which PDF",
 "summary": "For assistants asked to pull text or values out of PDF files, this feature separates tools that are easy to confuse (text extraction, OCR, PDF structure and document conversion) and gives one check-first sequence built from headlesson's records. Test results are dated operator observations on the stated version; descriptions of what a tool does come from its documentation, and qpdf's lesson has not been run. None of it is an accuracy guarantee.",
 "published": "2026-10-13",
 "updated": "2026-10-13",
 "body": "# Text layer or page images decide the tool\n\nA PDF either carries a text layer or is only page images. app:poppler (pdftotext) reads the text layer; on an image-only PDF it gave no text in headlesson's 2026-10-11 test with Poppler 26.04.0, yet exited 0 and wrote a single form feed. app:tesseract reads images of text, not PDF files, so a scanned PDF is first rendered to images. Decide which kind of PDF you have before choosing.\n\n# Tools that are easy to confuse\n\n- app:poppler extracts text and metadata (pdftotext, pdfinfo) and renders pages to images (pdftoppm). Its lesson passed all 7 steps on 26.04.0.\n- app:tesseract is an OCR engine for images. Its lesson rendered a self-made PDF to a 300 dpi PNG with app:poppler's pdftoppm and recognised the text; all 8 steps passed on 5.5.1. The pdftoppm step there only made a test image; Tesseract itself does not need Poppler.\n- app:qpdf checks, splits, merges and rewrites PDF structure; its record states it does not render PDFs or extract text. headlesson has not run its lesson.\n- app:docling and app:marker convert whole documents to Markdown or JSON with local models. app:docling's lesson passed 3 steps on 2.137.0 with one self-made sample; app:marker's stopped after install and help on 2.0.0: conversion first requested a font, headlesson's own test guard blocked that download, and models were not downloaded, so its conversion is unverified.\n- app:mistral-ocr is a paid external OCR API; headlesson did not run an authenticated OCR request because it needs an owner key and approval to send documents out; a request without a key returned HTTP 401.\n- app:kamiscan is a worksheet reader made by the headlesson operator; its passed lesson used a no-reader mode that reads no text, so it is not evidence of OCR.\n\n# A check-first sequence for one PDF\n\nWith app:poppler and app:tesseract, both tested on macOS on 2026-10-11: run pdfinfo to see the page count and encryption; extract with pdftotext and check that the output is not empty, not only the exit code; if it is empty, or if some pages or regions are scans (non-empty text for the whole file can hide pages that gave no text), render those pages with pdftoppm at 300 dpi and run tesseract on the images with a language listed by tesseract --list-langs. In app:tesseract's test a 50 dpi render was misread (\"Inyatce 2026\") with exit 0, so compare a sample of the OCR text, and of pdftotext's text, with the original pages. headlesson tested each tool's lesson separately; this branch as one script was not run.\n\n# What the records do not show\n\nNone of these records measures accuracy on real documents, Japanese text, general tables or handwriting; app:docling's test matched one small table (Alpha 2, Beta 3) on one self-made page, which is not an accuracy rate. app:docling and app:marker record that a successful conversion is not proof of a faithful transcription, and app:tesseract's record asks to spot-check the OCR text against the image. Use headlesson_get with sections [\"lesson\",\"gotchas\"] on app:poppler, app:tesseract or any ID above for the exact steps, versions and failure conditions.\n",
 "refs": [
  {
   "id": "app:poppler",
   "name": "Poppler utilities",
   "url": "https://headlesson.com/apps/poppler",
   "source": {
    "source": "https://poppler.freedesktop.org/",
    "checked": "2026-10-11",
    "certainty": "primary",
    "by": "official"
   }
  },
  {
   "id": "app:tesseract",
   "name": "Tesseract OCR",
   "url": "https://headlesson.com/apps/tesseract",
   "source": {
    "source": "https://github.com/tesseract-ocr/tesseract",
    "checked": "2026-10-11",
    "certainty": "primary",
    "by": "official"
   }
  },
  {
   "id": "app:qpdf",
   "name": "qpdf",
   "source": {
    "source": "https://qpdf.readthedocs.io/en/stable/cli.html",
    "checked": "2026-10-11",
    "certainty": "primary",
    "by": "official"
   }
  },
  {
   "id": "app:docling",
   "name": "Docling",
   "url": "https://headlesson.com/apps/docling",
   "source": {
    "source": "https://github.com/docling-project/docling/blob/main/README.md",
    "checked": "2026-10-09",
    "certainty": "primary",
    "by": "official"
   }
  },
  {
   "id": "app:marker",
   "name": "Marker",
   "url": "https://headlesson.com/apps/marker",
   "source": {
    "source": "https://github.com/datalab-to/marker/blob/master/README.md",
    "checked": "2026-10-09",
    "certainty": "primary",
    "by": "official"
   }
  },
  {
   "id": "app:mistral-ocr",
   "name": "Mistral OCR",
   "source": {
    "source": "https://docs.mistral.ai/capabilities/document_ai/basic_ocr",
    "checked": "2026-10-09",
    "certainty": "primary",
    "by": "official"
   }
  },
  {
   "id": "app:kamiscan",
   "name": "KAMISCAN (kasc)",
   "source": {
    "source": "operator-provided; not publicly verifiable",
    "checked": "2026-10-09",
    "certainty": "primary",
    "by": "official"
   }
  }
 ],
 "article_source": {
  "source": "headlesson editorial judgement",
  "checked": "2026-10-13",
  "certainty": "inferred",
  "by": "editorial"
 },
 "note": "Editorial article by the headlesson operator, written from the catalog records it cites. Test results in it are the operator's own observations, not vendor claims. Each cited record keeps its own source; the article's editorial source does not stand in for them.",
 "mcp_get": "headlesson_get id=\"feature:pdf-text\"",
 "page_updated": "2026-10-13"
}
