@lpdfio/lpdf 0.30.0 → 0.32.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,19 @@ 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();
43
+ this._profiles = new Map();
47
44
  this._markdownStyles = new Map();
48
45
  this._disposed = false;
49
- this._encrypt = null;
50
46
  }
51
47
  /**
52
48
  * Set the license key. Returns `this` for chaining.
@@ -90,30 +86,31 @@ class PdfEngine {
90
86
  return this;
91
87
  }
92
88
  /**
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.
89
+ * Register the bytes of a file the document carries, for the `<file>` of its `<attachments>` whose `ref`, or with
90
+ * no ref whose name, is `name`. The PDF carries them as they are. Call before `render`. Returns `this` for chaining.
95
91
  */
96
- loadMarkdownStyles(src, text) {
92
+ loadFile(name, bytes) {
97
93
  this._throwIfDisposed();
98
- this._markdownStyles.set(src, text);
94
+ this._files.set(name, bytes);
99
95
  return this;
100
96
  }
101
97
  /**
102
- * Configure encryption for all subsequent `render` calls: AES-256 unless
103
- * `options.algorithm` says `'rc4-128'`. Returns `this` for chaining.
98
+ * Register the bytes of an ICC color profile, for the `<profile>` of the document's `<assets>` whose `ref`, or with
99
+ * no ref whose name, is `name`: the press profile a PDF/X document names in its output. Call before `render`.
100
+ * Returns `this` for chaining.
104
101
  */
105
- setEncryption(options) {
102
+ loadProfile(name, bytes) {
106
103
  this._throwIfDisposed();
107
- this._encrypt = options;
104
+ this._profiles.set(name, bytes);
108
105
  return this;
109
106
  }
110
107
  /**
111
- * Remove any previously configured encryption.
112
- * Returns `this` for chaining.
108
+ * Register the text of a file of Markdown rules, the root of which is a `<markdown-styles>`, for the `src` that
109
+ * names it, as `<markdown-styles src="…">` does. Call before `render`. Returns `this` for chaining.
113
110
  */
114
- clearEncryption() {
111
+ loadMarkdownStyles(src, text) {
115
112
  this._throwIfDisposed();
116
- this._encrypt = null;
113
+ this._markdownStyles.set(src, text);
117
114
  return this;
118
115
  }
119
116
  /**
@@ -142,6 +139,46 @@ class PdfEngine {
142
139
  engine.load_markdown_styles(src, text);
143
140
  }
144
141
  }
142
+ /**
143
+ * Hand the engine the files the document carries: the ones loaded here, and the ones read from the `src` of a file
144
+ * that wasn't. A `src` that can't be read is an error that names the file and the path.
145
+ */
146
+ _loadFiles(engine, srcs) {
147
+ const files = new Map(this._files);
148
+ for (const [key, src] of srcs) {
149
+ if (files.has(key))
150
+ continue;
151
+ try {
152
+ files.set(key, (0, node_fs_1.readFileSync)(src));
153
+ }
154
+ catch (e) {
155
+ throw new LpdfRenderError(`file '${key}' of <attachments>: cannot read its src '${src}': ${e instanceof Error ? e.message : String(e)}`);
156
+ }
157
+ }
158
+ for (const [key, bytes] of files) {
159
+ engine.load_file(key, bytes);
160
+ }
161
+ }
162
+ /**
163
+ * Hand the engine the color profiles the document declares: the ones loaded here, and the ones read from the `src`
164
+ * of a profile that wasn't. A `src` that can't be read is an error that names the profile and the path.
165
+ */
166
+ _loadProfiles(engine, srcs) {
167
+ const profiles = new Map(this._profiles);
168
+ for (const [key, src] of srcs) {
169
+ if (profiles.has(key))
170
+ continue;
171
+ try {
172
+ profiles.set(key, (0, node_fs_1.readFileSync)(src));
173
+ }
174
+ catch (e) {
175
+ throw new LpdfRenderError(`profile '${key}' of <assets>: cannot read its src '${src}': ${e instanceof Error ? e.message : String(e)}`);
176
+ }
177
+ }
178
+ for (const [key, bytes] of profiles) {
179
+ engine.load_profile(key, bytes);
180
+ }
181
+ }
145
182
  _throwIfDisposed() {
146
183
  if (this._disposed)
147
184
  throw new Error('PdfEngine has been disposed.');
@@ -176,6 +213,8 @@ class PdfEngine {
176
213
  engine.load_image(name, bytes);
177
214
  }
178
215
  this._loadMarkdownStyles(engine, markdownStylesSrcs(xml));
216
+ this._loadFiles(engine, extractAssetSrcs(xml, 'file'));
217
+ this._loadProfiles(engine, extractAssetSrcs(xml, 'profile'));
179
218
  // Auto-load images declared via <image src="…"> that weren't pre-loaded.
180
219
  for (const [key, src] of extractAssetSrcs(xml, 'image')) {
181
220
  if (!this._images.has(key)) {
@@ -185,11 +224,8 @@ class PdfEngine {
185
224
  catch { /* skip unresolvable image */ }
186
225
  }
187
226
  }
188
- if (this._encrypt) {
189
- configureEncryption(engine, this._encrypt);
190
- }
191
227
  const dataJson = callOptions.data != null ? JSON.stringify(callOptions.data) : null;
192
- pdf = engine.render_pdf(xml, dataJson);
228
+ pdf = engine.render_pdf(xml, dataJson, ...(0, _shared_1.encryptionArguments)(callOptions, secureRandomBytes));
193
229
  }
194
230
  else {
195
231
  // JSON (Kit tree) path — pass JSON directly to render_tree_pdf. Fonts and images the
@@ -220,14 +256,15 @@ class PdfEngine {
220
256
  engine.load_image(name, bytes);
221
257
  }
222
258
  this._loadMarkdownStyles(engine, markdownStylesSrcsOfDocument(input));
223
- if (this._encrypt) {
224
- configureEncryption(engine, this._encrypt);
225
- }
226
- pdf = engine.render_tree_pdf(json);
259
+ this._loadFiles(engine, fileSrcsOfDocument(input));
260
+ this._loadProfiles(engine, assetSrcsOfDocument(input, 'profiles'));
261
+ pdf = engine.render_tree_pdf(json, ...(0, _shared_1.encryptionArguments)(callOptions, secureRandomBytes));
227
262
  }
228
263
  }
229
264
  catch (e) {
230
265
  engine.free();
266
+ if (e instanceof LpdfRenderError)
267
+ throw e;
231
268
  const msg = e instanceof Error ? e.message : String(e);
232
269
  throw new LpdfRenderError(msg);
233
270
  }
@@ -237,16 +274,40 @@ class PdfEngine {
237
274
  }
238
275
  exports.PdfEngine = PdfEngine;
239
276
  // ── Helpers ───────────────────────────────────────────────────────────────────
240
- /** Extract `ref??name → src` pairs from `<font>` or `<image>` tags in XML. */
277
+ // Comments and CDATA sections, whose text is not markup: a <file> shown in a Markdown block is text, not a file.
278
+ const XML_NOT_MARKUP_RE = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>/g;
279
+ const XML_ATTRIBUTE_RE = /([\w:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
280
+ const XML_ENTITY_RE = /&(?:(amp|lt|gt|quot|apos)|#(\d+)|#x([0-9a-fA-F]+));/g;
281
+ const XML_ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'" };
282
+ /** An attribute value as the XML means it, with its entities and character references decoded. */
283
+ function decodeXml(value) {
284
+ return value.replace(XML_ENTITY_RE, (_, name, decimal, hex) =>
285
+ // XML_ENTITY_RE matches exactly one of its three groups, so without a name or a decimal there is a hex.
286
+ name ? XML_ENTITIES[name] : String.fromCodePoint(parseInt(decimal ?? hex, decimal ? 10 : 16)));
287
+ }
288
+ /**
289
+ * The attributes of each `<tag>` element in XML, read as the engine reads them: comments and CDATA sections are left
290
+ * out, a quoted value may hold a `>`, either quote may be used, and the values are decoded.
291
+ */
292
+ function xmlElements(xml, tag) {
293
+ const elements = [];
294
+ // Attribute values may contain '>', so quoted runs are matched whole.
295
+ const re = new RegExp(`<${tag}\\b((?:[^>"']|"[^"]*"|'[^']*')*)>`, 'g');
296
+ for (const match of xml.replace(XML_NOT_MARKUP_RE, '').matchAll(re)) {
297
+ const attributes = new Map();
298
+ for (const attribute of match[1].matchAll(XML_ATTRIBUTE_RE)) {
299
+ attributes.set(attribute[1], decodeXml(attribute[2] ?? attribute[3]));
300
+ }
301
+ elements.push(attributes);
302
+ }
303
+ return elements;
304
+ }
305
+ /** The `ref ?? name → src` pairs of the `<font>`, `<image>`, `<file>` or `<profile>` elements in XML. */
241
306
  function extractAssetSrcs(xml, tag) {
242
307
  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;
308
+ for (const attributes of xmlElements(xml, tag)) {
309
+ const key = attributes.get('ref') ?? attributes.get('name');
310
+ const src = attributes.get('src');
250
311
  if (key && src)
251
312
  result.set(key, src);
252
313
  }
@@ -255,8 +316,8 @@ function extractAssetSrcs(xml, tag) {
255
316
  /** The `src` of each `<markdown-styles>` in XML. */
256
317
  function markdownStylesSrcs(xml) {
257
318
  const srcs = [];
258
- for (const match of xml.matchAll(/<markdown-styles\s[^>]*>/g)) {
259
- const src = /\bsrc="([^"]*)"/.exec(match[0])?.[1];
319
+ for (const attributes of xmlElements(xml, 'markdown-styles')) {
320
+ const src = attributes.get('src');
260
321
  if (src)
261
322
  srcs.push(src);
262
323
  }
@@ -270,14 +331,24 @@ function markdownStylesSrcsOfDocument(document) {
270
331
  if (child.type !== 'markdown')
271
332
  continue;
272
333
  for (const part of child.nodes) {
273
- if (part.type === 'markdown-styles' && part.attrs['src'])
274
- srcs.push(part.attrs['src']);
334
+ const src = part.type === 'markdown-styles' ? part.attrs['src'] : undefined;
335
+ if (typeof src === 'string' && src)
336
+ srcs.push(src);
275
337
  }
276
338
  }
277
339
  }
278
340
  return srcs;
279
341
  }
280
- /** The `ref ?? name → src` pairs of the fonts or images a document declares in `attrs.assets`. */
342
+ /** The `ref ?? name → src` pairs of the files a document carries in `attrs.attachments`. */
343
+ function fileSrcsOfDocument(document) {
344
+ const result = new Map();
345
+ for (const { name, ref, src } of document.attrs['attachments'] ?? []) {
346
+ if (src)
347
+ result.set(ref ?? name, src);
348
+ }
349
+ return result;
350
+ }
351
+ /** The `ref ?? name → src` pairs of the fonts, images or color profiles a document declares in `attrs.assets`. */
281
352
  function assetSrcsOfDocument(document, kind) {
282
353
  const result = new Map();
283
354
  const declared = document.attrs['assets']?.[kind] ?? [];
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, ProfileAttr, 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, ProfileAttr } 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,23 +37,30 @@ 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
- * The fonts and images the document declares, as the `assets` element of the XML does. A font or image
64
- * is picked by its `name`: text sets `font` to a font's name, an `img` sets `name` to an image's.
52
+ * The fonts, images and color profiles the document declares, as the `assets` element of the XML does. A font or
53
+ * image is picked by its `name`: text sets `font` to a font's name, an `img` sets `name` to an image's, and the
54
+ * output's `profile` names a profile.
65
55
  */
66
56
  export interface DocumentAssets {
67
57
  fonts?: FontAttr[];
68
58
  images?: ImageAttr[];
59
+ /**
60
+ * ICC color profiles: the profile of the printing press a PDF/X document is made for, which the printer gives out.
61
+ * Each is read from its `src`, or loaded with `loadProfile` under its `ref` or name.
62
+ */
63
+ profiles?: ProfileAttr[];
69
64
  }
70
65
  export interface DocumentMeta {
71
66
  title?: string;
@@ -73,37 +68,107 @@ export interface DocumentMeta {
73
68
  subject?: string;
74
69
  keywords?: string;
75
70
  creator?: string;
71
+ /** The language the document is written in, as a language tag such as `en-US`. A PDF/UA document needs it. */
72
+ lang?: string;
73
+ }
74
+ /**
75
+ * How the PDF file is written, as the `output` element of the XML is: whether it is encrypted, and what a reader
76
+ * may do with it, and the standard it meets. The passwords are not here: they come with the render call.
77
+ */
78
+ export interface DocumentOutput {
79
+ /** The cipher the PDF is encrypted with, `aes-256` or `rc4-128` (`Cipher`). Without it the PDF is not encrypted. */
80
+ encrypt?: string;
81
+ /**
82
+ * What a reader may do without the owner password, separated by spaces (`Permission`): `all`, or any of
83
+ * `print`, `modify`, `copy`, `annotate`, `fill-forms`, `accessibility`, `assemble`. A permission comes only from
84
+ * naming it: without `allow`, or with `''`, nothing is allowed, and `all` allows everything. It needs `encrypt`.
85
+ */
86
+ allow?: string;
87
+ /**
88
+ * Writes the PDF as PDF/A, the standard for files kept for the long term: `2b`, or `2u` when its text must also
89
+ * be searchable. The render needs `createdOn`, and every text a font of `assets`. What PDF/A cannot hold is an
90
+ * error that says what to change: a built-in font, a CMYK color, a form field, a barcode's digits, `encrypt`.
91
+ */
92
+ pdfa?: string;
93
+ /**
94
+ * Writes the PDF as PDF/UA-1, a tagged PDF a screen reader can read: `1`. It combines with `pdfa`. The render
95
+ * needs `createdOn`, the meta a `title` and a `lang`, every image and barcode of the layout an `alt`, and every
96
+ * text a font of `assets`. What PDF/UA cannot hold is an error that says what to change.
97
+ */
98
+ pdfua?: string;
99
+ /**
100
+ * Writes the PDF as PDF/X-4, for a commercial printer: `4`. It needs `profile`, and combines with `pdfua`. Every
101
+ * page gets a TrimBox and a BleedBox, RGB is tied to sRGB, and links print as text, as PDF/X allows nothing
102
+ * clickable. The render needs `createdOn`, the meta a `title`, and every text a font of `assets`. What PDF/X cannot
103
+ * hold is an error that says what to change: a profile that is not a printer profile, a built-in font, a form
104
+ * field, a CMYK color with a profile that is not a CMYK one, `encrypt`, `pdfa`, attachments.
105
+ */
106
+ pdfx?: string;
107
+ /** The profile of `assets` that a PDF/X file is made for: the ICC profile of the printing press. It needs `pdfx`. */
108
+ profile?: string;
109
+ /**
110
+ * Writes the PDF linearized, for fast web view: page 1 comes first in the file, with hints that say where every
111
+ * other page is, so a viewer that fetches the file by byte ranges, as a browser does, shows page 1 while the rest
112
+ * downloads. Each page then names only the fonts and images it draws. With `encrypt` the file is encrypted first
113
+ * and linearized last. Any change to the file afterwards, such as a stamp, ends the linearization.
114
+ */
115
+ linearize?: boolean;
76
116
  }
77
117
  export interface SectionAttr {
78
118
  size?: string;
79
119
  orientation?: string;
120
+ /** How far this section's pages reach past their trim edge, such as `5mm`. Overrides the document's; `0mm` takes it away. */
121
+ bleed?: string;
80
122
  background?: string;
81
123
  title?: string;
82
- debug?: string;
124
+ debug?: boolean;
83
125
  type?: string;
84
126
  tocIndentStep?: string;
85
127
  footnoteCounter?: string;
128
+ /**
129
+ * The number of the section's first page, which starts a new run of pages: `1` restarts the count. Without it the
130
+ * pages count on from the section before. `{page}` prints a page's number, and `{pages}` how many pages its run has.
131
+ */
132
+ pageStart?: number;
133
+ /**
134
+ * How the section's page numbers are written: `decimal`, `lower-alpha`, `upper-alpha`, `lower-roman` or
135
+ * `upper-roman`. Without it the section keeps the format before.
136
+ */
137
+ pageFormat?: string;
138
+ /** Text before the section's page numbers, such as `A-`. Without it the section keeps the prefix before; `''` takes it away. */
139
+ pagePrefix?: string;
86
140
  }
87
141
  export interface DocumentAttr {
88
142
  size?: string;
89
143
  orientation?: string;
144
+ /**
145
+ * How far each page reaches past its trim edge, such as `3mm`, for a background or an image that runs off the page
146
+ * when it is cut. The page grows by it and its background fills it; the layout keeps to the trimmed page.
147
+ */
148
+ bleed?: string;
90
149
  background?: string;
91
150
  font?: string;
92
151
  assets?: DocumentAssets;
93
152
  tokens?: DocumentTokens;
94
153
  meta?: DocumentMeta;
95
- debug?: string;
154
+ output?: DocumentOutput;
155
+ /**
156
+ * Files the PDF carries inside it, as the `attachments` element of the XML does: a reader lists them in its
157
+ * attachments panel. Each file's bytes are read from its `src`, or loaded with `loadFile` under its `ref` or name.
158
+ */
159
+ attachments?: FileAttr[];
160
+ debug?: boolean;
96
161
  footnoteCounter?: string;
97
162
  }
98
163
  /** A layout block — wraps an ordered list of layout nodes. */
99
164
  export interface LpdfLayoutBlock {
100
165
  type: 'layout';
101
- attrs: Record<string, string>;
166
+ attrs: Record<string, AttrValue>;
102
167
  nodes: LpdfNode[];
103
168
  }
104
169
  export interface LpdfSectionNode {
105
170
  type: 'section';
106
- attrs: Record<string, string>;
171
+ attrs: Record<string, AttrValue>;
107
172
  nodes: LpdfSectionChild[];
108
173
  }
109
174
  /**
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
  /**