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'"
- Searches first 1024 bytes for
📝 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 ofTextCharobjects
- Returns
🎨 Added — XObject Path Extraction (Issue #40)
- Form XObject support in path extraction — Extracts vector graphics from embedded XObjects
extract_paths()recursively processes Form XObjects viaDooperator- Image XObjects properly skipped (only Form XObjects extracted)
- Coordinate transformations via
/Matrixproperly 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)?;withlet (major, minor, _offset) = parse_header(&mut reader, true)?; - Note: Consider using
doc.version()for typical use cases instead
- Before:
📦 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)
- Updated all parser combinators to use
🏆 Community Contributors
🥇 @SeanPedersen — Filed Issue #41 with a real-world PDF that exposed the header parsing bug 🔍
📥 Installation
Rust (crates.io)
cargo add pdf_oxidePython (PyPI)
pip install pdf_oxidePre-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