@reportwright/engine 0.12.1 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +89 -5
  3. package/THIRD-PARTY-NOTICES.md +52 -0
  4. package/dist/index.js +10994 -4609
  5. package/dist/pdfstreamworker.js +2 -2
  6. package/dist/types/packages/engine/entry.d.ts +64 -22
  7. package/dist/types/src/engine/data/index.d.ts +14 -1
  8. package/dist/types/src/engine/expr/evaluate.d.ts +1 -0
  9. package/dist/types/src/engine/index.d.ts +11 -3
  10. package/dist/types/src/engine/lazylibs.d.ts +46 -0
  11. package/dist/types/src/engine/notices.d.ts +1 -0
  12. package/dist/types/src/engine/options.d.ts +10 -0
  13. package/dist/types/src/engine/schema/report.schema.d.ts +3 -0
  14. package/dist/types/src/engine/schema/validate.d.ts +2 -0
  15. package/dist/types/src/engine/stream.d.ts +30 -35
  16. package/dist/types/src/engine/text/fonts.d.ts +35 -5
  17. package/dist/types/src/engine/text/measure.d.ts +6 -1
  18. package/dist/types/src/engine/text/standard-metrics.d.ts +190 -0
  19. package/dist/types/src/engine/text/standard.d.ts +42 -0
  20. package/dist/types/src/exporters/docx.d.ts +14 -0
  21. package/dist/types/src/exporters/officestream.d.ts +27 -0
  22. package/dist/types/src/exporters/pdf.d.ts +5 -3
  23. package/dist/types/src/exporters/pdfstandard.d.ts +23 -0
  24. package/dist/types/src/exporters/pdfstream.d.ts +15 -6
  25. package/dist/types/src/exporters/pdfstreamtags.d.ts +2 -0
  26. package/dist/types/src/exporters/spill.d.ts +15 -3
  27. package/dist/types/src/exporters/subset.d.ts +14 -0
  28. package/dist/types/src/exporters/xlsxstream.d.ts +22 -0
  29. package/package.json +4 -3
  30. package/report.d.ts +1 -0
  31. package/schema.json +3 -0
