github yfedoseev/pdf_oxide v0.3.43
v0.3.43 | Cross-binding parity, WASI build target, and a basket of issue fixes.

latest releases: v0.3.73, go/v0.3.73, v0.3.72...
2 months ago

Highlights

  • render_page_fit() now ships in all five bindings (Rust core +
    Python, Node.js / TypeScript, C#, Go). Picks the largest DPI such
    that both rendered dimensions fit inside a target pixel box,
    preserving aspect ratio. No more "what DPI hits 1024×768?" math
    on the caller's side. Fixes #441,
    closes #448.
  • Idiomatic page iteration parity across bindings. Rust gets
    page_indices(), Python gets .pages, Node.js gets
    [Symbol.asyncIterator] (the sync [Symbol.iterator] was already
    there). C# Pages and Go Pages() were already shipped. Closes
    #447.
  • WASI build targetcargo build --target wasm32-wasip1 now
    builds the lib cleanly on stable Rust. Unblocks @RALaBarge's
    external pdf-oxide-wasi stdin→stdout wrapper and any other
    consumer wanting to embed pdf_oxide in a sandboxed WASI runtime.
    CI now gates that the WASI build stays green. Closes
    #214.
  • Spurious-table fix on dense word grids — Roland's #405 lands
    via cherry-pick. A new has_split_modal_column_groups validator
    inspects the column co-occurrence graph across modal rows and
    rejects candidates whose populated columns split into two or more
    disconnected components — the signature of two adjacent text
    flows mis-clustered as one table. Composes cleanly with v0.3.42's
    Table::is_real_grid filter. Validated against the 86-PDF
    cross-build corpus: 888 / 888 byte-equal — zero observable change
    on common documents, the gate's value is in the safety net for
    adversarial cases.

Fixes

  • #456PdfDocument::open(path) now populates source_bytes,
    unblocking convert_to_pdf_a(), the C FFI pdf_document_get_source_bytes,
    and any other API that re-reads the in-memory copy. Path-loaded
    documents previously got an empty Vec<u8> and hit
    "Invalid PDF header: File is empty (0 bytes read)" from the
    PDF/A converter. Reported by @potatochipcoconut on PR #445.
  • #451 — Standard14 PostScript fonts with no open-source
    equivalent (Symbol, ZapfDingbats) are now downgraded from
    hard FontNotEmbedded errors to a new KnownUnembeddableFont
    warning during PDF/A conversion. A document that's otherwise
    compliant no longer fails solely because of one symbolic font.
  • #395 — closed; verified the off-by-one C# ExceptionMapper
    fix in v0.3.38 actually resolves the reported RenderPage
    SignatureException [8500]. Added a Rust regression test that
    opens @gevorgter's exact reproducer PDF and asserts render_page
    succeeds. The fixture is pinned in pdf_oxide_tests.
  • #462 — dropped the scripts/modernize_stubs.py post-processor
    and the python_version = "3.8" setting from rylai.toml. Rylai's
    default already emits PEP-585 / PEP-604 syntax with
    from __future__ import annotations at the top, so post-processing
    was duplicate work in opposite directions. Runtime support for
    Python 3.8/3.9 is unaffected — .pyi stubs are type-checker
    artifacts, never imported at runtime. Reported by @monchin with a
    clean diagnosis of the root cause.

Behavior changes

  • PdfDocument::open(path) now reads the file once into memory
    rather than streaming via BufReader<File>. The doc comment
    already promised "Reads the entire file into memory"; this makes
    it true. Memory usage on open() is now equivalent to
    from_bytes(std::fs::read(path)?). Required by #456; the
    streaming reader was a partial optimisation no caller could rely
    on (every code path that touched source_bytes already required
    the in-memory copy).
  • PdfReader enum collapsed to a single in-memory variant —
    removed unused File variant. std::io::{Read, Seek, BufRead, …}
    imports are no longer cfg-gated, which is what unblocked the
    wasm32-wasip1 build target.

Dependencies

  • Batch-applied 9 dependabot bumps onto release/v0.3.43:
    CI workflows (golangci-lint-action v7→v9, setup-go 5.5→6.4,
    setup-node 4.4→6.4, github-script SHA refresh,
    scorecard-action 2.4.0→2.4.3), Go (testify 1.8→1.11 — was
    declared but unimported, dropped entirely), JS (rimraf 5→6 —
    @types/node deferred to a follow-up after a TypeScript-strict
    shake-out), Python (onnx ≥1.14→≥1.19.1).
  • The RustCrypto 0.8 stack (pkcs8 0.11, spki 0.8, der 0.8,
    digest 0.11, crypto-common 0.2, block-buffer 0.12) stays
    pinned — rsa 0.10 and p256/p384 0.14 are still RC upstream.
    See the existing pin note at Cargo.toml:185-187.

Internal

  • New wasm32-wasip1 build smoke check in .github/workflows/ci.yml
    alongside the existing wasm32-unknown-unknown job.
  • Regenerated SBOMs (pdf_oxide_cli/sbom.cdx.json,
    pdf_oxide_mcp/sbom.cdx.json) for 0.3.43.
  • New regression tests:
    • tests/test_issue_456_path_open_source_bytes.rs
    • tests/test_issue_447_page_indices.rs
    • tests/test_issue_395_render_page.rs
  • New unit tests on compliance::converter::downgrade_known_unembeddable_fonts.

Validation

86-PDF stratified corpus comparison (academic, mixed, forms,
government, newspapers, theses, plus the three #211 fixtures), 888
sampled (pdf, page, method) triples across extract_text,
to_plain_text, to_markdown, to_html:

  • v0.3.43 vs v0.3.42 — 888 / 888 byte-equal, zero deltas
  • v0.3.43 vs PyPI v0.3.41 — 860 equal, 28 reorder/de-dup, 0 real
    content losses (same profile as v0.3.42's regression report)

Community contributors

This release exists because of the community. Special thanks to:

  • @RolandWArnold — landed
    the spurious-table fix in #405.
    After iterating away from an earlier density-gate framing, the
    shipped form is has_split_modal_column_groups: a connected-
    component check on the column co-occurrence graph across modal
    rows that flags two-flow grids the regular-row-ratio gate
    accepts. Roland's doc-comment explicitly flags it as a heuristic,
    making it easy to revisit later. The fix composes with v0.3.42's
    struct-tree-aware reading-order rewire without any merge conflict.
  • @RALaBarge — built an
    external WASI binary wrapper for pdf_oxide
    (pdf-oxide-wasi)
    and reported in #214
    that it required nightly Rust because of an internal
    ceil_char_boundary call. That call was already removed; this
    release fixes the second hidden blocker (cfg-gated std::io
    imports) and adds CI gating so the WASI target stays green.
  • @gevorgter — flagged two
    rendering-area gaps: the C# binding's misleading
    SignatureException on RenderPage (#395,
    fixed in v0.3.38, regression-guarded here) and the lack of a
    pixel-dimension render API (#441,
    closed by render_page_fit shipping in all five bindings).
  • @potatochipcoconut
    surfaced the convert_to_pdf_a failure on path-loaded documents
    while testing PR #445; the investigation traced it to the empty
    source_bytes field and produced the one-line fix in this
    release (#456).
  • @monchin — pointed out
    (#462) that
    scripts/modernize_stubs.py was redundant work because rylai itself
    controls the typing flavour via its python_version setting, and
    noted that office/barcodes/ocr feature alignment between
    rylai.toml and the released wheel is worth a follow-up. The
    cleaner stub pipeline ships in this release.

Installation

Rust (crates.io)

cargo add pdf_oxide

Python (PyPI)

pip install pdf_oxide

JavaScript/WASM (npm)

npm install pdf-oxide-wasm

CLI (Homebrew)

brew install yfedoseev/tap/pdf-oxide

CLI (Scoop — Windows)

scoop bucket add pdf-oxide https://github.com/yfedoseev/scoop-pdf-oxide
scoop install pdf-oxide

CLI (Shell installer)

curl -fsSL https://raw.githubusercontent.com/yfedoseev/pdf_oxide/main/install.sh | sh

CLI (cargo-binstall)

cargo binstall pdf_oxide_cli

MCP Server (for AI assistants)

cargo install pdf_oxide_mcp

Pre-built Binaries
Download archives for Linux, macOS, and Windows from the assets below. Each archive includes both pdf-oxide (CLI) and pdf-oxide-mcp (MCP server).

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.

Don't miss a new pdf_oxide release

NewReleases is sending notifications on new releases.