Repository navigation
Releases
v0.8.1
v0.8.1: simpler font configuration and release workflow
This unstable maintenance release preserves v0.8.0 public APIs, supported
profiles and PDF output. No migration or additional font options are needed.
cargo install --locked caj2pdf-cli --version 0.8.1
npm install caj2pdf-rust@0.8.1
caj2pdf paper.caj -o paper.pdf- One authored resource manifest produces the Rust and JavaScript constants;
CI rejects drift (#551). - The CLI reuses core font configuration. CLI/WASM move maps and identities
into conversion instead of cloning them; JavaScript resolves optional role
indices once. WASMSessionkeeps itsSend + Syncguarantees (#552). - Default CLI/Node/browser usage comes first, advanced font flags are grouped,
and current publication instructions are separated from linked bootstrap
history. Coverage status now describes the unchanged 90% floor (#553).
Validation and limits
The retained Wine-reference cohort contains 13 documents / 77 pages. Default
CLI, Node and Chromium PDFs remain byte-identical to v0.8.0. Existing custom,
absent and shared font sources, TTC faces, malformed maps, cancellation and
actual npm package tests remain covered. Seven ignored optional Rust corpus
tests are excluded from compatibility evidence. No fresh Wine pixel comparison
or additional supported profile is claimed.
Font bytes, geometry/glyph tables and embedding choices are unchanged. Native
binaries include the font pack; Node reads the packaged resource and browsers
cache it in OPFS on first use. See bundled fonts and
format support. C8/HN-B
outlines and the recorded raster/unsupported-profile limitations remain.
The tagged workflow requires the complete native/container inventories,
quality and actual-package checks, and verified file/container attestations
before GitHub publication. It then publishes core/CLI to crates.io and the
same tested tarball to npm using OIDC. The tagged run and its SHA256SUMS
identify release bytes; Windows and macOS binaries remain unsigned. See
build provenance for verification commands.
v0.8.0
v0.8.0: automatic native-text fonts
This unstable minor release changes default native-text conversion behavior
since v0.7.0.
CLI and JavaScript native C8/HN-B conversions now use a built-in CAJViewer
7.3 Wine reference preset. Fonts, mode selection, glyph aliases, symbol and
ornament identities, and full-font embedding require no user configuration.
This intentionally changes the default PDF and distribution sizes. Native
binaries contain the 58.6 MiB uncompressed resource pack and work offline.
npm contains the same pack; Node reads it locally, while the browser downloads
it into a verified OPFS cache on first native-text conversion. The core and
raw WASM do not embed font programs. See bundled fonts.
Upgrade and breaking changes
cargo install --locked caj2pdf-cli --version 0.8.0
npm install caj2pdf-rust@0.8.0
caj2pdf paper.caj -o paper.pdf| Surface | Previous behavior | New behavior and migration |
|---|---|---|
| CLI | Discover installed fonts unless custom fonts are supplied. | Use the bundled mode-specific preset. No flags are needed. Use --no-bundled-fonts to restore installed-font discovery; add --no-system-fonts to disable both. |
| Node/browser | Callers supply native-text fonts and measured parameters. | convert(module, source, sink) selects the bundled preset. Explicit custom fonts retain their previous behavior; hnc8: { fonts: false } disables automatic fonts. |
| Output PDF | Default text rendering depends on caller/system fonts. | Default native-text PDFs use full original programs and measured mappings. Regenerate PDF hash/size expectations; full-font embedding increases PDF size. |
| Source builds | No bundled resource download is needed. | Install curl or set CAJ2PDF_FONT_PACK to the verified local pack for an offline build. |
| Browser deployment | Caller-provided fonts determine storage needs. | Serve the packaged font asset and use a secure context with OPFS and Web Locks. The first native-text conversion populates a verified cache; later conversions reuse it. |
Use matching JavaScript and WASM builds. Rust core and raw WASM remain
font-data-free; Rust callers can opt into bundled_fonts::configure with
ranged sources for the pinned pack. Existing explicit-font callers need no
new configuration. See bundled fonts for deployment
and override examples.
Validation and limits
No additional document profile is admitted. C8/HN-B outlines remain omitted;
existing codec, image-raster and unsupported-profile limitations remain.
The immutable font-data fonts-v1 release is a build input, separate from
the converter release. No external document corpus is distributed.
Validation: CLI, Node and Chromium each reproduce all 13 retained documents /
77 pages from the explicit Wine-reference configuration byte-for-byte. See
the receipt. This preserves
the prior Wine comparison and its remaining raster differences; it does not
claim universal pixel equivalence. The implementation checkpoint passes
1,470 Rust tests and 194 JavaScript tests, including real Chromium. Seven
ignored optional Rust corpus tests are excluded from compatibility passes.
The actual npm tarball and an extracted native archive were tested with
automatic fonts, and the Rust registry packages passed isolated build checks.
The tag workflow gates publication on the complete native/container inventory,
quality and packaged-artifact checks, and verification of artifact attestations.
Its run and uploaded SHA256SUMS are authoritative for released bytes.
It publishes core/CLI to crates.io and the tested JavaScript tarball to npm
after the GitHub release succeeds. Windows and macOS binaries are unsigned;
see build provenance for verification commands.
v0.7.0
v0.7.0 — original fonts, Wine rendering and measured format recovery
This unstable minor release includes breaking API and PDF-output changes since
v0.6.1. It includes the classic-Worker bundling fix documented for
v0.6.2, together with all intervening changes on main.
Highlights
- Explicit original TrueType programs, CJK style roles, symbol/ornament maps
and glyph aliases bring the measured C8/HN-B cohort into normal-reading
agreement with Wine CAJViewer 7.3.151 Simple. Font resources remain supplied
by the caller; the converter does not bundle or download them. - Correct page geometry, Latin origins, type-1 JPEG orientation, rule contrast,
native link appearances and continuous ornament bands. Semantic text remains
separate from the selected glyph. Full font programs increase the measured
PDFs to approximately 21–32 MB; subsetting remains the default. - Add explicit-response conversion for the measured TTKN PDF profile, more
bounded CAJ/KDH recovery and PDF footer/outline/object-stream profiles, and
additional NJU native C8 records. Unmeasured profiles remain refused. - Fix Worker cancellation and classic-Worker bundling. A conversion failure
combined with failed spool cleanup now exposes both errors.
Upgrade overview
cargo install --locked caj2pdf-cli --version 0.7.0
cargo add caj2pdf-core@0.7.0
npm install caj2pdf-rust@0.7.0Use matching JavaScript and WASM builds. Users coming from v0.5.0 must also
apply the v0.6.0 migration.
| Surface | Required migration from v0.6.1 |
|---|---|
| Rust font structs | Add the optional style roles, maps, identity fields and embedding mode described below; prefer ..Fonts::default() for owned fonts. |
| Native/CLI/Node/browser PDFs | Reconvert and regenerate page-box, coordinate and PDF-hash expectations. Font maps must contain the required glyphs. |
| Native C8 notes | Handle unsupported-format errors for unknown required note appearances; supported links now appear in output. |
| CLI TEB inspection | Match unsupported_reason: "not-implemented" instead of "drm-encrypted". |
| JavaScript spooling | When conversion and cleanup both fail, inspect AggregateError.cause for the original failure and errors[1] for cleanup. |
The detailed sections below give previous/new behavior and migration examples.
Validation and remaining limits
The final Wine study covers 13 unchanged documents / 77 pages, reviewed
at reading size, with 113,418 semantic characters retained and 133 link
annotations on 11 pages. Original controls include independent repeats,
negative controls and multiple zooms. Final CLI, Node and real Chromium outputs
agree byte-for-byte for 16 actual/control documents per runtime; writes
are at most 65,536 bytes. See the final band study
and its linked font, geometry, rule and link reports for reproducible receipts.
The implementation checkpoint passed 1,466 Rust tests and 192 JavaScript
tests. Seven ignored optional Rust corpus tests are excluded from successful
compatibility evidence. Package/version preparation reruns locked native/WASM
builds and the actual npm tarball's Node/Chromium tests. The tag workflow gates
publication on quality checks, every required native/container target and
verification of artifact attestations. Its run and uploaded SHA256SUMS are
authoritative for the released artifacts.
The format-specific measurements below are attributed historical checkpoints,
not a new full-corpus run for this version. General patterned-ornament cadence,
small image-edge/resampling differences and antialiasing remain. Link marks
approximate a device-fixed Wine pattern; printing and navigation are untested.
No universal Wine/native-Windows pixel equivalence is claimed. C8/HN-B outlines,
other TTKN profiles, TEB conversion and missing-source-data cases retain their
documented limits. External documents, fonts and screenshots are not included.
Windows and macOS binaries are unsigned. GitHub assets include native archives,
WASM/npm, the OCI archive, checksums and provenance bundles. The existing OIDC
workflow publishes caj2pdf-core, caj2pdf-cli and caj2pdf-rust only after the
GitHub release succeeds; the Rust WASM crate remains unpublished. See
build provenance for consumer verification.
Additional features and fixes
- TTKN PDFs (#504, #505, #506): Rust adds
TtknResponseand
convert_with_ttkn_response; CLI adds--ttkn-response-file FILE;
Node/browser add{ ttknResponse: response }. The matching 32-character
case-sensitive hexadecimal ASCII response is caller-supplied. Conversion
contacts no authentication endpoint. Existingconvertand options remain
compatible. The measured original's 180 pages, 234 decrypted streams and
97 outlines match independent recovery across native/Node/Chromium; this is
not all-page vendor-viewer fidelity. See profile and bounds. - PDF profiles (#494–#496, #507, #510, #511): accept the measured framed
WebFastLoadmetadata, complete block padding, unused object-zero sentinel,
package XML suffix and bounded acyclic object-stream collection links.
Existing graph, ambiguity and payload-preservation checks remain in force.
See input bounds and conformance. - NJU C8 records (#513, #514, #516): independently controlled title styles,
glyph flags, state records and single-segment geometry admit three originals
with 17 pages. All 29,986 glyphs and 27 vectors were checked; native, Node
and Chromium outputs agree. See provenance. - Classic Workers (#433, #517): remove top-level
awaitfrom the Worker
module graph so default Vite/Rolldown IIFE production builds succeed. - Dependencies: TTKN adds the documented MIT-compatible RustCrypto and
base64 dependencies;signal-hookupdates to 0.4.5. There is no dependency
update in the release-version commit itself. Source/license audits remain
required and no vendor/private HN/JBIG implementation is migrated.
Detailed changes and migration
Breaking CLI inspection value: TEB support reason (#469)
For TEB, inspect --json now reports "unsupported_reason":"not-implemented"
in place of "drm-encrypted". Update callers matching drm-encrypted to
accept not-implemented; format: "TEB", conversion_supported: false and
unknown page/outline metadata are unchanged. CLI text and Node/browser errors
now say conversion is unsupported. Recognizing a TEB prefix does not prove
encryption, corruption or impossibility of recovery. JavaScript retains
UnsupportedFormatError, code: "UNSUPPORTED_FORMAT" and format: "teb";
callers should match these fields instead of diagnostic prose. No supported
conversion/PDF output, I/O limits, dependency or core/WASM API changes.
Breaking Rust API: native symbol glyph maps (#518)
caj2pdf_core::Fonts gains symbol_glyphs: Vec<NativeSymbolGlyph> and now
implements Default; hnc8::C8FontSources gains
symbol_glyphs: &[NativeSymbolGlyph]. Each entry selects the symbols
font character-map entry drawn for one raw HN-B mode-0 symbol code
(hnc8::is_mode_zero_symbol) while extracted text keeps the decoded
character. Codes that decode alike can draw distinct glyphs, and one glyph
can carry different text. Rust callers building these structs with literals
add symbol_glyphs: Vec::new() / symbol_glyphs: &[], or use
..Fonts::default(). An empty map leaves output byte-identical.
A non-empty map must name distinct symbol codes, needs a symbols role
whose font maps every glyph, and applies only to HN-B mode-0 native text.
Native conversion refuses anything else before a PDF is finished, with no
fallback to another glyph or font. Like the fonts themselves, the map is
unread for documents routed to image composition. A symbols source shared
with other roles is embedded as one mapped font for all of them. The core
ships no mappings. Font identity binding and measured source-resource verification are included
in this release, as described below and in the provenance record. No fonts
are bundled, fetched or inferred.
Symbol font identity binding (#518)
A map can be bound to the font it was measured on. Rust Fonts::symbol_font
and C8FontSources::symbol_font, CLI --symbol-font-identity NAME:CHECKSUM,
JavaScript symbolFontIdentity and raw WASM caj2pdf_hnb_set_symbol_font
all take the font's PostScript name and head checkSumAdjustment. A
different symbols font is refused before output is finished, and an
identity without a map is refused. The check uses retained metadata, so no
full-font hash is computed. This guards against using the wrong font by
mistake, not against deliberate tampering: a tool that edits outlines but
leaves the checksum field unchanged would still pass. Rust struct literals
of C8FontSources, and Fonts literals without ..Fonts::default(), add
symbol_font: None.
CLI and JavaScript symbol glyph options (#518)
The CLI accepts repeatable --symbol-glyph CODE=CHAR (four hex digits, then
one BMP character or U+XXXX) with --font-symbols or --fonts DIR.
Node and browser accept hnc8.fonts.symbolGlyphs, an iterable of
{ code, glyph } that requires symbols. The raw WASM ABI adds
`caj2pdf_hnb_add_symbol_glyph(code, glyp...
Font data v1
Immutable font-data input for automatic native C8/HN-B conversion, tracked in #547.
caj2pdf-fonts-v1.bin concatenates ten original font files without changing their bytes. manifest.json records per-file offsets, sizes and SHA-256 identities; FONT-NOTICES.txt preserves embedded attribution. It includes the nine measured CAJViewer 7.3.151 Simple resource fonts and the measured SimSun collection. No document samples or executable converter code are included.
Pack size: 61,493,200 bytes. SHA-256: 8d828782065b3a405736cb51e47a747d0848a61ca47958550e26915de39d68c0.
This is a versioned data release, not a new converter version. Consumers pin and verify its bytes. Published assets must not be replaced.
v0.6.1
v0.6.1 — measured CAJ recovery and npm publication
Measured CAJ tiling-pattern matrices (#414)
- Two measured CAJ originals (264 pages) now convert by making the observed
CAJViewer/Poppler identity fallback explicit for four malformed Pattern
matrices. All pattern streams are retained. Valid matrices and other
malformed profiles keep their existing behavior. - Recovery bounds the header to 512 bytes, proves the exact dictionary and
stream boundary, and preserves source checks and sequential ranged output.
Native, CLI and JavaScript APIs and dependencies are unchanged. - MuPDF interprets the malformed source differently; this is a measured source
viewer recovery, not general scientific-notation support. See the
conformance scope.
Upgrade and publication
There are no new public API or dependency changes since v0.6.0. npm users
upgrading from v0.5.0 must apply the v0.6.0 migration guide:
handle the new CAA format and nullable JavaScript page counts. Use matching
JavaScript and WASM versions.
v0.6.0 was published to GitHub and crates.io through the release workflow,
but npm interpreted its tarball path as a GitHub shorthand and the npm upload
failed before authentication. This release adds the explicit ./ local path
and uses the normal tagged OIDC pipeline for all three packages. The existing
v0.6.0 tag and assets are unchanged; no npm v0.6.0 is published by this release.
The Rust WASM crate remains unpublished.
Validation scope
The conformance checkpoint
records prior validation of both unchanged originals across native, Node and
Chromium, all 264 pages and 207 bookmarks, plus the broader frozen-corpus
receipt. These are attributed historical measurements, not a new full corpus
run for this version bump. Optional external-corpus checks without inputs are
NOT_RUN. Remaining unsupported inputs and fidelity limits still apply.
The tag workflow requires native/WASM quality checks, the complete platform
matrix, Node/Chromium checks of the actual npm tarball and artifact attestation
verification before registry uploads. GitHub assets include native binaries,
WASM/npm, OCI, checksums and provenance bundles. Windows/macOS binaries remain
unsigned. See build provenance.
cargo install --locked caj2pdf-cli --version 0.6.1
cargo add caj2pdf-core@0.6.1
npm install caj2pdf-rust@0.6.1v0.6.0
v0.6.0 — CAA descriptor recognition and trusted registry publishing
This unstable release recognizes the independently measured CAA target-descriptor
profile across Rust, the CLI, Node.js and browsers. Inspection reports the format
with unknown counts. A descriptor contains no document pages: conversion returns
the typed unsupported error before writing PDF bytes and asks for the referenced
document. Target values are neither decoded nor fetched.
Breaking changes and migration
-
Rust
InputFormatgainsCaa. AddInputFormat::Caato exhaustive matches;
route it to a request for the referenced document rather than conversion.
DocumentInfo.page_countremainsOption<u32>and isNonefor CAA. -
JavaScript
Formatgains"caa", andDocumentInfo.pageCountchanges from
numbertonumber | null. Handle the new format and guard unknown counts:const info = await inspect(module, input); if (info.pageCount === null) { console.log("Page count is unknown"); } else { console.log(`${info.pageCount} pages`); }
-
CLI
inspect --jsoncan report"format": "CAA", null counts and
"unsupported_reason": "target-descriptor". Consumers must handle that
format; converting a CAA descriptor exits with status 1. -
Raw WASM adds format code 8, preserving codes 0–7. CAA inspection returns
raw page count 0 and bookmark count -1; the JavaScript adapter maps unknown
counts to null. Upgrade JavaScript and WASM together.
Existing document conversion behavior and PDF output are unchanged by CAA
recognition. The bounded probe reads at most 1,024 bytes; the complete measured
field profile is required. Partial or contradictory descriptors are rejected.
No new dependency or external document payload is included.
Format evidence and limits
The NH/CAA checkpoint
records 18 unique external CAA descriptors and an original .nh HN-A document
converted through CLI, Node and Chromium: 433 pages, 365 bookmarks and identical
PDFs. This is the recorded research receipt, not a new release corpus run or a
viewer pixel-parity claim. File extensions do not define input signatures.
Historical CAS remains an unverified sample gap; no CAS decoder is claimed.
Other v0.5.0 limits,
including unverified C8/HN-B outlines and substitute-font fidelity, still apply.
Optional external-corpus tests without inputs are NOT_RUN.
Publication and validation
This is the first release intended to publish caj2pdf-core, caj2pdf-cli and
npm caj2pdf-rust through GitHub OIDC after all release gates. The WASM Rust
crate remains unpublished. The npm upload uses the same tested tarball as the
GitHub asset and requests npm provenance; no long-lived registry token is stored
in CI. Successful publication is established by the tag workflow and registry
records, not by this intention.
The release workflow requires locked native/WASM quality checks, the complete
platform inventory, Node/Chromium tests of the actual npm package, and verified
artifact attestations before publishing. GitHub assets include native binaries,
JS/WASM, OCI, SHA256SUMS and Sigstore bundles. Windows/macOS binaries remain
unsigned. See build provenance for verification.
cargo install --locked caj2pdf-cli --version 0.6.0
cargo add caj2pdf-core@0.6.0
npm install caj2pdf-rust@0.6.0v0.5.0
v0.5.0 — synchronous core, Worker APIs and more document profiles
This unstable release replaces the asynchronous Rust core and scratch bitmap
stores with synchronous ranged reads, sequential writes and per-image,
per-dictionary or per-page allocations bounded by Limits. Browser and Node.js
conversion runs in a Worker. The CLI retains its conversion commands and adds
explicit damaged-CAJ conversion. Update native API consumers and JavaScript
call sites together with the matching WASM build.
Breaking changes and migration
This table describes the final v0.5.0 API. The changelog
records individual removals, error-message changes and intermediate refactors.
| Surface / old usage | v0.5.0 migration |
|---|---|
Rust asynchronous source/decoder calls, SequentialSink and scratch workspaces |
Remove .await; provide RangedSource (or native::SeekableSource) and std::io::Write. Remove scratch resources. Keep inputs unchanged for the entire operation; HN/C8 payload SHA-256 rechecks are removed. |
| Separate format dispatch | Call the root convert, inspect, inspect_pages, read_outline or index_pdf facade. ConversionOptions is borrowed and carries format/font policy. Prefer ..ConversionOptions::default() in literals. |
| Codec-specific budget structures | Use one Limits; former per-codec budget parameters are removed. max_image_pixels and max_symbols cap decoded bitmaps and dictionaries; max_allocation_bytes caps individual retained buffers. |
| Codec-specific error enums | Match Error.kind (ErrorKind), then use offset, context and reason. Removed error payloads are not available. HN/C8 I/O/cancellation now uses JS IO/CANCELLED. |
| Complete report literals or copying reports | ConversionReport gains outline, application_info, images, omitted_pages and substituted_glyphs; use Default where appropriate. Reports containing omissions are no longer Copy; borrow or clone explicitly. DocumentInfo.page_count is Option<u32>. |
JS instance/exports and {size, readAt} wrappers |
Pass the WebAssembly.Module from loadModule() and a browser File/Blob/OPFS handle, or a Node path/URL/descriptor/Blob. Remove blobSource, fileHandleSource and hnc8.scratch. Font sources use the same input types. |
| Custom raw WASM poll/resume hosts | Replace caj2pdf_start/caj2pdf_io_* with synchronous caj2pdf_convert/caj2pdf_inspect and the five host imports in the ABI reference. Rust hosts use engine::Session/Host, not Engine/Request. |
TrueTypeFont, whole-font embedding |
Use OpenTypeFont, supply a face index (0 for a single face), and call embed_font after the last draw before finish. Fonts are subsetted/compressed; CFF and collection faces are supported. |
| Caller-provided QM/MQ tables | Remove CLI --qm-states/--mq-states and JS hnc8.qmStates/mqStates. Built-in standard codec states are used. Removed selected-image helpers are replaced by document conversion; see the changelog for low-level decoder removals. |
| CLI native-text input without font options | Installed fonts are discovered automatically. Use --no-system-fonts for the old no-search behavior, or pass explicit font paths. JS never searches installed fonts. |
| Output byte identity and early size failures | Font/content compression, font subsetting, top-first type-0 image matrices and the outline trailer /ID change PDF bytes. Output limits can fail during sequential writing; stage output if atomic publication is required. Do not compare old/new PDF hashes as a fidelity test. |
// Before: convert(instance, blobSource(file), sink, { hnc8: { scratch } })
// v0.5.0: file is a browser File; sink is a supported sequential sink.
const module = await loadModule();
await convert(module, file, sink, { signal });For prompt browser cancellation and sink backpressure use a cross-origin
isolated page (SharedArrayBuffer). Otherwise cancellation terminates the
Worker. Spooled inputs are now a Node path or OPFS handle. See the
JS examples and native architecture.
Conversion improvements and remaining limits
- More measured CAJ/PDF/KDH profiles: bounded source-path metadata repair,
absent optional link appearances, equivalent opacity resources, interrupted
live prefixes, duplicate identical page boxes, PNG Up xrefs and Flate
object-stream metadata. Unknown or contradictory profiles still fail. - More measured HN-A, C8 and HN-B profiles, including generic-only JBIG2,
empty symbol content, uncompressed images, full-page JPEG region records,
native article records and HN-B type-3 images after text. --allow-damagedexplicitly permits reported blank substitutions for eligible
damaged CAJ pages. Exit 3 means partial output was committed. JS callers use
allowDamaged: trueand inspectomittedPages; default conversion stays strict.- Native text needs caller/installed fonts; no fonts are bundled. Substitute
fonts are not original-font fidelity. A measured private-use HN-B character
may use a reported visual substitute while preserving U+E6C7 in ActualText. - C8/HN-B bookmarks remain unverified and omitted with a warning (#303).
Image pages receive no OCR. TEB remains unsupported. Corpus recovery and
source-fidelity work (#406, #414, #415, #420) remains open.
Validation and artifacts
The release workflow reruns locked native/WASM quality gates, the complete
platform inventory, actual npm tarball tests on Node and Chromium, and
artifact provenance verification. Windows/macOS binaries remain unsigned.
GitHub assets include native CLI archives, JS/WASM, OCI and SHA256SUMS;
verify them using the provenance guide.
The registry packages are caj2pdf-core, caj2pdf-cli (binary caj2pdf) and
npm caj2pdf-rust. caj2pdf-wasm remains unpublished as a Rust crate.
Initial registry publication uses owner authentication; trusted publishers
are configured after bootstrap. A GitHub release alone is not proof that a
registry upload succeeded. Release policy documents
this transition and the required clean-install checks.
The conformance record separates corpus conversion,
runtime parity, glyph/image checks and selected source-render comparisons.
The 1,232 accepted originals at #408 passed native/Node/Chromium parity;
that is historical source evidence, not a fresh full-corpus run of this tag.
Optional external corpus tests for this release are NOT_RUN unless a separate
receipt records them. Filtered guest rendering tests are not compatibility
passes; their portable conversion checks and host smoke tests are separate.
The #346 source-size target remains unmet (37.9k vs 30k at its recorded
checkpoint); this release does not claim it is complete.
Detailed conversion checkpoints
The entries below preserve the recorded scope and timing of each pre-release
validation checkpoint; later checkpoints supersede earlier failure counts.
Unescaped CAJ source-path metadata (#419)
- A measured 139-page CAJ original now converts after two malformed QITE
source-path strings are preserved as hexadecimal strings; all 35 source
bookmarks remain intact. All incoming
references must be confined to retained Page metadata; rendering strings
and unmeasured profiles remain strict. - The repair uses bounded reads, source rechecks and existing sequential
reconstruction. No public API or dependency changes. See the
scoped conformance evidence.
Absent optional CAJ link appearances (#417)
- A measured 109-page CAJ original now converts after proving two optional
Link appearance targets absent. Its live destinations, 574 source streams
and 70 source bookmarks remain intact. Required references and unmeasured
appearance/border profiles remain strict errors. - Existing bounded metadata reads, retained repair budgets and sequential
reconstruction apply. No public API or product dependency changes. See the
scoped source comparison.
Equivalent PDF opacity resources (#412)
- Two KDH originals now convert after proving duplicate ExtGState references
carry exactly equal direct CA/ca values. Unknown or conflicting resources
remain errors; equal-looking rounded numbers are never used as proof. - All 14 pages and 151 streams match independent source decoding, and output
bytes agree across native, Node.js and Chromium. See the
checkpoint.
No public API or dependency changes; target reads, metadata and incremental
repairs remain bounded. Existing v0.4.0 artifacts are unchanged.
Interrupted live PDF prefixes (#410)
- A measured seven-page KDH file now converts after bounded validation and
whitespace replacement of inactive object prefixes. Only exact prefixes of
xref-selected complete non-stream dictionaries/integers qualify; conflicts,
stream objects and unrelated gap contents remain errors. - Native, Node.js and Chromium outputs match; all page renders, text, geometry
and 22 raw streams match the independent source PDF. See the
checkpoint.
No public API or dependencies change; reads, parser metadata and retained
patches remain bounded under existing limits.
Corpus runtime and order verification (#406)
- All 1,232 accepted originals at PR #408 pass native/Node/Chromium byte parity
and browser cleanup. All 26 formerly failing applicable order checks pass
after correcting bitmap row conventions and adding native glyph checks. - The checkpoint
records the scope and remaining source-fidelity/r...
v0.4.0
v0.4.0 — native C8/HN-B pages, font fallback and tolerant outlines
v0.4.0 is the first release with native-text C8 and HN-B pages. It also
reads more real documents without failing: damaged HN-A outlines are
repaired with warnings, C8/HN-B outlines are omitted with a warning, and a
missing font role falls back to the CJK or Latin font. v0.x is unstable;
breaking changes and their migration are listed below.
Conversion scope
- HN-A: verified paired raw/compressed framing, per-page dimensions and marked
image coordinates. The recorded corpus passes 19/19 HN-A inputs; this does
not establish universal HN-A support or full viewer pixel parity. A
malformed outline entry is skipped or clamped with a located warning
instead of failing the document. - HN-B: admitted mode-0/mode-2 native text profiles and both verified index
layouts. Three selected documents have complete 4/4/6-page runtime checks.
Mode-2 leading images are supported; images after text and mode-0 images
remain explicit errors. - C8: the admitted six-page profile and additional four/five-page profiles
preserve native text, drawings and descriptor-ordered images. A trailing
application-info package adds/CNKI_DOIand/CNKI_URLto the PDF
/Infodictionary. - CAJ: bounded interrupted-object and stream recovery admits independently
validated complete copies. Corrupt or ambiguous content still fails. - PDF: a
%PDF-header that follows a newline, byte-order mark or junk line
within the first 1,024 bytes is now recognized. - TEB: still rejected, now with a message naming it a DRM-encrypted container
and"unsupported_reason":"drm-encrypted"ininspect --json.
C8/HN-B outline layouts are unverified. Those PDFs have no outline and the CLI
prints one warning; --no-bookmarks (JS includeBookmarks: false) gives the
same PDF silently, and JS reports outlineOmitted: true. Unknown outline
metadata is not a confirmed empty table of contents (#303).
Native text pages need a caller-supplied CJK and Latin TrueType font, for
example with --fonts DIR. The CLI reference
documents a tested free recipe, Droid Sans Fallback with DejaVu Sans, that
covers all six pinned C8/HN-B corpus inputs. Fonts are not bundled.
Substitute fonts do not reproduce the original typography. Image-only pages
receive no OCR. See current support
and Node/browser usage.
Other changes
caj2pdf inspect --pagesprints a structure-only per-page report that is
safe to share in a bug report. It contains no text, titles or pixels.- Conversion shows input progress on a terminal;
-q/--quiethides it. - Native HN/C8 scratch I/O uses positioned reads and writes, cutting system
calls by almost two thirds on a 163-page HN-A input. - New CLI archives: i586 GNU, FreeBSD RISC-V64 GC and FreeBSD PowerPC64.
Breaking changes and migration
These changes affect direct Rust users and custom WASM hosts; the bundled JS
adapters handle resource routing. CLI scripts keep working; the visible CLI
differences are the C8/HN-B warning, the font fallback and the progress line
on a terminal.
| Surface | Change and migration |
|---|---|
| Rust toolchain | Minimum Rust version becomes 1.88.0. Update older toolchains before building. |
C8PageFonts literals |
alternate_latin becomes Option<usize>; wrap old values in Some. Add symbols, latin_state3, latin_state28, latin_state31; use None for absent roles. An absent role or unmapped glyph now falls back to the CJK or Latin font instead of failing. |
Hnc8Reader::visit_bookmarks |
Returns an OutlineReport (declared, written, defects, unverified) instead of the declared count. Read report.written for the old count of emitted entries. |
ComposeReport |
Gains outline and application_info. Construct it only through the converter, or add the fields with their defaults. |
hnc8::Header literals |
Include native_origin and native_mode from parsed metadata rather than inventing values. HN-B native_origin and page_size can now be Some even for zero extents; their presence is not a renderability test. |
TextCoordinates literals |
Include page_size. Admitted paired HN-A pages now use verified page dimensions instead of always inheriting the document header; output geometry can change. |
NativeRecord::End |
value becomes Option<u16>; wrap explicit old values in Some and handle None as an absent payload. |
| Exhaustive native-record matches | Handle added ImageReference, ExtendedControl and EncodedString events. Updated C8 drawing framing preserves following controls separately, changing visitor event counts. |
| Rust WASM engine | Request::Read includes resource; route document and font reads to the registered source. Engine::set_c8_fonts adds the symbols argument (u32::MAX when absent) and accepts 0xffffffff as an absent alternate role. Existing raw WASM font-setter exports remain available. |
| C8/HN-B bookmarks | Requesting bookmarks no longer fails; the outline is omitted with a warning. Callers that relied on the error should check outlineOmitted (JS) or OutlineReport::unverified (Rust). |
| Invalid HN-B input | Unknown layout markers and invalid compact-index fields are rejected explicitly; do not rely on permissive parsing. |
| Damaged CAJ streams | Flate Length recovery now validates the compressed stream. The recorded issue-20 input changes from a warning-producing old output to explicit rejection; no unchecked repair fallback is provided. |
For exact resource registration and extended font roles, use the
I/O and ABI reference. The
changelog lists every change since v0.3.1.
Evidence and limits
The refreshed pre-tag Linux CI baseline ran all 56 fixed corpus inputs using
the v0.4.0 artifact from main 0153d22 (run 37251767647): 38 default
conversions, 13 unsupported inputs (seven TEB and six font-free HN-B/C8),
and five damaged CAJ failures. This font-free run is separate from explicit-font
HN-B/C8 validation. It does not yet verify the final tagged artifact identity. External documents, fonts,
captures and copied text stay outside Git and release assets.
Unicode evidence separates glyph transport from
independent source checks, extraction order, spacing and visual appearance.
Marker-font or adapter-hash agreement does not establish original-font pixel
parity. Missing optional evidence remains NOT_RUN.
Conformance evidence records representative native RSS,
WASM high-water capacity, bounded requests and scratch peaks. These are
measured cases, not a universal memory bound. Ranged input, sequential output,
forward-only spooling and failure cleanup remain the conversion model.
Artifacts
The release carries 53 native OS/CPU/ABI CLI archives, adding i586 GNU and
FreeBSD RISC-V64/PowerPC64 to v0.3.1's 50. Baselines are documented in
platforms. It also carries the tested JS tarball,
standalone WASM, the OCI container archive and SHA256SUMS. Nothing is
published to npm or crates.io (#293).
The release workflow builds every asset from the tagged commit, runs the full
platform matrix and attests the complete file set and the container digest.
The uploaded SHA256SUMS is authoritative. Verify downloads with the
build provenance guide. Windows and macOS binaries
are unsigned.
v0.3.1
v0.3.1 — verifiable release build provenance
This unstable maintenance release adds GitHub Actions artifact attestations.
There are no conversion, native API, CLI, browser/Node API or PDF behavior
changes from v0.3.0, and no new breaking changes.
Downloads and verification
The same 50 native OS/CPU/ABI archives, browser/Node JS package, standalone
WASM and ten-platform OCI archive are provided. Names use v0.3.1 for native
archives and caj2pdf-rust-0.3.1.tgz for the JS package. GHCR is
ghcr.io/rwv/caj2pdf-rust:v0.3.1; prefer its verified immutable digest.
RELEASE-PROVENANCE.sigstore.json authenticates all release payloads and
SHA256SUMS. CONTAINER-PROVENANCE.sigstore.json authenticates the registry
image digest, and is also published as an OCI attestation. These two signature
bundles are not listed in SHA256SUMS; verify their signatures with gh attestation verify. All other uploaded files are covered by the signed
checksum manifest. GitHub-generated source archives are outside this set.
See the verification guide for commands enforcing
repository, workflow, tag and commit identity. The publisher verifies every
file and the registry attestation before exposing the GitHub release. A
modified-file rejection check is mandatory. This is release-workflow provenance
from the aggregation job, not a claim of SLSA level 3 or platform code signing.
Earlier published releases are unchanged.
Existing limits
The v0.3.0 platform and format limits still apply, including
runtime/libc baselines, caller-supplied experimental HN/C8 tables and deferred
candidate architectures. Browser and Node are both supported. Native archives
include MIT dependency notices; macOS/Windows executables have no platform
code signatures. No npm, crates.io or Docker Hub publication is included.
Release CI reruns required native, WASM, JS, license, coverage and platform
checks. The private CAJSamples corpus is not shipped or run by public CI;
missing optional-corpus checks remain NOT_RUN, not compatibility passes.
v0.3.0
v0.3.0 — wider native and container platforms
This unstable release expands delivery of the MIT Rust converter. Conversion
formats, bounded input/scratch, sequential output and PDF encoding retain the
v0.2.0 behavior. Browser and Node remain first-class WASM targets.
Downloads
Choose caj2pdf-v0.3.0-<target>.zip on Windows or .tar.gz elsewhere. The exact
required set contains 50 OS/CPU/ABI targets and is recorded in docs/platform-targets.json; docs/platforms.md explains
CPU/ABI, libc and OS baselines. Additions include LoongArch64 GNU/musl, ARMv5TE,
ARMv6, further ARMv7 variants, MIPS32/64 in both byte orders, PowerPC32/64,
SPARC64, i586 musl and additional musl variants, RISC-V32 GNU, NetBSD
x86_64/ARM64, OpenBSD x86_64/ARM64/RISC-V64, illumos x86_64 and Android x86/x86_64 command-line executables. Bootlin musl targets are dynamically linked and need
the matching runtime; other listed musl builds are static. A libc variant is not
counted as a distinct CPU architecture. Unverified candidates are documented
separately and do not appear among release assets.
caj2pdf-rust-0.3.0.tgz: browser and Node 22+ JS package, WASM and declarations.
Install withnpm install ./caj2pdf-rust-0.3.0.tgz.caj2pdf_wasm.wasm: standalone module for the ranged-I/O JS adapters.caj2pdf-container.oci.tar: the exact tested multiarch static CLI image.CONTAINER-DIGEST.txt: digest ofghcr.io/rwv/caj2pdf-rust:v0.3.0.SHA256SUMS: hashes for every published asset.
The Docker platforms are linux/amd64, linux/arm64, linux/arm/v5,
linux/arm/v6, linux/arm/v7, linux/386, linux/loong64, linux/riscv64,
linux/ppc64le and linux/ppc64. Images run as non-root on scratch and need
writable output and temporary storage. Large documents should use disk-backed
scratch instead of tmpfs. See docs/docker.md for usage. There is no npm,
crates.io or Docker Hub publication in this release.
Breaking change: browser cleanup errors
OPFS spool removal retries only NoModificationAllowedError, at most three
attempts with 10 ms and 50 ms waits. If removal permanently fails after a spool
error, the promise now rejects with AggregateError; its cause and first
errors entry preserve the original error, and the second entry is the removal
failure. Previously the removal failure was suppressed. Callers matching a
primary error code can migrate with:
const primary = error instanceof AggregateError ? error.cause : error;
if (primary?.code === "LIMIT_EXCEEDED") {
// Handle the input limit and separately report error.errors when present.
}A temporary file may
remain after such a reported failure. The exact cause of the single original
Chromium cleanup failure was not reproduced; deterministic error injection and
real-browser tests validate the hardening without claiming a browser root cause.
Limits and evidence
CAJ/PDF/KDH retain their documented profiles. HN/C8 remains experimental with
known viewer-pixel/geometry differences; C8/HN-B requires --no-bookmarks.
No OCR, searchable HN or TEB support is added. Absent optional corpora remain
NOT_RUN. Windows archives are unsigned and macOS archives are not notarized.
User-mode emulation is runtime evidence, not certification of every old kernel,
physical CPU or vendor ABI. Some rare targets build std with a separate pinned
nightly; the normal project compiler remains pinned stable Rust.
Every published target must run its declared tests; build-only success is not
support. Native, WASM/JS, MIT/advisory and exact 100% Linux Rust line-coverage
gates remain mandatory. Other OSes have separate runtime evidence, not a 100%
coverage claim. Release publication checks the complete native inventory and
container manifest, preserves tested binaries and checksums every asset.