Skip to content

Build Not yet confirmed elsewhere1 publisher 3 min read Published

PDF extraction is reconstruction, not reading: budget the pipeline that way

A dev.to walkthrough of pypdf and PyMuPDF internals is a useful reminder that PDFs store canvas draw operators, so every paragraph and table your pipeline emits is inferred.

The Engineer · Build desk

How we use AI Send a correction

Illustration accompanying PDF extraction is reconstruction, not reading: budget the pipeline that way
Generated illustration

What happened

  • A PDF has no concept of a paragraph, a column or a table, and does not store text in reading order.
  • A PDF is essentially a set of vector drawing instructions for a printer canvas.
  • When a word processor exports a document to PDF, it discards semantic document structure (DOM) in favour of visual position.
  • Rather than storing elements such as <h2> or <table>, the PDF encoder generates low-level drawing operators: BT to begin a text object, Tf to set font and size, Td to move the cursor to a coordinate, Tj to draw a character stream, and ET to end the text object.
  • The drawing stream contains no space characters, no paragraph breaks and no reading order.

Why it matters

A walkthrough published on dev.to steps through what pypdf and PyMuPDF actually do when they open a file, and its central point is structural rather than a matter of tool preference: a PDF has no concept of a paragraph, a column or a table, and does not even store text in reading order [2]. If that is true, then every ingestion pipeline that outputs paragraphs and table cells is running a reconstruction, and it should be scoped, budgeted and tested as one rather than as a library call.

The reason is in the format. According to the dev.to piece, a PDF is essentially a set of vector drawing instructions for a printer canvas [3], and when a word processor exports to PDF it discards the semantic document structure in favour of visual position [4]. Instead of a heading or table element, the encoder writes low-level operators: begin a text object, set a font at a size, move the cursor to an absolute coordinate, draw a character stream, end the object [5]. What is absent from that stream is space characters, paragraph breaks and reading order [6]. Because glyphs land at hardcoded x,y points, a generator may draw the footer first, the header second, the right-hand sidebar third and the main paragraph last [7], and a naive dump of the raw stream produces interleaved columns [8].

So the work splits into two jobs, which the article labels as decoding binary anatomy with pypdf and then spatial geometry and layout analysis with PyMuPDF [1]. The first job is plumbing with sharp edges. pypdf's reader seeks to the end of the file, reads the last 1,024 bytes to find the startxref keyword, and parses the cross-reference table into an index from object IDs to byte offsets [9]. The trailer dictionary points at the document catalog, at metadata such as Author, CreationDate and Producer, and at the total XRef entry count [10]; the root catalog exposes keys including /Pages, /Names and /AcroForm [11]. Page drawing commands live in a stream, or an array of streams, under /Contents [12], usually compressed with FlateDecode and inflated in RAM [13]. Stream strings are not UTF-8 by default but 8-bit encodings, often WinAnsiEncoding or PDFDocEncoding, which is why latin1 is used as a decode that preserves byte values instead of raising [14]. The mapping from a stream alias like /F1 to real characters sits in the font dictionary, with /BaseFont names and embedded /ToUnicode character maps [15].

The second job is where the error rate lives. PyMuPDF runs MuPDF's C engine [16] and returns, for each word, an eight-element tuple: bounding box, the word text, and block, word and line numbers [17], with (x0,y0) top-left and (x1,y1) bottom-right [18]. Four of those eight fields are geometry, one is text, and three are grouping indices [21], and the grouping exists because the snippet clusters tokens spatially to solve multi-column reading order [19]. That is a heuristic with a failure mode, not a field read out of the file.

Practical consequence: treat column detection, header and footer stripping, and table cell assignment as estimators. Keep a fixture corpus of the actual vendor documents you receive, assert on extracted output rather than on exit codes, and price the long tail into the schedule.

Watch three things in your own corpus. Files whose fonts ship no /ToUnicode map [15], since character recovery depends on that mapping. Files carrying /AcroForm data [11], where the values you want are form fields rather than drawn text. And pages leaning on /XObject entries, which the article notes hold embedded images or sub-canvases [20] and therefore push content outside the main stream you are parsing.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence42
Adoption
Insufficient
Hype gap+8
Incentives22
Confidence48
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    The walkthrough divides parsing into Phase 1, decoding binary anatomy with pypdf, and Phase 2, spatial geometry and layout analysis with PyMuPDF (fitz).

  2. [2]

    A PDF has no concept of a paragraph, a column or a table, and does not store text in reading order.

    Reported Supported Source: dev.to, How PDF & Document Parsers Actually Work Under the HoodView cited source
  3. [3]

    A PDF is essentially a set of vector drawing instructions for a printer canvas.

    Reported Supported Source: dev.toView cited source

Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 16, 2026

    How PDF & Document Parsers Actually Work Under the Hood

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories