caj2pdf-core 0.6.1

Platform-neutral CAJ-family conversion core
Documentation

caj2pdf-rust

Convert CNKI CAJ-family documents (CAJ, KDH, HN, C8) to PDF from the command line, Node.js or the browser. MIT-licensed Rust with a WebAssembly build. Memory stays bounded: input is read by range and output is written as a stream. Releases are v0.x: CLI behavior and APIs may still change, and HN/C8 support is experimental.

Quick start

Download the CLI archive for your platform from GitHub Releases, extract it, and run:

caj2pdf paper.caj                  # writes paper.pdf next to the input
caj2pdf paper.caj -o out.pdf       # explicit output; --force replaces a file
caj2pdf inspect paper.caj --json   # format, page count, bookmarks

Or install it from crates.io with Rust 1.88 or newer:

cargo install --locked caj2pdf-cli

Releases also carry a container image (Docker usage) and a JavaScript tarball for Node.js 22+ and browsers (JS API). The JavaScript package is also on npm:

npm install caj2pdf-rust

Supported operating systems and CPUs are listed in the platform matrix.

The JavaScript API runs each conversion in a Worker. It reads a browser File, Blob or OPFS file handle, or a Node.js path, descriptor or Blob, and writes to a stream:

import { convert, loadModule, webWritableSink } from "caj2pdf-rust";

const writer = (await (await showSaveFilePicker()).createWritable()).getWriter();
await convert(await loadModule(), file, webWritableSink(writer), { signal });
await writer.close();

What converts

Input Status
PDF Supported: copied, with documented repairs.
CAJ Supported, with its bookmarks.
KDH Supported for embedded PDFs.
HN-A Experimental: scanned page images and bookmarks; no text layer.
C8, HN-B Experimental: image pages, and native text pages with installed or given fonts (below). No bookmarks yet.
CAA (unreleased) Recognized target descriptor: inspection only; obtain the referenced document to convert it.
TEB Rejected: a DRM-encrypted container.

The support matrix is the source of truth, with the verified profiles and remaining differences. Unsupported content fails with a located error instead of dropping pages.

Extensions do not determine the format: the measured .nh sample contains HN-A bytes and converts through that path (433 pages, 365 bookmarks; validation). Historical CAJViewer documentation also names CAS, but no authentic sample has been found; CAS research remains open.

Fonts for C8 and HN-B text pages

Native C8/HN-B text pages need a CJK and a Latin font. Nothing is bundled. Without font options the CLI uses installed fonts from a fixed list and names them on standard error. This free pair covers every tested document:

sudo apt-get install -y fonts-noto-cjk fonts-freefont-ttf
caj2pdf paper.caj -o paper.pdf

To choose fonts yourself, pass --font-cjk FILE --font-latin FILE or --fonts DIR; --no-system-fonts turns the search off. The CLI reference lists the searched directories and faces, the measured comparison, the optional font roles and the fallback rule. Node and the browser take fonts only as explicit options.

Limits

  • C8/HN-B outlines are not verified, so those PDFs have no outline and the CLI prints a warning. --no-bookmarks silences it.
  • HN-A pages are images. Run an OCR tool such as ocrmypdf for search.
  • Substitute fonts do not reproduce the CAJViewer typography pixel for pixel. A measured HN-B private-use glyph may use a visual approximation with an explicit warning; its private-use code is retained in PDF ActualText.
  • TEB files cannot be converted.

Library

Add the Rust library with cargo add caj2pdf-core.

The caj2pdf-core crate is the engine behind the CLI and the WASM build. It reads any RangedSource (SeekableSource over a File, or a byte slice) and writes any std::io::Write:

use caj2pdf_core::{ConversionOptions, Limits, NeverCancel, convert, native::SeekableSource};
use std::{fs::File, io::BufWriter};

let mut input = SeekableSource::new(File::open("paper.caj")?)?;
let mut output = BufWriter::new(File::create("paper.pdf")?);
let report = convert(&mut input, &mut output, ConversionOptions::default(),
                     &Limits::default(), &mut NeverCancel)?;

convert detects the format and converts it; inspect returns the format, page count, outline and structure. ConversionOptions selects bookmarks, damaged-CAJ substitution and C8/HN-B fonts; a Progress receives the detected format and read progress and can cancel. The I/O architecture describes the contract.

More

Contributing, provenance and license

Source code is MIT. Code must be original: do not copy from the Python or Go converters or from decoders under other licenses. The provenance record lists format references, the external test corpus rules and the dependency review. Sample documents are not part of this repository; the separate CAJSamples collection is an optional compatibility corpus. See CONTRIBUTING.md for checks, commit style and the merge gate, and CHANGELOG.md for changes. The project's public language is English.