Examples
Every snippet below is runnable as-is; they all start from document bytes
(Uint8Array) however you obtained them — a File, a fetch, fs.
One parse, many targets
Section titled “One parse, many targets”Ream.parse reads the document once into the interlayer; every convert
renders from it without re-parsing:
import { Ream } from 'reamkit';
const doc = Ream.parse(bytes);
const pdf = await doc.convert('pdf', { fonts });const svg = await doc.convert('svg', { fonts }); // page-stack preview, no PDF involvedBrowser: file input → PDF preview
Section titled “Browser: file input → PDF preview”import { Ream } from 'reamkit';
input.addEventListener('change', async () => { const bytes = new Uint8Array(await input.files![0].arrayBuffer()); const pdf = await Ream.parse(bytes).convert('pdf'); window.open(URL.createObjectURL(new Blob([pdf], { type: 'application/pdf' })));});Node: file in, file out
Section titled “Node: file in, file out”import { readFileSync, writeFileSync } from 'node:fs';import { Ream } from 'reamkit';
const doc = Ream.parse(new Uint8Array(readFileSync('report.docx')));writeFileSync('report.pdf', await doc.convert('pdf'));Archival PDF/A with the source embedded
Section titled “Archival PDF/A with the source embedded”The whole PDF/A family is supported (1a/1b, 2a/2b/2u, 3a/3b/3u). PDF/A-3 can carry the source document inside the PDF as an associated file:
const { bytes: pdfa, losses } = await doc.convertWithReport('pdf', { fonts, pdfA: 'PDF/A-3b', embedSource: true, // .docx/.xlsx rides along as /AF with /AFRelationship /Source});'PDF/A-1a' / 'PDF/A-2a' / 'PDF/A-3a' additionally emit the tagged
logical structure (headings, tables, lists, figure alt text).
Digital signature
Section titled “Digital signature”PKCS#7 detached (ISO 32000 §12.8) via WebCrypto — RSA or ECDSA:
const signed = await doc.convert('pdf', { fonts, signature: { certificate: certificateDer, // Uint8Array, DER privateKey: cryptoKey, // WebCrypto CryptoKey // optional: signingTime, reason, location, pades: true, timestampUrl },});Font resolution chain
Section titled “Font resolution chain”Chain providers; the first byte answer wins. A remote or local winner is
recorded as a substituted loss:
import { Ream, callerFontProvider, localFontProvider, remoteFontProvider } from 'reamkit';
const { bytes, losses } = await doc.convertWithReport('pdf', { fontProviders: [ callerFontProvider(myFonts), // your bytes first localFontProvider(), // system fonts (Chromium Local Font Access; // embedding-restricted fonts are never used) remoteFontProvider(), // open substitute set, last resort ],});// losses[0] → { severity: 'substituted', feature: 'fonts.substitution', … }Fonts embedded in the document itself (w:embed, including obfuscated
.odttf) always win — glyph-exact, no substitution.
Strict mode (compliance flows)
Section titled “Strict mode (compliance flows)”Make any loss fatal instead of reported:
import { ConversionLossError } from 'reamkit';
try { await doc.convert('pdf', { fonts, strict: true });} catch (e) { if (e instanceof ConversionLossError) { // e.loss — what exactly would have been dropped/degraded/substituted }}Inspect the interlayer
Section titled “Inspect the interlayer”parse produces a format-neutral tree before any rendering:
const doc = Ream.parse(bytes);
doc.format; // 'docx' | 'xlsx'doc.losses; // read-time lossesfor (const el of doc.flow.body) { // el.kind: 'paragraph' | 'table' | 'image' | 'chart' | 'shape'}PDF metadata
Section titled “PDF metadata”/Info is read automatically from the document’s docProps/core.xml;
caller values override it:
const pdf = await doc.convert('pdf', { fonts, info: { title: 'Q4 Report', author: 'Finance' },});Hyphenation
Section titled “Hyphenation”import { getHyphenator } from 'reamkit';
const hyphenator = await getHyphenator('en-us'); // or 'ru'const pdf = await doc.convert('pdf', { fonts, hyphenator });