import { ConvertResult, SourceDoc } from './facade.js'; import { FontBytesByVariant } from '../font/index.js'; import { FetchLike } from '../fonts/index.js'; import { FontProvider } from '../fonts/provider.js'; import { Loss } from '../ir/index.js'; import { DocumentReader } from '../ir/adapters.js'; import { FlowDoc } from '../ir/flow.js'; import { SheetDoc } from '../ir/sheet.js'; import { StreamFilters } from '../../pdf-reader/document.js'; import { SignatureOptions, StyledRenderOptions } from '../../pdf/index.js'; /** The output formats {@link Ream.convert} can produce. */ export type ReamTarget = 'pdf' | 'svg' | 'html' | 'md' | 'docx' | 'xlsx'; /** Options for {@link Ream.parse}. */ export interface ReamParseOptions { /** Reader registry override; defaults to the built-in docx + xlsx readers. */ readonly readers?: ReadonlyArray>; /** * The password an encrypted source is opened with: a PDF's user password * (ISO 32000 §7.6) or an OOXML package's (ECMA-376 §2.3, MS-OFFCRYPTO). * Defaults to the empty string, which opens a PDF's common permissions-only * encryption (EP14); an encrypted OOXML package always names a password. */ readonly password?: string; /** * PDF only: decoders for `/Filter` names the reader does not implement * (§7.4). A filter it cannot undo leaves that stream unread — and when the * unread one is the cross-reference, the whole document is missing — so a * caller who needs such a file supplies the decoder rather than the library * carrying one for every filter anyone might write: * * ```ts * // browser: any wasm/JS decoder you ship — brotli-dec-wasm is ~200 KB * import brotliPromise from 'brotli-dec-wasm'; * const brotli = await brotliPromise; * Ream.parse(pdf, { filters: { BrotliDecode: (b) => brotli.decompress(b) } }); * ``` * * ```ts * // node, where the runtime already carries one * import { brotliDecompressSync } from 'node:zlib'; * Ream.parse(pdf, { filters: { BrotliDecode: (b) => brotliDecompressSync(b) } }); * ``` * * Absent, or throwing, the filter is reported unreadable by name. */ readonly filters?: StreamFilters; } /** * Options for {@link Ream.convert} and {@link Ream.convertWithReport}. Extends * the low-level {@link StyledRenderOptions} (minus the font `registry` and * `styles`, which Ream builds itself) with font resolution and source-touching * conveniences. */ export interface ReamConvertOptions extends Omit { /** Explicit font bytes per variant (regular/bold/italic/bold-italic). */ readonly fonts?: FontBytesByVariant; /** Shorthand for supplying a single regular-variant font as raw bytes. */ readonly fontBytes?: Uint8Array; /** Substitute family hint for the auto-download path. */ readonly fontFamily?: string; /** Injectable `fetch` for the auto-download path (defaults to the global `fetch`). */ readonly fontFetch?: FetchLike; /** * Font resolution chain (caller/embedded/local/remote), used when neither * `fonts` nor `fontBytes` is given. A remote or local winner records a * substitution {@link Loss}. */ readonly fontProviders?: ReadonlyArray; /** Throw {@link ConversionLossError} on the first loss instead of reporting it. */ readonly strict?: boolean; /** PDF/A-3 only: embed the parsed source file (`/AFRelationship /Source`). */ readonly embedSource?: boolean; /** Digitally sign the output (ISO 32000 §12.8, WebCrypto). */ readonly signature?: SignatureOptions; /** * Reference date for spreadsheet conditional-format `timePeriod` rules and for * `TODAY()`/`NOW()` in `expression` rules (E-SHEET W9). Supplying it re-projects * a spreadsheet source so those clock-relative rules resolve against this date — * an explicit input, never the wall clock. Omitted, they no-op and the output is * unchanged. */ readonly now?: Date; /** * §18.3.1.34 `&F` — the workbook's file name, for a spreadsheet whose header * or footer prints it. A byte-oriented reader cannot know it; supplied here, * the code resolves, and omitted it is dropped exactly as before. */ readonly fileName?: string; /** * Markdown only: how a picture reaches the output — inlined as a `data:` URI * (the default), named under `./media/` for a caller that writes the bytes * itself, or dropped. See {@link MarkdownWriteOptions}. */ readonly images?: 'dataUri' | 'link' | 'drop'; /** * Markdown only: what a page break becomes — nothing (the default), or the * `---` thematic break a slide deck wants between its slides. See * {@link MarkdownWriteOptions}. */ readonly pageBreaks?: 'rule' | 'drop'; /** * Markdown from a SPREADSHEET only: open each sheet with a heading carrying * its tab name. On by default — markdown has no pages to tell one sheet from * the next by, so without them a workbook is a pile of tables with nothing to * say which is which. Set `false` for the bare tables. */ readonly sheetNames?: boolean; } /** * The object face of the library: parse a document once into the format-neutral * {@link FlowDoc} interlayer, then convert it to any number of targets without * re-reading the source. * * ```ts * const doc = Ream.parse(bytes); // sniff → reader → FlowDoc * const pdf = await doc.convert('pdf', { fonts }); * const svg = await doc.convert('svg', { fonts }); * ``` * * It is a thin GRASP Controller: readers parse, `flowRenderOptions` projects the * FlowDoc, and layout/emit plus the writers do the work. As a deliberate * composition root, importing it pulls in every format module — prefer the * per-format functions when bundle size matters more than convenience. The * source bytes are retained only for the two source-touching features: docx * substitute-font auto-detection and PDF/A-3 `embedSource`. */ export declare class Ream { readonly flow: FlowDoc; readonly sheet: SheetDoc | undefined; readonly losses: ReadonlyArray; private readonly source; private readonly readerId; /** * @param flow The interlayer — the parsed, format-neutral document tree. * @param sheet The native SpreadsheetML tree when the source is a spreadsheet * (xlsx); {@link Ream.flow} is its projection through the print model. * @param losses Losses recorded while reading the source. * @param source The original source bytes (kept only for docx auto-fonts and * PDF/A-3 `embedSource`). * @param readerId The id of the reader that parsed the source. */ private constructor(); /** * Sniff the format and parse the bytes once into the {@link FlowDoc} interlayer. * * @param bytes The raw document bytes; the format is detected by sniffing. * @param options Optional reader-registry override and/or password for an * encrypted source. * @returns A reusable {@link Ream} instance. * @throws Error when no registered reader recognizes the bytes. */ static parse(bytes: Uint8Array, options?: ReamParseOptions): Ream; /** The source format id (`'docx'`, `'xlsx'`, …). */ get format(): string; /** * Convert the parsed document to `to` and return just the output bytes. A thin * wrapper over {@link Ream.convertWithReport} that drops the loss report. * * @param to The target format. * @param options Font resolution and target-specific options. * @returns The encoded output bytes. */ convert(to: ReamTarget, options?: ReamConvertOptions): Promise; /** * Convert the parsed document to `to`, returning the output bytes together with * the accumulated {@link Loss} report (read-time losses plus any added while * writing). HTML, Markdown, DOCX and XLSX are produced straight from the * interlayer — no layout, no fonts, zero I/O; SVG and PDF run the layout * engine and resolve fonts first. * * @param to The target format. `'xlsx'` requires a spreadsheet source. * @param options Font resolution and target-specific options. * @returns The encoded bytes and the loss report. * @throws Error when `to` is `'xlsx'` but the source has no grid. * @throws ConversionLossError when `options.strict` is set and any loss was recorded. */ convertWithReport(to: ReamTarget, options?: ReamConvertOptions): Promise; /** * Resolve the font set for a layout/PDF conversion. Explicit `fonts`/`fontBytes` * win; otherwise the provider chain is tried, then an open substitute set is * auto-downloaded (per detected family for docx). Any substitution is appended * to `losses`. * * @param options The convert options carrying the font preferences. * @param losses The mutable loss list a substitution is appended to. * @returns The font bytes and, for docx, optional per-family registries. */ private resolveFonts; /** * Fetch one face per writing system the document holds text in — the curated * families are Latin, and Han, Kana, Hangul, Arabic and the geometric symbols * are a notdef box in every one of them. * * Only the regular weight is fetched: Noto Sans SC is ten megabytes, and a * bold run in it is better stroked (see `SyntheticFace`) than downloaded four * times over. A face that fails to arrive is a recorded loss, not a throw — * the rest of the document still renders. * * @param into The registry map the run resolver looks in. * @param flow The parsed document. * @param options The convert options (for the injectable `fetch`). * @param losses Where a face that could not be fetched records itself. */ private addScriptFonts; /** * In strict mode, throw {@link ConversionLossError} for the first recorded loss. * * @param options The convert options (checked for `strict`). * @param losses The losses accumulated so far. * @throws ConversionLossError when `options.strict` is set and `losses` is non-empty. */ private enforceStrict; }