Build1 distinct publisher3 min readUpdated
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

Compiled by The EngineerSomething wrong?How this is made
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 [1]. 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 [2], and when a word processor exports to PDF it discards the semantic document structure in favour of visual position [3]. 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 [4]. What is absent from that stream is space characters, paragraph breaks and reading order [5]. 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 [6], and a naive dump of the raw stream produces interleaved columns [7].
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 [20]. 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 [8]. The trailer dictionary points at the document catalog, at metadata such as Author, CreationDate and Producer, and at the total XRef entry count [9]; the root catalog exposes keys including /Pages, /Names and /AcroForm [10]. Page drawing commands live in a stream, or an array of streams, under /Contents [11], usually compressed with FlateDecode and inflated in RAM [12]. 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 [13]. The mapping from a stream alias like /F1 to real characters sits in the font dictionary, with /BaseFont names and embedded /ToUnicode character maps [14].
The second job is where the error rate lives. PyMuPDF runs MuPDF's C engine [15] and returns, for each word, an eight-element tuple: bounding box, the word text, and block, word and line numbers [16], with (x0,y0) top-left and (x1,y1) bottom-right [17]. Four of those eight fields are geometry, one is text, and three are grouping indices [1], and the grouping exists because the snippet clusters tokens spatially to solve multi-column reading order [18]. 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 [14], since character recovery depends on that mapping. Files carrying /AcroForm data [10], 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 [19] and therefore push content outside the main stream you are parsing.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The walkthrough divides parsing into Phase 1, decoding binary anatomy with pypdf, and Phase 2, spatial geometry and layout analysis with PyMuPDF (fitz).
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.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
One self-published explainer, mechanically specific but uncorroborated
The claims are unusually concrete for a single source — named operators (BT/Tf/Td/Tj/ET), the 1,024-byte startxref lookback, FlateDecode inflation, /ToUnicode CMaps, the eight-field PyMuPDF word tuple — and are internally consistent with the code shown. But everything rests on one dev.to post with no citation of the PDF specification or library documentation, no test corpus, and no second publisher, and the supplied body is truncated mid-way through the pdfplumber section.
No adoption signal in supplied sources
The cluster contains no release, deployment, version, download, benchmark, pricing or usage disclosure for pypdf, PyMuPDF, MuPDF or pdfplumber. The libraries are described functionally only, so no adoption level can be scored without inventing facts.
Slightly overstated rhetoric over sound mechanics
The substantive claims are modest, long-established format facts and match the evidence, so the gap is near zero. It tilts marginally positive because the framing ('abyss', 'Spatial Order Nightmare', 'how document parsers actually work') is more dramatic than the content, and because a two-column threshold at a hardcoded 250 pt with 10 pt y-rounding is presented as solving multi-column reading order without any accuracy claim, generalisation test, or discussion of tables, rotated text or scanned pages.
Low commercial stake; developer-audience content
The piece is a self-published developer post on a community platform that explains three third-party open-source libraries, sells no product, quotes no vendor, and asks for no action beyond reading. The residual incentive is attention and portfolio building typical of dev.to tutorials, which favours confident explanation over caveats — visible in the dramatic framing and absence of failure cases. No author affiliation or sponsorship is disclosed either way.
Moderate on mechanics, weak on everything else
Confidence is moderate for the format and API mechanics, which are specific, self-consistent and independently checkable, but is held down by single-source dependence, a truncated body, no adoption or performance data, and no external corroboration of the library behaviours described.
build
Force the tool call, then hand Lightsail a long-lived key1 distinct publisher
build
Stop timing your GraphQL tests and start counting loader calls1 distinct publisher
security
Google's reference agent approved a $10,000 refund on a $149 order, on purpose1 distinct publisher
build
Your 90% Cache Hit Ratio Is a Lagging Indicator. Alert on Cold Misses Per Key1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 16, 2026