| feat(extractor): report text paint colour and render mode; read document info fields (#579) * feat(extractor): report text paint colour and render mode; read document info fields Each text item now reports the paint its run was shown with, so callers can read the colour of a run and tell visible text from invisible text: TextItem::fill_color and TextItem::stroke_color are the non-stroking and stroking colours as 8-bit sRGB (DeviceGray, DeviceRGB, DeviceCMYK as the PDF specification converts it, ICCBased read by its component count, Indexed through its palette's base space; None for Separation, DeviceN, Pattern and other spaces), and TextItem::render_mode is the Tr mode, 0..=7. The paint is graphics state: it holds across text objects, q/Q save and restore it, Form XObjects start with the paint they are invoked under, and an ActualText span reports the paint of its first painted glyph. Which runs are extracted, and the markdown, are unchanged; weight inference keeps its stricter reading. The page content parser now handles the " operator (set the word and character spacing, move to the next line, show the string), which it skipped; Form XObjects already handled it. PdfProcessResult and PdfTypeResult gain the document information entries author, subject, keywords, creator, producer, creation_date and mod_date beside title. Every entry, the title included, is decoded as a PDF text string: UTF-16BE or UTF-8 after a byte order mark, PDFDocEncoding otherwise. The Node, WebAssembly and Python bindings, the Python stubs, and the JSON of pdf2md (--json, --items-json) and detect-pdf (--json, with and without --analyze) expose the new fields. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(changelog): link the new entries to #579 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(extractor): read abbreviated and named palette bases, indirect ICC component counts and the first glyph's paint for ActualText weight Colour spaces: - An Indexed space written with the /I abbreviation is read, and its base may be a device space's abbreviation (G, RGB, CMYK) or the name of another space in the same ColorSpace resources. - An ICCBased profile's /N may be an indirect object (or an integral real). - Selecting a space with cs/CS sets the initial colour the specification gives it: a four-component ICCBased space starts at every component 0, which reads as white; DeviceCMYK keeps its initial 0 0 0 1 (black). ActualText: whether a span's fill and stroke add weight is read from the paint state its first painted glyph was shown with, the state its reported colours and render mode come from, instead of the state at the span's end. The page parser ignores a " operator outside a text object, as it ignores Tj and TJ there. Docs: complete the sentence on how the document information entries are decoded and when they are missing, in the Rust, Node, WebAssembly and Python docs and stubs. Tests: the Node and Python tests check every paint field on real image, link and form-field items, the Node test asserts every document information entry when present and when missing, and the Python fixture test checks the stroke colour too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 1 个月前 |