github yfedoseev/pdf_oxide v0.3.4
v0.3.4 | Parsing Robustness, Character Extraction & XObject Paths

latest releases: v0.3.76, go/v0.3.76, v0.3.75...
5 months ago

v0.3.4 Release

This release delivers PDF parsing robustness for real-world malformed PDFs, character-level text extraction API, and XObject path extraction — driven by community bug reports.

🔧 Fixed — PDF Parsing Robustness (Issue #41)

  • Header offset support — PDFs with binary prefixes or BOM headers now open successfully
    • Searches first 1024 bytes for %PDF- marker (PDF spec compliant)
    • Supports UTF-8 BOM, email headers, and other leading binary data
    • parse_header() returns byte offset where header was found
    • Lenient mode (default) handles real-world malformed PDFs; strict mode for compliance testing
    • Fixes parsing errors like "expected '%PDF-', found '1b965'"

📝 Added — Character-Level Text Extraction (Issue #39)

  • extract_chars() API — Low-level character extraction for layout analysis
    • Returns Vec<TextChar> with per-character positioning, font, and styling data
    • Includes transformation matrix, rotation angle, advance width
    • Sorted in reading order (top-to-bottom, left-to-right)
    • Overlapping characters (rendered multiple times) deduplicated
    • 30-50% faster than span extraction for character-only use cases
    • Python binding: doc.extract_chars(page_index) returns list of TextChar objects

🎨 Added — XObject Path Extraction (Issue #40)

  • Form XObject support in path extraction — Extracts vector graphics from embedded XObjects
    • extract_paths() recursively processes Form XObjects via Do operator
    • Image XObjects properly skipped (only Form XObjects extracted)
    • Coordinate transformations via /Matrix properly applied
    • Graphics state properly isolated (save/restore)
    • Duplicate XObject detection prevents infinite loops
    • Nested XObjects (XObject containing XObject) supported

⚠️ Breaking Changes

  • parse_header() function signature — Now includes offset tracking
    • Before: parse_header(reader) -> Result<(u8, u8)>
    • After: parse_header(reader, lenient) -> Result<(u8, u8, u64)>
    • Migration: Replace let (major, minor) = parse_header(&mut reader)?; with let (major, minor, _offset) = parse_header(&mut reader, true)?;
    • Note: Consider using doc.version() for typical use cases instead

📦 Changed

  • Dependencies: Upgraded nom parser library from 7.1 to 8.0
    • Updated all parser combinators to use .parse() method
    • No user-facing API changes
    • Performance stable (no regressions detected)

🏆 Community Contributors

🥇 @SeanPedersen — Filed Issue #41 with a real-world PDF that exposed the header parsing bug 🔍

📥 Installation

Rust (crates.io)

cargo add pdf_oxide

Python (PyPI)

pip install pdf_oxide

Pre-built Binaries
Download archives for Linux, macOS, and Windows from the assets below.


Release Date: February 12, 2026
Branch: main
Tag: v0.3.4
Closes: #39, #40, #41

Don't miss a new pdf_oxide release

NewReleases is sending notifications on new releases.