Verified — 3,829-PDF Corpus (v0.3.6 → v0.3.7)
| Metric | v0.3.6 | v0.3.7 | Change |
|---|---|---|---|
| Clean rate | 95.7% | 99.6% | 3,812 of 3,829 PDFs |
| Dirty PDFs | 165 | 17 | -90% |
Systematic benchmark testing across 3,829 real-world PDFs identified and fixed 13 text extraction issues.
Added — Parser & Decoders
- BrotliDecode stream filter (PDF 2.0, ISO 32000-2:2020) — New
BrotliDecoderfor PDFs using Brotli-compressed streams (#95) - Xref trailer selection — Improve selection of the correct trailer when multiple trailers exist, fixing files where the wrong trailer was selected
- Headerless PDF recovery — Search for first object marker when
%PDF-header is missing - Multi-line object headers — Handle
1 0\nobjformat used by Google-generated PDFs - Cross-reference stream reconstruction — Rebuild xref from object markers for damaged PDFs
Added — Font Encoding
- CFF font encoding parser (
src/fonts/cff_encoding.rs) — Parse CFF/OpenType font programs to extract character encoding when no ToUnicode CMap is present (#87, #99) - Type1 font encoding parser (
src/fonts/type1_encoding.rs) — Parse embedded Type 1 font programs for/Encodingarrays withdup CODE /GLYPHNAME putpatterns (#89) - 80K+ CID-to-Unicode mappings — Expanded Adobe-CNS1 (+18K), Adobe-GB1 (+30K), Adobe-Japan1 (+15K), Adobe-Korea1 (+17K) character collections (#98)
- Shift-JIS/RKSJ decoding — Added
encoding_rsdependency for Japanese Shift-JIS encoded CMap streams (#100) - TeX math glyph names — Map MSAM, MSBM, and Computer Modern glyph names to Unicode equivalents
- Identity-H cmap propagation — Propagate TrueType cmap tables from CIDFont descendants to Type0 parent fonts (#91)
- Cross-font cmap sharing — Share TrueType cmap tables across Identity-H fonts that lack embedded encoding data (#91)
Fixed — Text Extraction Pipeline
- Tf buffer flush — Flush pending text buffer on font switch (
Tfoperator) to prevent text loss when multiple fonts are used in the same text block (#88) - Adaptive space threshold — Replace fixed 0.25em threshold with bbox-based adaptive spacing, eliminating spurious spaces in tightly-set text (#97)
- Span deduplication — Deduplicate overlapping text spans rendered multiple times at the same position (used for bold/shadow effects in some PDFs) (#102)
- Character deduplication — Remove duplicate characters within 2pt horizontal distance on the same line (#102)
- BT operator check removal — Remove incorrect content stream validation that silently skipped valid text blocks, causing empty output (#101)
- ByteMode decoding — Properly handle 1-byte, 2-byte (Identity-H/UCS2), and variable-width (Shift-JIS) character code decoding (#100, #103)
- Annotation text extraction — Extract text from Widget (form field), FreeText, and appearance stream annotations (#92)
- Metadata string filtering — Filter leaked WhitePoint, BlackPoint, and CalRGB metadata from extracted text output
- ToUnicode control character fallback — Fall back to font encoding when ToUnicode maps to control characters
Fixed — Font Handling
- TrueType cmap format 4 — Fix off-by-one in segment endCode comparison for format 4 lookup tables (#98)
- CMap byte-width detection — Detect CMap input code width from
begincodespacerangefor proper multi-byte decoding - CMap bfrange array targets — Handle
bfrangeentries with array targets (mapping ranges to non-contiguous Unicode sequences) - Symbolic font encoding — Correct encoding resolution order for symbolic fonts without explicit
/Encoding
Added — Tooling
- Benchmark suite —
bench_extract_allexample for corpus-wide extraction benchmarking - Comparison scripts —
bench_compare.py(pdf_oxide vs PyMuPDF),bench_pymupdf.py,export_text_comparison.pyfor side-by-side quality analysis - Regression tests — 11 new regression tests in
test_v037_regressions.rscovering all major fixes
Issues Resolved
| Closes | Description |
|---|---|
| #87 | Custom encoding producing garbage text |
| #88 | Multi-font text loss on Tf switch |
| #89 | Type1 subset font encoding not parsed |
| #91 | Identity-H fonts missing cmap propagation |
| #92 | Annotation/form field text not extracted |
| #95 | BrotliDecode stream filter not supported |
| #97 | Spurious spaces from fixed threshold |
| #98 | CID/ToUnicode producing U+FFFD replacements |
| #99 | Font encoding offset errors |
| #100 | Raw bytes emitted instead of decoded text |
| #101 | Empty output from valid content streams |
| #102 | Overlapping duplicate text not deduplicated |
| #103 | Character fragmentation from byte-width errors |
Ref #90, #93, #94, #96, #104, #105
📥 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.
Platform Support
| Platform | Architecture | Archive |
|---|---|---|
| Linux | x86_64 (glibc) | pdf_oxide-linux-x86_64-*.tar.gz
|
| Linux | x86_64 (musl) | pdf_oxide-linux-x86_64-musl-*.tar.gz
|
| Linux | ARM64 | pdf_oxide-linux-aarch64-*.tar.gz
|
| macOS | x86_64 (Intel) | pdf_oxide-macos-x86_64-*.tar.gz
|
| macOS | ARM64 (Apple Silicon) | pdf_oxide-macos-aarch64-*.tar.gz
|
| Windows | x86_64 | pdf_oxide-windows-x86_64-*.zip
|
Changelog
See CHANGELOG.md for full details.