@@ -21,7 +21,7 @@ parentPort.on('message', async (/** @type {any} */ job) => {
21
21
  const m = await render(job.def, { ...job.opts, fontStore, sources: { __rows: { rows: job.rows } }, pageWindow: job.pageWindow });
22
22
  // a writer of its own for the names and glyphs (nothing is written): the pages' links name pages by placeholder
23
23
  const rec = createPdfStream({ write() {} }, { fontStore });
24
- const env = paintEnv(rec, fontStore, { drillBase: job.drillBase, pageRef: (i) => `@@P${i}@@` });
24
+ const env = paintEnv(rec, fontStore, { drillBase: job.drillBase, pageRef: (i) => `@@P${i}@@`, embedAll: job.embedAll });
25
25
  const pages = [], transfer = [];
26
26
  for (let k = 0; k < Math.min(job.keep, m.pages.length); k++) {
27
27
  const pg = m.pages[k];
@@ -31,7 +31,7 @@ parentPort.on('message', async (/** @type {any} */ job) => {
31
31
  pages.push({ deflated: z, annots, width: pg.width, height: pg.height, firstRow: pg.firstRow, rowHead: pg.rowHead, headLevel: pg.headLevel });
32
32
  }
33
33
  parentPort.postMessage({ id: job.id, count: m.pages.length, nextFirst: m.pages[job.keep]?.firstRow, pages, used: rec.used(),
34
- shaped: [...env.shaped].map(([k, g]) => [k, [...g]]), bookmarks: m.bookmarks, headings: m.headings, warnings: m.warnings, ms: Date.now() - t0 }, transfer);
34
+ shaped: [...env.shaped].map(([k, g]) => [k, [...g]]), embedded: [...env.embedded || []], bookmarks: m.bookmarks, headings: m.headings, warnings: m.warnings, ms: Date.now() - t0 }, transfer);
35
35
  } catch (e) {
36
36
  parentPort.postMessage({ id: job.id, error: { message: String(/** @type {any} */ (e)?.message || e), status: /** @type {any} */ (e)?.status, timeout: /** @type {any} */ (e)?.timeout } });
37
37
  }
@@ -8,14 +8,15 @@
8
8
  export function defaultFontStore(opts?: {
9
9
  fontsUrl?: string;
10
10
  }): FontStore;
11
+ export function render(def0: any, opts?: import("../../src/engine/index.js").RenderOptions): Promise<any>;
11
12
  /**
12
13
  * @typedef {Omit<NonNullable<Parameters<typeof exportPdfFull>[2]>, 'subset' | 'pdfa'> & {
13
14
  * fonts?: FontStore, subset?: false | NonNullable<Parameters<typeof exportPdfFull>[2]>['subset'],
14
15
  * pdfa?: boolean | { icc?: Uint8Array, [k: string]: unknown } }} PdfOptions
15
16
  */
16
17
  /**
17
- * Page model → PDF bytes: exportPdf(model, { fonts, title, ... }). fonts: the FontStore render() used (default: the
18
- * one over @reportwright/fonts). Fonts are subset with harfbuzz-subset.wasm from @reportwright/fonts (when it can't be
18
+ * Page model → PDF bytes: exportPdf(model, { fonts, title, ... }). fonts: optional, any FontStore (render()'s saves
19
+ * loading the fonts again; default: the one over @reportwright/fonts); the fonts the model uses are loaded into it. Fonts are subset with harfbuzz-subset.wasm from @reportwright/fonts (when it can't be
19
20
  * loaded the full font is embedded); `subset: false` embeds every font in full, a function replaces the subsetter.
20
21
  * The 0.7 form exportPdf(model, fontStore, options) still works in 0.8 (deprecated).
21
22
  * @param {any} model render() output
@@ -48,32 +49,69 @@ export function exportDocx(model: any, options?: NonNullable<Parameters<typeof e
48
49
  export function exportPptx(model: any, options?: NonNullable<Parameters<typeof exportPptxFull>[1]> & {
49
50
  fonts?: FontStore;
50
51
  }, ...args: any[]): Promise<Uint8Array>;
52
+ export function exportPdfStream(def: any, options: any, sink: any): Promise<{
53
+ streamed: boolean;
54
+ pages: number;
55
+ warnings: string[];
56
+ why?: string;
57
+ }>;
51
58
  /**
52
- * @typedef {RenderOptions & Omit<PdfOptions, 'fonts'> & { fonts?: FontStore, tagged?: { lang?: string, maxElements?: number },
53
- * window?: number, parallel?: number, maxRows?: number, id?: string, presorted?: boolean,
54
- * spill?: false | { dir?: string, maxMB?: number }, sorter?: (cmp: (a: any, b: any) => number) => any }} PdfStreamOptions
55
- * render()'s options and exportPdf's, plus: window (rows laid out at once, 1000), parallel (Node: worker threads
56
- * that lay out and paint pages, opt-in; the file is the same, each thread costs memory), maxRows (rows read,
57
- * 5,000,000), id (the file ID). presorted: the rows come in the report's order (its sort, its groups together), so
58
- * they are not sorted, only checked as they pass (a row out of order stops the export). spill: where a sort's runs go
59
- * (Node: the system temp folder, 2 GB at most; false sorts in memory); in a browser a sort is in memory.
59
+ * Report definition → .docx written to a sink as its pages are laid out (exportPdfStream's sink, timeoutMs, signal,
60
+ * window, maxRows, sqlRows and spill): a flowing table report is never held whole; any other report is rendered whole
61
+ * and written in one piece (the result's why says why).
62
+ * @param {any} def @param {RenderOptions & { fonts?: FontStore, title?: string, lang?: string, figures?: boolean, timeoutMs?: number, signal?: AbortSignal,
63
+ * window?: number, maxRows?: number, spill?: false | { dir?: string, maxMB?: number } }} options
64
+ * @param {{ write(bytes: Uint8Array): any, once?: (ev: string, cb: () => void) => any }} sink not closed
65
+ * @returns {Promise<{ streamed: boolean, pages?: number, warnings: string[], why?: string }>}
60
66
  */
67
+ export function exportDocxStream(def: any, options: RenderOptions & {
68
+ fonts?: FontStore;
69
+ title?: string;
70
+ lang?: string;
71
+ figures?: boolean;
72
+ timeoutMs?: number;
73
+ signal?: AbortSignal;
74
+ window?: number;
75
+ maxRows?: number;
76
+ spill?: false | {
77
+ dir?: string;
78
+ maxMB?: number;
79
+ };
80
+ }, sink: {
81
+ write(bytes: Uint8Array): any;
82
+ once?: (ev: string, cb: () => void) => any;
83
+ }): Promise<{
84
+ streamed: boolean;
85
+ pages?: number;
86
+ warnings: string[];
87
+ why?: string;
88
+ }>;
61
89
  /**
62
- * Report definition → PDF written to a sink as it is made, for reports too large to hold as a page model: a flowing
63
- * table (and its groups) is laid out a window of rows at a time and each page leaves memory once written. Other
64
- * reports are rendered whole and written by exportPdf in one piece (the result says why). sink: a Node Writable
65
- * (fs.createWriteStream) or anything with write(bytes) that may return a promise (back-pressure); it is not closed.
66
- * @param {any} def the report definition
67
- * @param {PdfStreamOptions} options
68
- * @param {{ write(bytes: Uint8Array): any, once?: (ev: string, cb: () => void) => any }} sink
69
- * @returns {Promise<{ streamed: boolean, pages: number, warnings: string[], why?: string }>}
90
+ * Report definition → .xlsx written to a sink row by row (exportPdfStream's sink, timeoutMs, signal, maxRows, sqlRows
91
+ * and spill); any other report is rendered whole and written in one piece (the result's why says why).
92
+ * @param {any} def @param {RenderOptions & { fonts?: FontStore, title?: string, currency?: string, formulas?: boolean, timeoutMs?: number, signal?: AbortSignal,
93
+ * maxRows?: number, spill?: false | { dir?: string, maxMB?: number } }} options
94
+ * @param {{ write(bytes: Uint8Array): any, once?: (ev: string, cb: () => void) => any }} sink not closed
95
+ * @returns {Promise<{ streamed: boolean, rows?: number, warnings: string[], why?: string }>}
70
96
  */
71
- export function exportPdfStream(def: any, options: PdfStreamOptions, sink: {
97
+ export function exportXlsxStream(def: any, options: RenderOptions & {
98
+ fonts?: FontStore;
99
+ title?: string;
100
+ currency?: string;
101
+ formulas?: boolean;
102
+ timeoutMs?: number;
103
+ signal?: AbortSignal;
104
+ maxRows?: number;
105
+ spill?: false | {
106
+ dir?: string;
107
+ maxMB?: number;
108
+ };
109
+ }, sink: {
72
110
  write(bytes: Uint8Array): any;
73
111
  once?: (ev: string, cb: () => void) => any;
74
112
  }): Promise<{
75
113
  streamed: boolean;
76
- pages: number;
114
+ rows?: number;
77
115
  warnings: string[];
78
116
  why?: string;
79
117
  }>;
@@ -97,12 +135,16 @@ export type PdfOptions = Omit<NonNullable<Parameters<typeof exportPdfFull>[2]>,
97
135
  [k: string]: unknown;
98
136
  };
99
137
  };
138
+ export type RenderOptions = import("../../src/engine/index.js").RenderOptions;
100
139
  /**
101
140
  * render()'s options and exportPdf's, plus: window (rows laid out at once, 1000), parallel (Node: worker threads
102
141
  * that lay out and paint pages, opt-in; the file is the same, each thread costs memory), maxRows (rows read,
103
142
  * 5,000,000), id (the file ID). presorted: the rows come in the report's order (its sort, its groups together), so
104
143
  * they are not sorted, only checked as they pass (a row out of order stops the export). spill: where a sort's runs go
105
144
  * (Node: the system temp folder, 2 GB at most; false sorts in memory); in a browser a sort is in memory.
145
+ * timeoutMs: the whole export's time limit, waits for the sink included (504, code 'ETIMEDOUT'). signal: an
146
+ * AbortSignal that stops the export at its next wait (rejects with the signal's reason, an AbortError); sqlRows gets
147
+ * it as its third argument ({ signal }) so a database cursor can cancel its query.
106
148
  */
107
149
  export type PdfStreamOptions = RenderOptions & Omit<PdfOptions, "fonts"> & {
108
150
  fonts?: FontStore;
@@ -115,13 +157,13 @@ export type PdfStreamOptions = RenderOptions & Omit<PdfOptions, "fonts"> & {
115
157
  maxRows?: number;
116
158
  id?: string;
117
159
  presorted?: boolean;
160
+ signal?: AbortSignal;
118
161
  spill?: false | {
119
162
  dir?: string;
120
163
  maxMB?: number;
121
164
  };
122
165
  sorter?: (cmp: (a: any, b: any) => number) => any;
123
166
  };
124
- export type RenderOptions = import("../../src/engine/index.js").RenderOptions;
125
167
  import { FontStore } from '../../src/engine/index.js';
126
168
  import { exportXlsx as exportXlsxFull } from '../../src/exporters/xlsx.js';
127
169
  import { exportDocx as exportDocxFull } from '../../src/exporters/docx.js';
@@ -134,6 +176,6 @@ import { paintEnv } from '../../src/exporters/pdfstream.js';
134
176
  import { latin1 } from '../../src/exporters/pdfpaint.js';
135
177
  import { exportPdf as exportPdfFull } from '../../src/exporters/pdf.js';
136
178
  export { fontFile, createSubsetter };
137
- export { render, FontStore, ITEMS, parameterOptions, resolveParameters } from "../../src/engine/index.js";
179
+ export { FontStore, ITEMS, parameterOptions, resolveParameters } from "../../src/engine/index.js";
138
180
  export { blankReport, blankDashboard } from "../../src/engine/schema/template.js";
139
181
  export { tableFor, matrixFor } from "../../src/designer/tableGen.js";
@@ -13,13 +13,26 @@ export function jsonPathRows(obj: any, path: any): {
13
13
  * A raw value as the field's type. Numbers in text are read the locale's way ("12,5" and "1.234,56" in de-DE, "1,234.5"
14
14
  * and "(1,234)" in en-US); dates without a zone are the wall clock of the report's zone. A value that does not read
15
15
  * is null, and opt.bad hears of it (the data set warns once).
16
- * @param {any} v @param {string} type @param {{ locale?: string, timeZone?: string, bad?: (v: any) => void }} [opt]
16
+ * @param {any} v @param {string} type @param {{ locale?: string, timeZone?: string, bad?: (v: any) => void, empty?: () => void }} [opt]
17
17
  */
18
18
  export function coerce(v: any, type: string, opt?: {
19
19
  locale?: string;
20
20
  timeZone?: string;
21
21
  bad?: (v: any) => void;
22
+ empty?: () => void;
22
23
  }): any;
24
+ /**
25
+ * Counts, per field, the values that do not read as the field's type (and the empty ones), for one warning per field.
26
+ * Shared by the whole render (buildDataSets) and the streaming export, which both type rows with coerce().
27
+ * @param {string} dsName @param {(msg: string) => void} warn
28
+ */
29
+ export function badValues(dsName: string, warn: (msg: string) => void): {
30
+ /** the row being typed, 1-based */
31
+ at: (n: number) => void;
32
+ /** coerce() options for one field, on top of base */
33
+ opt: (fd: any, base: any) => any;
34
+ done: () => void;
35
+ };
23
36
  /**
24
37
  * Guess field names and types from sample rows. A string that looks like an ISO date (2024-01-31, 2024-01-31T10:00Z)
25
38
  * becomes a date field; dates: false (a data set's detectDates: false) keeps it text. To keep one field as text,
@@ -92,6 +92,7 @@ export const OVER_TIME: "The report ran past its time limit.";
92
92
  export const ROW_PARENT: WeakMap<object, any>;
93
93
  /** The aggregates a streaming export folds row by row (aggregator). */
94
94
  export const FOLDS: Set<string>;
95
+ export function isAggregate(name: any): boolean;
95
96
  export namespace FUNCTIONS {
96
97
  function format(ctx: any, v: any, f: any): any;
97
98
  function upper(ctx: any, s: any): any;
@@ -70,8 +70,6 @@ export class ReportError extends Error {
70
70
  constructor(msg: string, extra?: object);
71
71
  }
72
72
  export const BAND_ITEMS: 250000;
73
- /** The start of the warning for CJK text without the optional CJK fonts (the viewer opens its warnings for it). */
74
- export const NO_CJK_FONTS: "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm i @reportwright/fonts-cjk, then npx reportwright-fonts-cjk (npm); in the app, npm run fonts:cjk";
75
73
  /**
76
74
  * Options for render(). All optional.
77
75
  */
@@ -90,6 +88,10 @@ export type RenderOptions = {
90
88
  * the fonts; share one across renders (a new one loads them again)
91
89
  */
92
90
  fontStore?: import("./text/fonts.js").FontStore;
91
+ /**
92
+ * the viewer: load only the core face up front; the other bundled faces when the layout first needs one
93
+ */
94
+ lazyFonts?: boolean;
93
95
  /**
94
96
  * loads a font the store does not have
95
97
  */
@@ -174,6 +176,11 @@ export type RenderOptions = {
174
176
  * the zone dates show in, when the report sets none
175
177
  */
176
178
  timeZone?: string;
179
+ /**
180
+ * the engine package's render() warns once (model.warnings, and the console once
181
+ * per process) when a data set has more rows than this, pointing at exportPdfStream (default 100000; 0 or false: no warning)
182
+ */
183
+ largeReportRows?: number | false;
177
184
  /**
178
185
  * internal (subreports): the width the child body is laid out to
179
186
  */
@@ -232,8 +239,9 @@ export type RenderOptions = {
232
239
  } | null)[];
233
240
  };
234
241
  };
242
+ import { NO_CJK_FONTS } from './notices.js';
235
243
  import { FontStore } from './text/fonts.js';
236
244
  import { TextMeasurer } from './text/measure.js';
237
245
  import { ITEMS } from './items/index.js';
238
246
  import { finishText } from './text/measure.js';
239
- export { FontStore, TextMeasurer, ITEMS, finishText };
247
+ export { NO_CJK_FONTS, FontStore, TextMeasurer, ITEMS, finishText };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * pdf-lib and fontkit, loaded on first use rather than at import: evaluating them was most of the engine's import time
3
+ * (about 110 ms and 180 ms on an M2). await loadFontkit() (a font is parsed) or loadPdfLib() (exportPdf) first; the
4
+ * bindings below are set then (ES modules' live bindings: `import { PDFName } from './lazylibs.js'` sees them).
5
+ * Node: require() (both are CommonJS; import() of one costs ~60 ms more, its named exports read by Node's CJS lexer
6
+ * through every re-exported file). Elsewhere (a bundler, a browser): import(), which a bundler may split into a chunk.
7
+ */
8
+ /** @typedef {typeof import('pdf-lib')} PdfLib */
9
+ /** @typedef {import('pdf-lib').PDFDocument} PDFDocument */
10
+ /** @typedef {import('pdf-lib').PDFName} PDFName */
11
+ /** @typedef {import('pdf-lib').PDFString} PDFString */
12
+ /** @typedef {import('pdf-lib').PDFHexString} PDFHexString */
13
+ /** @typedef {import('pdf-lib').PDFRawStream} PDFRawStream */
14
+ /** @typedef {import('pdf-lib').PDFStream} PDFStream */
15
+ /** @typedef {import('pdf-lib').PDFArray} PDFArray */
16
+ /** @typedef {import('pdf-lib').PDFDict} PDFDict */
17
+ /** @typedef {import('pdf-lib').PDFNumber} PDFNumber */
18
+ /** @typedef {import('pdf-lib').PDFBool} PDFBool */
19
+ /** @type {any} @pdf-lib/fontkit */ export let fontkit: any;
20
+ export type PDFDocument = import("pdf-lib").PDFDocument;
21
+ /** @type {PdfLib['PDFDocument']} */ export let PDFDocument: PdfLib["PDFDocument"];
22
+ export type PDFName = import("pdf-lib").PDFName;
23
+ /** @type {PdfLib['PDFName']} */ export let PDFName: PdfLib["PDFName"];
24
+ export type PDFString = import("pdf-lib").PDFString;
25
+ /** @type {PdfLib['PDFString']} */ export let PDFString: PdfLib["PDFString"];
26
+ export type PDFHexString = import("pdf-lib").PDFHexString;
27
+ /** @type {PdfLib['PDFHexString']} */ export let PDFHexString: PdfLib["PDFHexString"];
28
+ export type PDFRawStream = import("pdf-lib").PDFRawStream;
29
+ /** @type {PdfLib['PDFRawStream']} */ export let PDFRawStream: PdfLib["PDFRawStream"];
30
+ export type PDFStream = import("pdf-lib").PDFStream;
31
+ /** @type {PdfLib['PDFStream']} */ export let PDFStream: PdfLib["PDFStream"];
32
+ export type PDFArray = import("pdf-lib").PDFArray;
33
+ /** @type {PdfLib['PDFArray']} */ export let PDFArray: PdfLib["PDFArray"];
34
+ export type PDFDict = import("pdf-lib").PDFDict;
35
+ /** @type {PdfLib['PDFDict']} */ export let PDFDict: PdfLib["PDFDict"];
36
+ export type PDFNumber = import("pdf-lib").PDFNumber;
37
+ /** @type {PdfLib['PDFNumber']} */ export let PDFNumber: PdfLib["PDFNumber"];
38
+ export type PDFBool = import("pdf-lib").PDFBool;
39
+ /** @type {PdfLib['PDFBool']} */ export let PDFBool: PdfLib["PDFBool"];
40
+ /** @type {PdfLib['rgb']} */ export let rgb: PdfLib["rgb"];
41
+ /** @type {PdfLib['pushGraphicsState']} */ export let pushGraphicsState: PdfLib["pushGraphicsState"];
42
+ /** @type {PdfLib['popGraphicsState']} */ export let popGraphicsState: PdfLib["popGraphicsState"];
43
+ /** @type {PdfLib['concatTransformationMatrix']} */ export let concatTransformationMatrix: PdfLib["concatTransformationMatrix"];
44
+ export function loadFontkit(): Promise<void>;
45
+ export function loadPdfLib(): Promise<void>;
46
+ export type PdfLib = typeof import("pdf-lib");
@@ -0,0 +1 @@
1
+ export const NO_CJK_FONTS: "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm i @reportwright/fonts-cjk, then npx reportwright-fonts-cjk (npm); in the app, npm run fonts:cjk";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The warnings for the keys of opts that are not options, each with the nearest option (Levenshtein 2 at most) as
3
+ * "Did you mean". A key close to a security option (encrypt, prepare) throws instead: carrying on would write a file
4
+ * without the password or signature the caller asked for.
5
+ * @param {any} opts @param {Set<string>} [known]
6
+ * @returns {string[]}
7
+ */
8
+ export function checkOptions(opts: any, known?: Set<string>): string[];
9
+ /** every option the three functions (and what they pass on) read */
10
+ export const KNOWN_OPTIONS: Set<string>;
@@ -1210,6 +1210,9 @@ export namespace REPORT_SCHEMA {
1210
1210
  fieldName: {
1211
1211
  "x-ts": string;
1212
1212
  };
1213
+ label: {
1214
+ "x-ts": string;
1215
+ };
1213
1216
  value: {
1214
1217
  "x-ts": string;
1215
1218
  };
@@ -11,11 +11,13 @@ export function validate(def: any): {
11
11
  warnings: any[];
12
12
  exprErrors: any[];
13
13
  checkOnly: any[];
14
+ renderSays?: undefined;
14
15
  } | {
15
16
  errors: any[];
16
17
  warnings: string[];
17
18
  exprErrors: any[];
18
19
  checkOnly: any[];
20
+ renderSays: string[];
19
21
  };
20
22
  export const SCHEMA_ID: "pagewright/report@1";
21
23
  export const SCHEMA_ALIAS: "reportwright/report@1";
@@ -11,45 +11,40 @@ export function memorySorter(cmp: (a: any, b: any) => number): {
11
11
  * @param {any} def0 the report definition
12
12
  * @param {{ format: 'csv'|'xlsx', parameters?: object, timeZone?: string, now?: Date, state?: any, loadReport?: (id: string) => Promise<any>,
13
13
  * reportId?: string, fetch?: typeof fetch, baseUrl?: string, sql?: (src: any, p: any) => Promise<object[]>,
14
- * sqlRows?: (src: any, p: any) => AsyncIterable<object[]>, sources?: Record<string, any>, sorter?: (cmp: (a: any, b: any) => number) => any, presorted?: boolean,
14
+ * sqlRows?: (src: any, p: any, o: { signal?: AbortSignal }) => AsyncIterable<object[]>, signal?: AbortSignal, sources?: Record<string, any>, sorter?: (cmp: (a: any, b: any) => number) => any, presorted?: boolean,
15
15
  * deadline?: number, timeoutMs?: number, maxRows?: number, maxJsonRowBytes?: number, maxJsonWholeBytes?: number }} opts
16
16
  * @returns {Promise<{ why: string } | { why?: undefined, name: string, columns: number[], title: string, timeZone: string|null, locale: string|null, currency: string|null, warnings: string[], rows: () => AsyncGenerator<any>, records: () => AsyncGenerator<any>, grouped: () => AsyncGenerator<any>, fieldList: () => Promise<any[]>, stats: { rows: number } }>}
17
17
  */
18
- export function planStream(def0: any, opts: {
19
- format: "csv" | "xlsx";
20
- parameters?: object;
21
- timeZone?: string;
22
- now?: Date;
23
- state?: any;
24
- loadReport?: (id: string) => Promise<any>;
25
- reportId?: string;
26
- fetch?: typeof fetch;
27
- baseUrl?: string;
28
- sql?: (src: any, p: any) => Promise<object[]>;
29
- sqlRows?: (src: any, p: any) => AsyncIterable<object[]>;
30
- sources?: Record<string, any>;
31
- sorter?: (cmp: (a: any, b: any) => number) => any;
32
- presorted?: boolean;
33
- deadline?: number;
34
- timeoutMs?: number;
35
- maxRows?: number;
36
- maxJsonRowBytes?: number;
37
- maxJsonWholeBytes?: number;
38
- }): Promise<{
39
- why: string;
18
+ /**
19
+ * The batches of a sqlRows source: an async or sync iterable of arrays of rows, each checked (a single row is a common slip).
20
+ * @param {Function} sqlRows @param {any} src @param {any} p @param {AbortSignal} [signal]
21
+ */
22
+ export function sqlBatches(sqlRows: Function, src: any, p: any, signal?: AbortSignal): AsyncGenerator<any[], void, unknown>;
23
+ export function planStream(def0: any, opts: any): Promise<{
24
+ why: any;
40
25
  } | {
41
- why?: undefined;
42
- name: string;
43
- columns: number[];
44
- title: string;
45
- timeZone: string | null;
46
- locale: string | null;
47
- currency: string | null;
48
- warnings: string[];
49
- rows: () => AsyncGenerator<any>;
50
- records: () => AsyncGenerator<any>;
51
- grouped: () => AsyncGenerator<any>;
52
- fieldList: () => Promise<any[]>;
26
+ name: any;
27
+ columns: any;
28
+ title: any;
29
+ timeZone: any;
30
+ locale: any;
31
+ currency: any;
32
+ warnings: any[];
33
+ rows: () => AsyncGenerator<{
34
+ kind: any;
35
+ level: any;
36
+ cells: {
37
+ col: number;
38
+ value: any;
39
+ format: any;
40
+ bold: boolean;
41
+ span: number;
42
+ colIndex: number;
43
+ }[];
44
+ }, void, unknown>;
45
+ records: (grouped?: boolean) => AsyncGenerator<any, void, unknown>;
46
+ grouped: () => AsyncGenerator<any, void, unknown>;
47
+ fieldList: () => Promise<any>;
53
48
  stats: {
54
49
  rows: number;
55
50
  };
@@ -11,18 +11,43 @@ export function setCustomFonts(list: {
11
11
  weight?: number;
12
12
  style?: string;
13
13
  }[]): void;
14
+ /**
15
+ * The family a fontFamily value names: a registered family (any case), else the first of a CSS list that is one, else a
16
+ * generic name's family. Anything else is unknown: Inter, or JetBrains Mono for a monospace name, and `unknown` is the
17
+ * name the render warns about.
18
+ * @param {string} name @returns {{ family: string, unknown?: string }}
19
+ */
20
+ export function familyOf(name: string): {
21
+ family: string;
22
+ unknown?: string;
23
+ };
14
24
  /**
15
25
  * The face for a family, weight and style, matched as CSS does: style first (italic faces when asked
16
- * and there are some), then the nearest weight (CSS Fonts 4, §5.2). Unknown families use Inter.
17
- * @param {string} [family] @param {string|number} [weight] @param {string} [style]
26
+ * and there are some), then the nearest weight (CSS Fonts 4, §5.2). Unknown families use Inter (JetBrains Mono for a
27
+ * monospace name), and their names go into `unknown`, for the render's warning (N39).
28
+ * @param {string} [family] @param {string|number} [weight] @param {string} [style] @param {Set<string>} [unknown]
18
29
  */
19
- export function resolveFontKey(family?: string, weight?: string | number, style?: string): string;
30
+ export function resolveFontKey(family?: string, weight?: string | number, style?: string, unknown?: Set<string>): string;
31
+ /**
32
+ * The embedded face that stands in for a standard font where one must be embedded (PDF/A, PDF/UA, form fields) and for
33
+ * the characters its encoding lacks: JetBrains Mono for Courier, else Inter, in the same weight and style. No
34
+ * metric-compatible face (Liberation, say) ships, so its widths differ. Any other key is returned as it is.
35
+ * @param {string} key
36
+ */
37
+ export function embeddedFace(key: string): string;
38
+ /** The name Word, Excel and PowerPoint know for a font key's family (the standard fonts by their Windows equivalents). */
39
+ export function officeFamily(key: any): any;
20
40
  /** CSS description of a font key, for SVG/HTML output. */
21
41
  export function fontCss(key: any): {
22
42
  family: string;
23
43
  weight: number;
24
44
  style: string;
25
45
  };
46
+ /**
47
+ * The font keys a page model draws with (text runs, form fields, figures), for an exporter whose store render() did
48
+ * not fill (N40): load them first. @param {any} model @returns {string[]}
49
+ */
50
+ export function modelFontKeys(model: any): string[];
26
51
  /** The bundled fallback faces for `key` (one per script), for CSS font-family lists. */
27
52
  export function fallbackKeys(key: any): string[];
28
53
  /**
@@ -45,10 +70,12 @@ export function fallbackKey(cp: any, key: any): string;
45
70
  * The faces a report definition names before layout: every object with fontFamily, fontWeight or fontStyle (styles,
46
71
  * named styles, item and rich-text styles, table and group styles), and the bold face a chart's titles use. Loaded up
47
72
  * front, so a report that uses bold or italic lays out in one pass. Data (dataSources, inline data) is not read.
48
- * @param {any} def @returns {string[]} face keys
73
+ * Unknown family names go into `unknown` (the render warns about them).
74
+ * @param {any} def @param {Set<string>} [unknown] @returns {string[]} face keys
49
75
  */
50
- export function fontsForDef(def: any): string[];
76
+ export function fontsForDef(def: any, unknown?: Set<string>): string[];
51
77
  export function fontsFor(texts: any): any[];
78
+ export { isStandard };
52
79
  /** The optional families (scripts/fetch-fonts-cjk.mjs). */
53
80
  export const OPTIONAL_KEYS: string[];
54
81
  /** family → its faces: the bundled families, then the fonts uploaded to the server (setCustomFonts). @type {Record<string, Face[]>} */
@@ -63,6 +90,7 @@ export const CORE_FONT_KEY: "Inter-Regular";
63
90
  export const PRELOAD_KEYS: string[];
64
91
  /** The HarfBuzz shaper (WASM). FontStore loads it like a font, from /fonts/harfbuzz.wasm, once text needs shaping. */
65
92
  export const SHAPER: "harfbuzz";
93
+ export function standardCss(key: string): string | null;
66
94
  export function allFontKeys(): string[];
67
95
  /**
68
96
  * Loads font files once and keeps the parsed fontkit objects; SHAPER loads the HarfBuzz shaper.
@@ -93,4 +121,6 @@ export type Face = {
93
121
  weight: number;
94
122
  style: "normal" | "italic";
95
123
  file: string;
124
+ standard?: boolean;
96
125
  };
126
+ import { isStandard } from './standard.js';
@@ -45,6 +45,10 @@ export class TextMeasurer {
45
45
  missing: Set<string>;
46
46
  /** characters no font could draw @type {Set<string>} */
47
47
  noGlyph: Set<string>;
48
+ /** font names the text asked for that are not available (resolveFontKey): the render warns @type {Set<string>} */
49
+ unknownFonts: Set<string>;
50
+ /** standard-font key → the characters its encoding lacks, drawn in embeddedFace(key): the render warns @type {Map<string, Set<string>>} */
51
+ substituted: Map<string, Set<string>>;
48
52
  /**
49
53
  * The key to measure with: `key` when loaded; else (not loaded yet, or failed) the bundled face of the same weight
50
54
  * and style when that is loaded, else the core regular face. A key not yet loaded is asked for in the next pass.
@@ -102,12 +106,13 @@ export class TextMeasurer {
102
106
  * character), exactly as before. Others also break between CJK characters (kinsoku applied), never
103
107
  * inside a grapheme cluster (a conjunct, a letter and its marks), and remember their direction.
104
108
  * @param {string} text @param {string} key @param {number} size @param {number} maxWidth
105
- * @returns {{text: string, width: number, dir?: 'ltr'|'rtl'}[]}
109
+ * @returns {{text: string, width: number, dir?: 'ltr'|'rtl', end?: boolean}[]}
106
110
  */
107
111
  wrap(text: string, key: string, size: number, maxWidth: number): {
108
112
  text: string;
109
113
  width: number;
110
114
  dir?: "ltr" | "rtl";
115
+ end?: boolean;
111
116
  }[];
112
117
  /** @param {string} para @param {string} key @param {number} size @param {number} maxWidth @param {'ltr'|'rtl'} dir @param {number} sp @param {any[]} out */
113
118
  wrapComplex(para: string, key: string, size: number, maxWidth: number, dir: "ltr" | "rtl", sp: number, out: any[]): void;