TextExtractor

Linear text extraction (ISO 32000-1 §9.4).

Walks content-stream operations for text-showing operators:

  • Tj : show a string

  • TJ : show an array of strings/spacing adjustments

  • ' : move to next line and show

  • " : set spacing, move to next line, and show

Show strings are decoded through the current font (set by Tf), so each font's /Encoding and /ToUnicode CMap apply. This is what makes composite Type 0 fonts (Identity-H, 2-byte codes) and embedded CJK extract correctly rather than byte-for-byte. When no font is resolvable the bytes fall back to PdfString.asText() (PDFDocEncoding / UTF-16BE BOM detection).

Line breaks are inserted on BT/ET, Td, TD, T*, Tm heuristics because PDF text positioning is geometric, not line-based. Those size-scaled heuristics read the Tf size times the Tm scale, since a producer may set Tf to 1 and carry the real size in the matrix.

A form XObject that the page draws with Do adds its text in place, read with the form's own fonts (ISO 32000-1, 8.10, #459). A marked-content sequence with /ActualText adds that text in place of the text it encloses (14.9.4, #468).

A label that the page draws twice in one place reads once (#644). A fake bold fills a label and then strokes it, or fills it again a fraction of a point aside, and a halo strokes it in white under its fill, each pass a text-showing operator of its own (9.4.3). So the walk also moves a pen as the renderer does, through the text matrix, the advance of each glyph and the CTM (9.4.2 and 9.4.4), and skips a show when an earlier show that stayed drew the same text there: at the same size within a tenth, in the same direction, and with both ends less than a tenth of an em away. RedrawnText makes the same test glyph by glyph for the structured text. This walk compares whole shows, so a second pass that splits its text into other shows than the first reads twice here. Shows that follow each other along a line never match each other, and a space never matches, so double letters in tight type and the gap after a leader stay.

This is the cheap linear path: the order is the order of the content stream, and the line breaks follow Tm and Td alone. PdfPage.structuredText gives positions and reading order.

Functions

Link copied to clipboard
fun extract(page: PdfPage): String

Font-blind overload for callers that have operations but no resources: it draws no forms. Without the widths of a font it cannot tell where a show ends, so it skips no second pass.