* Text extraction: honour /ActualText, so Thai from Chrome PDFs is searchable Chrome (Skia) writes /ActualText around each Thai, Indic or Arabic cluster whose shaped glyphs (a tone mark moved clear of a vowel, a ligature) have no Unicode of their own. Text extraction ignored it, so those marks were lost: "น้ำหนัก" came out as "นาหนัก", "เป็น" as "เปน", and searching for any such word found nothing. - hayro-interpret patch (25): Device::actual_text hands the device a marked content sequence's /ActualText, inline or named in /Properties. - TextDevice turns the glyphs of a replacement into one glyph carrying the replacement text, boxed around them. The outermost replacement wins, an empty one stands for no text, and one spanning several lines or writing directions keeps per-glyph text so selection and lines still work. - Canva draws text twice (outline or shadow) and marks only one drawing: the unmarked redraw at the same places takes the same replacement, so the existing redraw detection still drops the copy. Checked on 1,114 real-world PDFs against the previous build, with pdftotext as the reference: 957 extract byte-identical text, 157 change and all of them read closer to the reference (exact matches 3 -> 122), none got worse, none newly fail, and total extraction time is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Text extraction: bound the work of the ActualText redraw dedupe `TextDevice::finish()` compared every replacement with every unmatched glyph in up to 4096 grid cells, so a hostile page stacking ~10^5 glyphs and ~10^5 replacements at one spot meant ~10^10 comparisons (a hang). It now charges each visited cell and each candidate run against a per-page budget (`MAX_REDRAW_WORK`, 2^22) and stops deduplicating once it is spent; redraws found so far are still merged. A regression test feeds 100,000 replacements and 100,000 non-matching glyphs at one place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: git-monchiant <4667243+git-monchiant@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
hayro-interpret
A crate for interpreting PDF files.
This crate provides an abstraction to interpret the content of a PDF file and render them
into an abstract [Device], which clients can implement as needed. This allows you, for
example, to render PDF files to bitmaps (which is what the hayro crate does), or other formats
such as SVG.
It should be noted that this crate is still very much in development. Therefore it currently lacks pretty much any documentation on how to use it. It's current API also only really makes it useful for rendering to PNG or SVG, though this will be improved upon in the future.
Examples
See the examples folder on the GitHub repository. Apart from that, you can also consult
the implementation of hayro and hayro-svg to get an idea on how to use this crate.
Safety
This crate forbids unsafe code via a crate-level attribute.
Cargo features
This crate has one optional feature:
embed-fonts: PDF processors are required to support 14 predefined fonts that do not need to be embedded into a PDF file. If you enable this feature, hayro will embed a (permissively-licensed) substitute for each font, so that you don't have to implement your custom font loading logic. This will add around ~240KB to your binary.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
PdfCraft function limits
The PdfCraft patch rejects function construction after 64 nested functions or 10,000 total
function nodes, including repeated references, and detects cycles on the current path. Shared
children in different branches remain valid. Stitching functions reject empty or unresolved
child lists instead of silently truncating them. Calculator functions have a shared limit of 64
procedure levels and 10,000 parsed tokens (operators and procedure openings); evaluation has
its own 10,000-operator limit across selected branches. These are implementation resource
limits rather than PDF format limits. Rejection returns the existing None, and the optional
logging feature reports the reason and limit; no new interface warning is introduced.
Calculator integer division without an integer result is also rejected. Bit shifting discards bits beyond the 32-bit word and fills with zeroes, including logical right shifts of negative words; rotation accepts the full signed shift range without negating the minimum integer. Fixed-size operand and procedure-stack overflow errors propagate instead of truncating work. Normal function signatures and supported color/transfer-function behavior remain unchanged.