@lpdfio/lpdf 0.30.0 → 0.31.0

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.
package/dist/engine.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PdfEngine = exports.LpdfRenderError = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const node_fs_1 = require("node:fs");
6
+ const _shared_1 = require("./_shared");
6
7
  // require() path is relative to the compiled output at dist/engine.js.
7
8
  // In the published package, wasm-pack artifacts live in dist/wasm/.
8
9
  // In the monorepo, `npm run build` copies them there from <root>/dist/node/.
@@ -29,24 +30,18 @@ class LpdfRenderError extends Error {
29
30
  }
30
31
  }
31
32
  exports.LpdfRenderError = LpdfRenderError;
32
- /**
33
- * Hands the engine the encryption options. AES-256 needs random bytes, which the engine does not
34
- * make: they come from Node's secure generator, 32 bytes for each render.
35
- */
36
- function configureEncryption(engine, options) {
37
- const algorithm = options.algorithm ?? 'aes-256';
38
- const entropy = algorithm === 'aes-256' ? new Uint8Array((0, node_crypto_1.randomBytes)(32)) : new Uint8Array(0);
39
- const permsJson = JSON.stringify(options.permissions ?? {});
40
- engine.set_encryption(options.userPassword, options.ownerPassword, permsJson, algorithm, entropy);
33
+ /** Node's secure generator, for the entropy of AES-256. */
34
+ function secureRandomBytes(count) {
35
+ return new Uint8Array((0, node_crypto_1.randomBytes)(count));
41
36
  }
42
37
  class PdfEngine {
43
38
  constructor() {
44
39
  this._licenseKey = '';
45
40
  this._fonts = new Map();
46
41
  this._images = new Map();
42
+ this._files = new Map();
47
43
  this._markdownStyles = new Map();
48
44
  this._disposed = false;
49
- this._encrypt = null;
50
45
  }
51
46
  /**
52
47
  * Set the license key. Returns `this` for chaining.
@@ -90,30 +85,21 @@ class PdfEngine {
90
85
  return this;
91
86
  }
92
87
  /**
93
- * Register the text of a file of Markdown rules, the root of which is a `<markdown-styles>`, for the `src` that
94
- * names it, as `<markdown-styles src="…">` does. Call before `render`. Returns `this` for chaining.
88
+ * Register the bytes of a file the document carries, for the `<file>` of its `<attachments>` whose `ref`, or with
89
+ * no ref whose name, is `name`. The PDF carries them as they are. Call before `render`. Returns `this` for chaining.
95
90
  */
96
- loadMarkdownStyles(src, text) {
91
+ loadFile(name, bytes) {
97
92
  this._throwIfDisposed();
98
- this._markdownStyles.set(src, text);
93
+ this._files.set(name, bytes);
99
94
  return this;
100
95
  }
101
96
  /**
102
- * Configure encryption for all subsequent `render` calls: AES-256 unless
103
- * `options.algorithm` says `'rc4-128'`. Returns `this` for chaining.
104
- */
105
- setEncryption(options) {
106
- this._throwIfDisposed();
107
- this._encrypt = options;
108
- return this;
109
- }
110
- /**
111
- * Remove any previously configured encryption.
112
- * Returns `this` for chaining.
97
+ * Register the text of a file of Markdown rules, the root of which is a `<markdown-styles>`, for the `src` that
98
+ * names it, as `<markdown-styles src="…">` does. Call before `render`. Returns `this` for chaining.
113
99
  */
114
- clearEncryption() {
100
+ loadMarkdownStyles(src, text) {
115
101
  this._throwIfDisposed();
116
- this._encrypt = null;
102
+ this._markdownStyles.set(src, text);
117
103
  return this;
118
104
  }
119
105
  /**
@@ -142,6 +128,26 @@ class PdfEngine {
142
128
  engine.load_markdown_styles(src, text);
143
129
  }
144
130
  }
131
+ /**
132
+ * Hand the engine the files the document carries: the ones loaded here, and the ones read from the `src` of a file
133
+ * that wasn't. A `src` that can't be read is an error that names the file and the path.
134
+ */
135
+ _loadFiles(engine, srcs) {
136
+ const files = new Map(this._files);
137
+ for (const [key, src] of srcs) {
138
+ if (files.has(key))
139
+ continue;
140
+ try {
141
+ files.set(key, (0, node_fs_1.readFileSync)(src));
142
+ }
143
+ catch (e) {
144
+ throw new LpdfRenderError(`file '${key}' of <attachments>: cannot read its src '${src}': ${e instanceof Error ? e.message : String(e)}`);
145
+ }
146
+ }
147
+ for (const [key, bytes] of files) {
148
+ engine.load_file(key, bytes);
149
+ }
150
+ }
145
151
  _throwIfDisposed() {
146
152
  if (this._disposed)
147
153
  throw new Error('PdfEngine has been disposed.');
@@ -176,6 +182,7 @@ class PdfEngine {
176
182
  engine.load_image(name, bytes);
177
183
  }
178
184
  this._loadMarkdownStyles(engine, markdownStylesSrcs(xml));
185
+ this._loadFiles(engine, extractAssetSrcs(xml, 'file'));
179
186
  // Auto-load images declared via <image src="…"> that weren't pre-loaded.
180
187
  for (const [key, src] of extractAssetSrcs(xml, 'image')) {
181
188
  if (!this._images.has(key)) {
@@ -185,11 +192,8 @@ class PdfEngine {
185
192
  catch { /* skip unresolvable image */ }
186
193
  }
187
194
  }
188
- if (this._encrypt) {
189
- configureEncryption(engine, this._encrypt);
190
- }
191
195
  const dataJson = callOptions.data != null ? JSON.stringify(callOptions.data) : null;
192
- pdf = engine.render_pdf(xml, dataJson);
196
+ pdf = engine.render_pdf(xml, dataJson, ...(0, _shared_1.encryptionArguments)(callOptions, secureRandomBytes));
193
197
  }
194
198
  else {
195
199
  // JSON (Kit tree) path — pass JSON directly to render_tree_pdf. Fonts and images the
@@ -220,14 +224,14 @@ class PdfEngine {
220
224
  engine.load_image(name, bytes);
221
225
  }
222
226
  this._loadMarkdownStyles(engine, markdownStylesSrcsOfDocument(input));
223
- if (this._encrypt) {
224
- configureEncryption(engine, this._encrypt);
225
- }
226
- pdf = engine.render_tree_pdf(json);
227
+ this._loadFiles(engine, fileSrcsOfDocument(input));
228
+ pdf = engine.render_tree_pdf(json, ...(0, _shared_1.encryptionArguments)(callOptions, secureRandomBytes));
227
229
  }
228
230
  }
229
231
  catch (e) {
230
232
  engine.free();
233
+ if (e instanceof LpdfRenderError)
234
+ throw e;
231
235
  const msg = e instanceof Error ? e.message : String(e);
232
236
  throw new LpdfRenderError(msg);
233
237
  }
@@ -237,16 +241,40 @@ class PdfEngine {
237
241
  }
238
242
  exports.PdfEngine = PdfEngine;
239
243
  // ── Helpers ───────────────────────────────────────────────────────────────────
240
- /** Extract `ref??name → src` pairs from `<font>` or `<image>` tags in XML. */
244
+ // Comments and CDATA sections, whose text is not markup: a <file> shown in a Markdown block is text, not a file.
245
+ const XML_NOT_MARKUP_RE = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>/g;
246
+ const XML_ATTRIBUTE_RE = /([\w:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
247
+ const XML_ENTITY_RE = /&(?:(amp|lt|gt|quot|apos)|#(\d+)|#x([0-9a-fA-F]+));/g;
248
+ const XML_ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'" };
249
+ /** An attribute value as the XML means it, with its entities and character references decoded. */
250
+ function decodeXml(value) {
251
+ return value.replace(XML_ENTITY_RE, (_, name, decimal, hex) =>
252
+ // XML_ENTITY_RE matches exactly one of its three groups, so without a name or a decimal there is a hex.
253
+ name ? XML_ENTITIES[name] : String.fromCodePoint(parseInt(decimal ?? hex, decimal ? 10 : 16)));
254
+ }
255
+ /**
256
+ * The attributes of each `<tag>` element in XML, read as the engine reads them: comments and CDATA sections are left
257
+ * out, a quoted value may hold a `>`, either quote may be used, and the values are decoded.
258
+ */
259
+ function xmlElements(xml, tag) {
260
+ const elements = [];
261
+ // Attribute values may contain '>', so quoted runs are matched whole.
262
+ const re = new RegExp(`<${tag}\\b((?:[^>"']|"[^"]*"|'[^']*')*)>`, 'g');
263
+ for (const match of xml.replace(XML_NOT_MARKUP_RE, '').matchAll(re)) {
264
+ const attributes = new Map();
265
+ for (const attribute of match[1].matchAll(XML_ATTRIBUTE_RE)) {
266
+ attributes.set(attribute[1], decodeXml(attribute[2] ?? attribute[3]));
267
+ }
268
+ elements.push(attributes);
269
+ }
270
+ return elements;
271
+ }
272
+ /** The `ref ?? name → src` pairs of the `<font>`, `<image>` or `<file>` elements in XML. */
241
273
  function extractAssetSrcs(xml, tag) {
242
274
  const result = new Map();
243
- const re = tag === 'font' ? /<font\s[^>]*>/g : /<image\s[^>]*>/g;
244
- for (const match of xml.matchAll(re)) {
245
- const t = match[0];
246
- const name = /\bname="([^"]*)"/.exec(t)?.[1];
247
- const ref = /\bref="([^"]*)"/.exec(t)?.[1];
248
- const src = /\bsrc="([^"]*)"/.exec(t)?.[1];
249
- const key = ref ?? name;
275
+ for (const attributes of xmlElements(xml, tag)) {
276
+ const key = attributes.get('ref') ?? attributes.get('name');
277
+ const src = attributes.get('src');
250
278
  if (key && src)
251
279
  result.set(key, src);
252
280
  }
@@ -255,8 +283,8 @@ function extractAssetSrcs(xml, tag) {
255
283
  /** The `src` of each `<markdown-styles>` in XML. */
256
284
  function markdownStylesSrcs(xml) {
257
285
  const srcs = [];
258
- for (const match of xml.matchAll(/<markdown-styles\s[^>]*>/g)) {
259
- const src = /\bsrc="([^"]*)"/.exec(match[0])?.[1];
286
+ for (const attributes of xmlElements(xml, 'markdown-styles')) {
287
+ const src = attributes.get('src');
260
288
  if (src)
261
289
  srcs.push(src);
262
290
  }
@@ -270,13 +298,23 @@ function markdownStylesSrcsOfDocument(document) {
270
298
  if (child.type !== 'markdown')
271
299
  continue;
272
300
  for (const part of child.nodes) {
273
- if (part.type === 'markdown-styles' && part.attrs['src'])
274
- srcs.push(part.attrs['src']);
301
+ const src = part.type === 'markdown-styles' ? part.attrs['src'] : undefined;
302
+ if (typeof src === 'string' && src)
303
+ srcs.push(src);
275
304
  }
276
305
  }
277
306
  }
278
307
  return srcs;
279
308
  }
309
+ /** The `ref ?? name → src` pairs of the files a document carries in `attrs.attachments`. */
310
+ function fileSrcsOfDocument(document) {
311
+ const result = new Map();
312
+ for (const { name, ref, src } of document.attrs['attachments'] ?? []) {
313
+ if (src)
314
+ result.set(ref ?? name, src);
315
+ }
316
+ return result;
317
+ }
280
318
  /** The `ref ?? name → src` pairs of the fonts or images a document declares in `attrs.assets`. */
281
319
  function assetSrcsOfDocument(document, kind) {
282
320
  const result = new Map();
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  export { PdfEngine, LpdfRenderError } from './engine';
2
- export type { EncryptAlgorithm, EncryptPermissions, EncryptOptions } from './engine';
3
2
  export type { RenderOptions, EngineOptions } from './_shared';
4
- export { FieldType, Pin, Orientation, SectionType, FootnoteCounter, BuiltinFont, PageScope } from './constants';
5
- export type { PdfDocument, LpdfSectionNode, LpdfSectionChild, LpdfLayoutBlock, DocumentTokens, GradientToken, GradientStop, DocumentAssets, DocumentMeta, SectionAttr, DocumentAttr, } from './kit';
6
- export type { ColumnAttr, RowAttr, GridAttr, ListAttr, LiAttr, FrameAttr, LinkAttr, TableAttr, TheadAttr, TrAttr, TdAttr, TextAttr, CodeAttr, SpanAttr, CodeSpanAttr, DividerAttr, ImgAttr, ShapeAttr, BarcodeAttr, RegionAttr, FieldAttr, BookmarkAttr, LayerAttr, RectAttr, CircleAttr, EllipseAttr, LineAttr, PathAttr, TextAtAttr, ImgAtAttr, BarcodeAtAttr, FontAttr, ImageAttr, LayoutAttr, MarkdownAttr, MarkdownStylesAttr, MarkdownH1Attr, MarkdownH2Attr, MarkdownH3Attr, MarkdownH4Attr, MarkdownH5Attr, MarkdownH6Attr, MarkdownPAttr, MarkdownCodeAttr, MarkdownQuoteAttr, MarkdownLiAttr, MarkdownAAttr, MarkdownStrongAttr, MarkdownEmAttr, MarkdownDelAttr, MarkdownTableAttr, MarkdownThAttr, MarkdownTdAttr, } from './attrs';
3
+ export { FieldType, Pin, Orientation, SectionType, FootnoteCounter, Cipher, Permission, FileRelationship, BuiltinFont, PageScope, } from './constants';
4
+ export type { PdfDocument, LpdfSectionNode, LpdfSectionChild, LpdfLayoutBlock, DocumentTokens, TokenSlot, TokenRow, DocumentAssets, DocumentMeta, DocumentOutput, SectionAttr, DocumentAttr, } from './kit';
5
+ export type { ColumnAttr, RowAttr, GridAttr, ListAttr, LiAttr, FrameAttr, LinkAttr, TableAttr, TheadAttr, TrAttr, TdAttr, TextAttr, CodeAttr, SpanAttr, CodeSpanAttr, DividerAttr, ImgAttr, ShapeAttr, BarcodeAttr, RegionAttr, FieldAttr, BookmarkAttr, LayerAttr, RectAttr, CircleAttr, EllipseAttr, LineAttr, PathAttr, TextAtAttr, ImgAtAttr, BarcodeAtAttr, FontAttr, ImageAttr, FileAttr, LayoutAttr, GradientToken, GradientStop, MarkdownAttr, MarkdownStylesAttr, MarkdownH1Attr, MarkdownH2Attr, MarkdownH3Attr, MarkdownH4Attr, MarkdownH5Attr, MarkdownH6Attr, MarkdownPAttr, MarkdownCodeAttr, MarkdownQuoteAttr, MarkdownLiAttr, MarkdownAAttr, MarkdownStrongAttr, MarkdownEmAttr, MarkdownDelAttr, MarkdownTableAttr, MarkdownThAttr, MarkdownTdAttr, } from './attrs';
7
6
  export type { LpdfNode, LpdfContainerNode, LpdfListNode, LpdfLiNode, LpdfTextNode, LpdfCodeNode, LpdfSpanNode, LpdfCodeSpanNode, LpdfFnNode, LpdfDividerNode, LpdfImgNode, LpdfShapeNode, LpdfBarcodeNode, LpdfTableNode, LpdfTheadNode, LpdfTrNode, LpdfTdNode, LpdfRegionNode, LpdfFieldNode, LpdfBookmarkNode, } from './layout';
8
7
  export { LMarkdown } from './markdown';
9
8
  export type { LpdfMarkdownNode, LpdfMarkdownStylesNode, LpdfMarkdownContentNode, LpdfMarkdownRuleNode, } from './markdown';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NoAttr = exports.L = exports.CanvasTransform = exports.LMarkdown = exports.PageScope = exports.BuiltinFont = exports.FootnoteCounter = exports.SectionType = exports.Orientation = exports.Pin = exports.FieldType = exports.LpdfRenderError = exports.PdfEngine = void 0;
3
+ exports.NoAttr = exports.L = exports.CanvasTransform = exports.LMarkdown = exports.PageScope = exports.BuiltinFont = exports.FileRelationship = exports.Permission = exports.Cipher = exports.FootnoteCounter = exports.SectionType = exports.Orientation = exports.Pin = exports.FieldType = exports.LpdfRenderError = exports.PdfEngine = void 0;
4
4
  // ── Engine ────────────────────────────────────────────────────────────────────
5
5
  var engine_1 = require("./engine");
6
6
  Object.defineProperty(exports, "PdfEngine", { enumerable: true, get: function () { return engine_1.PdfEngine; } });
@@ -12,6 +12,9 @@ Object.defineProperty(exports, "Pin", { enumerable: true, get: function () { ret
12
12
  Object.defineProperty(exports, "Orientation", { enumerable: true, get: function () { return constants_1.Orientation; } });
13
13
  Object.defineProperty(exports, "SectionType", { enumerable: true, get: function () { return constants_1.SectionType; } });
14
14
  Object.defineProperty(exports, "FootnoteCounter", { enumerable: true, get: function () { return constants_1.FootnoteCounter; } });
15
+ Object.defineProperty(exports, "Cipher", { enumerable: true, get: function () { return constants_1.Cipher; } });
16
+ Object.defineProperty(exports, "Permission", { enumerable: true, get: function () { return constants_1.Permission; } });
17
+ Object.defineProperty(exports, "FileRelationship", { enumerable: true, get: function () { return constants_1.FileRelationship; } });
15
18
  Object.defineProperty(exports, "BuiltinFont", { enumerable: true, get: function () { return constants_1.BuiltinFont; } });
16
19
  Object.defineProperty(exports, "PageScope", { enumerable: true, get: function () { return constants_1.PageScope; } });
17
20
  // ── Markdown ──────────────────────────────────────────────────────────────────
package/dist/kit.d.ts CHANGED
@@ -22,24 +22,12 @@
22
22
  import type { LpdfBookmarkNode, LpdfNode } from './layout';
23
23
  import type { LpdfCanvasLayerNode } from './canvas';
24
24
  import type { LpdfMarkdownNode } from './markdown';
25
- import type { FontAttr, ImageAttr, LayoutAttr } from './attrs';
26
- /** One color of a gradient: `color` is hex or a color token, at an `offset` from `'0%'` to `'100%'`. */
27
- export interface GradientStop {
28
- offset: string;
29
- color: string;
30
- }
31
- /**
32
- * A gradient of the tokens, which a background or a fill uses as `gradient:name`. Give `from` and `to` for two
33
- * colors, or `stops` for more. `angle` is the direction of a linear gradient in degrees: 0 runs top to bottom
34
- * and 90 left to right. A radial gradient runs from the center of the box out to its farthest corner.
35
- */
36
- export interface GradientToken {
37
- type: 'linear' | 'radial';
38
- from?: string;
39
- to?: string;
40
- angle?: number | string;
41
- stops?: GradientStop[];
42
- }
25
+ import type { FileAttr, FontAttr, GradientToken, ImageAttr, LayoutAttr } from './attrs';
26
+ import { type AttrValue } from './_shared';
27
+ /** A slot of a token row, from the smallest to the largest. */
28
+ export type TokenSlot = 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
29
+ /** A token row: a value for each slot it sets. */
30
+ export type TokenRow<T> = Partial<Record<TokenSlot, T>>;
43
31
  export interface DocumentTokens {
44
32
  colors?: Record<string, string>;
45
33
  gradients?: Record<string, GradientToken>;
@@ -49,15 +37,16 @@ export interface DocumentTokens {
49
37
  * starting with a letter, and is neither a slot nor `fill` or `full`.
50
38
  */
51
39
  aliases?: Record<string, string>;
52
- space?: Record<string, string>;
53
- grid?: Record<string, string>;
54
- border?: Record<string, string>;
55
- radius?: Record<string, string>;
56
- width?: Record<string, string>;
40
+ space?: TokenRow<string>;
41
+ grid?: TokenRow<string>;
42
+ border?: TokenRow<string>;
43
+ radius?: TokenRow<string>;
44
+ width?: TokenRow<string>;
57
45
  /** The heights `height` names: `{ xs: '16pt', s: '24pt', m: '32pt', l: '48pt', xl: '64pt', xxl: '96pt' }` by default. */
58
- height?: Record<string, string>;
59
- textSize?: Record<string, string>;
60
- lineHeight?: Record<string, string>;
46
+ height?: TokenRow<string>;
47
+ textSize?: TokenRow<string>;
48
+ /** The factors `lineHeight` names, each a number above 0: `{ xs: 1, s: 1.1, m: 1.2, l: 1.4, xl: 1.6, xxl: 2 }` by default. */
49
+ lineHeight?: TokenRow<number>;
61
50
  }
62
51
  /**
63
52
  * The fonts and images the document declares, as the `assets` element of the XML does. A font or image
@@ -74,12 +63,32 @@ export interface DocumentMeta {
74
63
  keywords?: string;
75
64
  creator?: string;
76
65
  }
66
+ /**
67
+ * How the PDF file is written, as the `output` element of the XML is: whether it is encrypted, and what a reader
68
+ * may do with it, and the standard it meets. The passwords are not here: they come with the render call.
69
+ */
70
+ export interface DocumentOutput {
71
+ /** The cipher the PDF is encrypted with, `aes-256` or `rc4-128` (`Cipher`). Without it the PDF is not encrypted. */
72
+ encrypt?: string;
73
+ /**
74
+ * What a reader may do without the owner password, separated by spaces (`Permission`): `all`, or any of
75
+ * `print`, `modify`, `copy`, `annotate`, `fill-forms`, `accessibility`, `assemble`. A permission comes only from
76
+ * naming it: without `allow`, or with `''`, nothing is allowed, and `all` allows everything. It needs `encrypt`.
77
+ */
78
+ allow?: string;
79
+ /**
80
+ * Writes the PDF as PDF/A, the standard for files kept for the long term: `2b`, or `2u` when its text must also
81
+ * be searchable. The render needs `createdOn`, and every text a font of `assets`. What PDF/A cannot hold is an
82
+ * error that says what to change: a built-in font, a CMYK color, a form field, a barcode's digits, `encrypt`.
83
+ */
84
+ pdfa?: string;
85
+ }
77
86
  export interface SectionAttr {
78
87
  size?: string;
79
88
  orientation?: string;
80
89
  background?: string;
81
90
  title?: string;
82
- debug?: string;
91
+ debug?: boolean;
83
92
  type?: string;
84
93
  tocIndentStep?: string;
85
94
  footnoteCounter?: string;
@@ -92,18 +101,24 @@ export interface DocumentAttr {
92
101
  assets?: DocumentAssets;
93
102
  tokens?: DocumentTokens;
94
103
  meta?: DocumentMeta;
95
- debug?: string;
104
+ output?: DocumentOutput;
105
+ /**
106
+ * Files the PDF carries inside it, as the `attachments` element of the XML does: a reader lists them in its
107
+ * attachments panel. Each file's bytes are read from its `src`, or loaded with `loadFile` under its `ref` or name.
108
+ */
109
+ attachments?: FileAttr[];
110
+ debug?: boolean;
96
111
  footnoteCounter?: string;
97
112
  }
98
113
  /** A layout block — wraps an ordered list of layout nodes. */
99
114
  export interface LpdfLayoutBlock {
100
115
  type: 'layout';
101
- attrs: Record<string, string>;
116
+ attrs: Record<string, AttrValue>;
102
117
  nodes: LpdfNode[];
103
118
  }
104
119
  export interface LpdfSectionNode {
105
120
  type: 'section';
106
- attrs: Record<string, string>;
121
+ attrs: Record<string, AttrValue>;
107
122
  nodes: LpdfSectionChild[];
108
123
  }
109
124
  /**
package/dist/kit.js CHANGED
@@ -35,13 +35,15 @@ function section(attrs, nodes = []) {
35
35
  };
36
36
  }
37
37
  function document(attrs, nodes = []) {
38
- const { assets, tokens, meta, ...restOpts } = attrs ?? {};
38
+ const { assets, tokens, meta, output, attachments, ...restOpts } = attrs ?? {};
39
39
  const attrsObj = {
40
40
  ...(0, _shared_1.buildAttrs)(restOpts),
41
41
  };
42
42
  if (assets !== undefined)
43
43
  attrsObj['assets'] = assets;
44
44
  if (tokens !== undefined) {
45
+ // A factor of the line-height row and a gradient's angle are numbers, which JSON can't hold as NaN or infinity.
46
+ (0, _shared_1.assertFiniteIn)('tokens', tokens);
45
47
  const { textSize, lineHeight, ...rest } = tokens;
46
48
  attrsObj['tokens'] = {
47
49
  ...rest,
@@ -51,6 +53,10 @@ function document(attrs, nodes = []) {
51
53
  }
52
54
  if (meta !== undefined)
53
55
  attrsObj['meta'] = meta;
56
+ if (output !== undefined)
57
+ attrsObj['output'] = output;
58
+ if (attachments !== undefined)
59
+ attrsObj['attachments'] = attachments;
54
60
  return {
55
61
  version: 1,
56
62
  type: 'document',
package/dist/layout.d.ts CHANGED
@@ -1,98 +1,99 @@
1
1
  import type { ColumnAttr, RowAttr, GridAttr, ListAttr, LiAttr, FrameAttr, LinkAttr, TableAttr, TheadAttr, TrAttr, TdAttr, TextAttr, CodeAttr, SpanAttr, CodeSpanAttr, DividerAttr, ImgAttr, ShapeAttr, BarcodeAttr, FieldAttr, RegionAttr, BookmarkAttr } from './attrs';
2
+ import { type AttrValue } from './_shared';
2
3
  export interface LpdfSpanNode {
3
4
  type: 'span';
4
- attrs: Record<string, string>;
5
+ attrs: Record<string, AttrValue>;
5
6
  nodes: string[];
6
7
  }
7
8
  /** A run of code inside a text: its words in the code font on a tint. */
8
9
  export interface LpdfCodeSpanNode {
9
10
  type: 'code-span';
10
- attrs: Record<string, string>;
11
+ attrs: Record<string, AttrValue>;
11
12
  nodes: string[];
12
13
  }
13
14
  /** A footnote: its words go at the bottom of the page where its number lands. */
14
15
  export interface LpdfFnNode {
15
16
  type: 'fn';
16
- attrs: Record<string, string>;
17
+ attrs: Record<string, AttrValue>;
17
18
  nodes: (string | LpdfSpanNode)[];
18
19
  }
19
20
  export interface LpdfContainerNode {
20
21
  type: 'column' | 'row' | 'grid' | 'frame' | 'link';
21
- attrs: Record<string, string>;
22
+ attrs: Record<string, AttrValue>;
22
23
  nodes: LpdfNode[];
23
24
  }
24
25
  /** A list of items, each marked with a bullet or a number the engine gives. */
25
26
  export interface LpdfListNode {
26
27
  type: 'list';
27
- attrs: Record<string, string>;
28
+ attrs: Record<string, AttrValue>;
28
29
  nodes: LpdfLiNode[];
29
30
  }
30
31
  /** One item of a list: what a layout holds, beside the item's marker. */
31
32
  export interface LpdfLiNode {
32
33
  type: 'li';
33
- attrs: Record<string, string>;
34
+ attrs: Record<string, AttrValue>;
34
35
  nodes: LpdfNode[];
35
36
  }
36
37
  export interface LpdfTextNode {
37
38
  type: 'text';
38
- attrs: Record<string, string>;
39
+ attrs: Record<string, AttrValue>;
39
40
  nodes: (string | LpdfSpanNode | LpdfCodeSpanNode | LpdfFnNode)[];
40
41
  }
41
42
  /** Code in a box. Its strings keep their spaces and lines; its spans color parts of it. */
42
43
  export interface LpdfCodeNode {
43
44
  type: 'code';
44
- attrs: Record<string, string>;
45
+ attrs: Record<string, AttrValue>;
45
46
  nodes: (string | LpdfSpanNode)[];
46
47
  }
47
48
  export interface LpdfDividerNode {
48
49
  type: 'divider';
49
- attrs: Record<string, string>;
50
+ attrs: Record<string, AttrValue>;
50
51
  }
51
52
  export interface LpdfImgNode {
52
53
  type: 'img';
53
- attrs: Record<string, string>;
54
+ attrs: Record<string, AttrValue>;
54
55
  }
55
56
  export interface LpdfShapeNode {
56
57
  type: 'shape';
57
- attrs: Record<string, string>;
58
+ attrs: Record<string, AttrValue>;
58
59
  }
59
60
  export interface LpdfBarcodeNode {
60
61
  type: 'barcode';
61
- attrs: Record<string, string>;
62
+ attrs: Record<string, AttrValue>;
62
63
  }
63
64
  export interface LpdfTheadNode {
64
65
  type: 'thead';
65
- attrs: Record<string, string>;
66
+ attrs: Record<string, AttrValue>;
66
67
  nodes: LpdfTdNode[];
67
68
  }
68
69
  export interface LpdfTrNode {
69
70
  type: 'tr';
70
- attrs: Record<string, string>;
71
+ attrs: Record<string, AttrValue>;
71
72
  nodes: LpdfTdNode[];
72
73
  }
73
74
  export interface LpdfTdNode {
74
75
  type: 'td';
75
- attrs: Record<string, string>;
76
+ attrs: Record<string, AttrValue>;
76
77
  nodes: LpdfNode[];
77
78
  }
78
79
  export interface LpdfTableNode {
79
80
  type: 'table';
80
- attrs: Record<string, string>;
81
+ attrs: Record<string, AttrValue>;
81
82
  nodes: (LpdfTheadNode | LpdfTrNode)[];
82
83
  }
83
84
  export interface LpdfRegionNode {
84
85
  type: 'region';
85
- attrs: Record<string, string>;
86
+ attrs: Record<string, AttrValue>;
86
87
  nodes: LpdfNode[];
87
88
  }
88
89
  export interface LpdfFieldNode {
89
90
  type: 'field';
90
- attrs: Record<string, string>;
91
+ attrs: Record<string, AttrValue>;
91
92
  }
92
93
  /** An outline entry and link target that marks the node after it; it draws nothing. */
93
94
  export interface LpdfBookmarkNode {
94
95
  type: 'bookmark';
95
- attrs: Record<string, string>;
96
+ attrs: Record<string, AttrValue>;
96
97
  }
97
98
  export type LpdfNode = LpdfContainerNode | LpdfListNode | LpdfTextNode | LpdfCodeNode | LpdfDividerNode | LpdfTableNode | LpdfImgNode | LpdfShapeNode | LpdfBarcodeNode | LpdfRegionNode | LpdfFieldNode | LpdfBookmarkNode;
98
99
  declare function column(attrs: ColumnAttr | null, nodes?: LpdfNode[]): LpdfContainerNode;
@@ -1,25 +1,26 @@
1
1
  import type { MarkdownAttr, MarkdownStylesAttr, MarkdownH1Attr, MarkdownH2Attr, MarkdownH3Attr, MarkdownH4Attr, MarkdownH5Attr, MarkdownH6Attr, MarkdownPAttr, MarkdownCodeAttr, MarkdownQuoteAttr, MarkdownLiAttr, MarkdownAAttr, MarkdownStrongAttr, MarkdownEmAttr, MarkdownDelAttr, MarkdownTableAttr, MarkdownThAttr, MarkdownTdAttr } from './attrs';
2
+ import { type AttrValue } from './_shared';
2
3
  /** A rule of a markdown's styles: the element it is named after, with the attributes it sets. */
3
4
  export interface LpdfMarkdownRuleNode {
4
5
  type: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'code' | 'quote' | 'li' | 'a' | 'strong' | 'em' | 'del' | 'table' | 'th' | 'td';
5
- attrs: Record<string, string>;
6
+ attrs: Record<string, AttrValue>;
6
7
  }
7
8
  /** The rules that say how the parts of a markdown look, with a file of them in `src` when it has one. */
8
9
  export interface LpdfMarkdownStylesNode {
9
10
  type: 'markdown-styles';
10
- attrs: Record<string, string>;
11
+ attrs: Record<string, AttrValue>;
11
12
  nodes: LpdfMarkdownRuleNode[];
12
13
  }
13
14
  /** The Markdown itself, as strings. */
14
15
  export interface LpdfMarkdownContentNode {
15
16
  type: 'markdown-content';
16
- attrs: Record<string, string>;
17
+ attrs: Record<string, AttrValue>;
17
18
  nodes: string[];
18
19
  }
19
20
  /** A markdown, which takes the place of a layout in a section and holds only Markdown. */
20
21
  export interface LpdfMarkdownNode {
21
22
  type: 'markdown';
22
- attrs: Record<string, string>;
23
+ attrs: Record<string, AttrValue>;
23
24
  nodes: (LpdfMarkdownStylesNode | LpdfMarkdownContentNode)[];
24
25
  }
25
26
  /**