@lpdfio/lpdf 0.29.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/LICENSE CHANGED
@@ -70,7 +70,7 @@ Unlicensed Use is not a substitute for a Professional or Enterprise License. An
70
70
 
71
71
  Unlicensed Use is subject to the following restriction:
72
72
 
73
- **Attribution.** Each document rendered shall include an attribution notice in the form specified in the Software documentation. This notice may not be suppressed, removed, or obscured.
73
+ **Attribution.** Each document rendered shall include the Lpdf attribution in the top-right corner. This notice may not be suppressed, removed, or obscured.
74
74
 
75
75
  This restriction is lifted upon obtaining a valid license key. Community keys are available at no cost at lpdf.io.
76
76
 
package/README.md CHANGED
@@ -25,10 +25,10 @@ const doc = L.document({ size: 'letter' }, [
25
25
  L.column({ gap: '24pt' }, [
26
26
  L.row({ justify: 'between' }, [
27
27
  L.text({ fontSize: '8pt', color: '#888888' }, ['ACME CORP']),
28
- L.text({ fontSize: '22pt', bold: 'true' }, ['Project Proposal']),
28
+ L.text({ fontSize: '22pt', bold: true }, ['Project Proposal']),
29
29
  ]),
30
30
  L.divider({ thickness: 'xs' }),
31
- L.text({ fontSize: '13pt', bold: 'true' }, ['Scope of Work']),
31
+ L.text({ fontSize: '13pt', bold: true }, ['Scope of Work']),
32
32
  L.row({ gap: '12pt', align: 'start' }, [
33
33
  L.text({ color: '#888888', width: '24pt' }, ['01']),
34
34
  L.text({ width: 'fill' }, ['Discovery & Research']),
package/dist/L.d.ts CHANGED
@@ -32,19 +32,24 @@ export declare const L: Readonly<{
32
32
  document: (attrs: import("./kit").DocumentAttr | null, nodes?: import("./kit").LpdfSectionNode[]) => PdfDocument;
33
33
  section: (attrs: import("./kit").SectionAttr | null, nodes?: import("./kit").LpdfSectionChild[]) => import("./kit").LpdfSectionNode;
34
34
  layout: (attrs: import("./attrs").LayoutAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./kit").LpdfLayoutBlock;
35
+ markdown: (attrs: import("./attrs").MarkdownAttr | null, nodes?: (import("./markdown").LpdfMarkdownStylesNode | import("./markdown").LpdfMarkdownContentNode)[]) => import("./markdown").LpdfMarkdownNode;
35
36
  assets: (attrs: DocumentAssets) => DocumentAssets;
36
37
  tokens: (attrs: DocumentTokens) => DocumentTokens;
37
38
  column: (attrs: import("./attrs").ColumnAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfContainerNode;
38
39
  row: (attrs: import("./attrs").RowAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfContainerNode;
39
40
  grid: (attrs: import("./attrs").GridAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfContainerNode;
41
+ list: (attrs: import("./attrs").ListAttr | null, nodes?: import("./layout").LpdfLiNode[]) => import("./layout").LpdfListNode;
42
+ li: (attrs: import("./attrs").LiAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfLiNode;
40
43
  frame: (attrs: import("./attrs").FrameAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfContainerNode;
41
44
  link: (attrs: import("./attrs").LinkAttr, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfContainerNode;
42
45
  table: (attrs: import("./attrs").TableAttr, nodes?: (import("./layout").LpdfTheadNode | import("./layout").LpdfTrNode)[]) => import("./layout").LpdfTableNode;
43
46
  thead: (attrs: import("./attrs").TheadAttr | null, nodes?: import("./layout").LpdfTdNode[]) => import("./layout").LpdfTheadNode;
44
47
  tr: (attrs: import("./attrs").TrAttr | null, nodes?: import("./layout").LpdfTdNode[]) => import("./layout").LpdfTrNode;
45
48
  td: (attrs: import("./attrs").TdAttr | null, nodes?: import("./layout").LpdfNode[]) => import("./layout").LpdfTdNode;
46
- text: (attrs: import("./attrs").TextAttr | null, nodes?: (string | import("./layout").LpdfSpanNode | import("./layout").LpdfFnNode)[]) => import("./layout").LpdfTextNode;
49
+ text: (attrs: import("./attrs").TextAttr | null, nodes?: (string | import("./layout").LpdfSpanNode | import("./layout").LpdfCodeSpanNode | import("./layout").LpdfFnNode)[]) => import("./layout").LpdfTextNode;
50
+ code: (attrs: import("./attrs").CodeAttr | null, nodes?: (string | import("./layout").LpdfSpanNode)[]) => import("./layout").LpdfCodeNode;
47
51
  span: (attrs: import("./attrs").SpanAttr | null, nodes?: string[]) => import("./layout").LpdfSpanNode;
52
+ codeSpan: (attrs: import("./attrs").CodeSpanAttr | null, nodes?: string[]) => import("./layout").LpdfCodeSpanNode;
48
53
  fn: (attrs: null, nodes?: (string | import("./layout").LpdfSpanNode)[]) => import("./layout").LpdfFnNode;
49
54
  divider: (attrs?: import("./attrs").DividerAttr | null) => import("./layout").LpdfDividerNode;
50
55
  img: (attrs: import("./attrs").ImgAttr) => import("./layout").LpdfImgNode;
@@ -59,7 +64,7 @@ export declare const L: Readonly<{
59
64
  ellipse: (attrs: import("./attrs").EllipseAttr) => import("./canvas").LpdfCanvasEllipseNode;
60
65
  circle: (attrs: import("./attrs").CircleAttr) => import("./canvas").LpdfCanvasCircleNode;
61
66
  path: (attrs: import("./attrs").PathAttr) => import("./canvas").LpdfCanvasPathNode;
62
- textAt: (attrs: import("./attrs").TextAtAttr, nodes?: (string | import("./layout").LpdfSpanNode)[]) => import("./canvas").LpdfCanvasTextAtNode;
67
+ textAt: (attrs: import("./attrs").TextAtAttr, nodes?: (string | import("./layout").LpdfSpanNode | import("./layout").LpdfCodeSpanNode)[]) => import("./canvas").LpdfCanvasTextAtNode;
63
68
  imgAt: (attrs: import("./attrs").ImgAtAttr) => import("./canvas").LpdfCanvasImgAtNode;
64
69
  barcodeAt: (attrs: import("./attrs").BarcodeAtAttr) => import("./canvas").LpdfCanvasBarcodeAtNode;
65
70
  }>;
package/dist/L.js CHANGED
@@ -4,6 +4,7 @@ exports.L = exports.NoAttr = void 0;
4
4
  const kit_1 = require("./kit");
5
5
  const layout_1 = require("./layout");
6
6
  const canvas_1 = require("./canvas");
7
+ const markdown_1 = require("./markdown");
7
8
  const engine_1 = require("./engine");
8
9
  const kit_to_xml_1 = require("./kit-to-xml");
9
10
  /** Pass as `attrs` when a node has no attributes. Equivalent to `null`. */
@@ -39,12 +40,15 @@ exports.L = Object.freeze({
39
40
  document: kit_1.LpdfKit.document,
40
41
  section: kit_1.LpdfKit.section,
41
42
  layout: kit_1.LpdfKit.layout,
43
+ markdown: markdown_1.LpdfMarkdown.markdown,
42
44
  assets: (attrs) => attrs,
43
45
  tokens: (attrs) => attrs,
44
46
  // ── Layout ────────────────────────────────────────────────────────────────
45
47
  column: layout_1.LpdfLayout.column,
46
48
  row: layout_1.LpdfLayout.row,
47
49
  grid: layout_1.LpdfLayout.grid,
50
+ list: layout_1.LpdfLayout.list,
51
+ li: layout_1.LpdfLayout.li,
48
52
  frame: layout_1.LpdfLayout.frame,
49
53
  link: layout_1.LpdfLayout.link,
50
54
  table: layout_1.LpdfLayout.table,
@@ -52,7 +56,9 @@ exports.L = Object.freeze({
52
56
  tr: layout_1.LpdfLayout.tr,
53
57
  td: layout_1.LpdfLayout.td,
54
58
  text: layout_1.LpdfLayout.text,
59
+ code: layout_1.LpdfLayout.code,
55
60
  span: layout_1.LpdfLayout.span,
61
+ codeSpan: layout_1.LpdfLayout.codeSpan,
56
62
  fn: layout_1.LpdfLayout.fn,
57
63
  divider: layout_1.LpdfLayout.divider,
58
64
  img: layout_1.LpdfLayout.img,
package/dist/_shared.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface RenderOptions {
9
9
  * Optional ISO 8601 creation timestamp (e.g. `"2024-06-01T12:00:00"`).
10
10
  * When provided, written as `/CreationDate` in the PDF info dictionary.
11
11
  * Omitting this keeps builds reproducible (no embedded timestamp).
12
+ * Required for a document whose `output` asks for `pdfa`, which writes it as both dates of the file.
12
13
  */
13
14
  createdOn?: string;
14
15
  /**
@@ -17,10 +18,48 @@ export interface RenderOptions {
17
18
  * Only applies when `input` is an XML string.
18
19
  */
19
20
  data?: Record<string, unknown> | null;
21
+ /**
22
+ * The password a reader asks for before showing the PDF, for a document that asks for encryption in its
23
+ * `output`. Leave it out, or pass `''`, for a PDF anyone can open, which only the `allow` of the `output`
24
+ * restricts.
25
+ */
26
+ userPassword?: string;
27
+ /**
28
+ * The password that opens the PDF with every permission. A document that asks for encryption in its `output`
29
+ * renders only with it, or with `skipEncryption`; a document that doesn't ask for encryption refuses it, so a
30
+ * file never goes out unencrypted while you believe it is locked.
31
+ */
32
+ ownerPassword?: string;
33
+ /**
34
+ * Renders a document that asks for encryption unencrypted, for a render that cannot have the passwords, such as
35
+ * a preview. Not together with the passwords.
36
+ */
37
+ skipEncryption?: boolean;
20
38
  }
39
+ /**
40
+ * The arguments of the engine's render calls after the document: the passwords and `skipEncryption` as given,
41
+ * and, when there is a password, 32 bytes from the platform's secure generator for AES-256 to cut its key, salts
42
+ * and IVs from, fresh for each render.
43
+ */
44
+ export declare function encryptionArguments(options: RenderOptions, randomBytes: (count: number) => Uint8Array): [string | null, string | null, boolean, Uint8Array];
45
+ /** The value of an attribute in a document tree: a string, a number or a boolean, as the schema types it. */
46
+ export type AttrValue = string | number | boolean;
21
47
  /**
22
48
  * The attributes of an element: an attribute object's own properties, with each camelCase name
23
- * written as the schema's kebab-case one (`fontSize` as `font-size`) and each value as a string.
24
- * Properties that are `undefined` are left out.
49
+ * written as the schema's kebab-case one (`fontSize` as `font-size`) and each value as it is given:
50
+ * `bold: true` and `cols: 3` stay a boolean and a number. The engine refuses a value of the wrong
51
+ * type, such as `cols: '3'` from plain JavaScript, when it renders. A `NaN` or an infinity, which
52
+ * JSON can't hold, is refused here with a `RangeError`. Properties that are `undefined` are left out.
53
+ *
54
+ * @throws {RangeError} when a value is a number that isn't finite.
55
+ */
56
+ export declare function buildAttrs(options: object | null | undefined): Record<string, AttrValue>;
57
+ /**
58
+ * Refuses a number a document can't hold, `NaN` or an infinity, naming where it is. JSON has no such number:
59
+ * `JSON.stringify` writes it as `null`, which the engine takes as an attribute that isn't there.
60
+ *
61
+ * @throws {RangeError} when `value` is a number that isn't finite.
25
62
  */
26
- export declare function buildAttrs(options: object | null | undefined): Record<string, string>;
63
+ export declare function assertFinite(name: string, value: unknown): void;
64
+ /** Refuses a number that isn't finite anywhere in `value`, an object or a list, named by its path from `name`. */
65
+ export declare function assertFiniteIn(name: string, value: unknown): void;
package/dist/_shared.js CHANGED
@@ -3,18 +3,67 @@
3
3
  * Shared types used by both the Node.js and browser entry points.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.encryptionArguments = encryptionArguments;
6
7
  exports.buildAttrs = buildAttrs;
8
+ exports.assertFinite = assertFinite;
9
+ exports.assertFiniteIn = assertFiniteIn;
10
+ /** The random bytes AES-256 needs from the host, one 256-bit key's worth. The engine makes none itself. */
11
+ const ENTROPY_BYTES = 32;
12
+ /**
13
+ * The arguments of the engine's render calls after the document: the passwords and `skipEncryption` as given,
14
+ * and, when there is a password, 32 bytes from the platform's secure generator for AES-256 to cut its key, salts
15
+ * and IVs from, fresh for each render.
16
+ */
17
+ function encryptionArguments(options, randomBytes) {
18
+ const hasPassword = options.userPassword !== undefined || options.ownerPassword !== undefined;
19
+ return [
20
+ options.userPassword ?? null,
21
+ options.ownerPassword ?? null,
22
+ options.skipEncryption ?? false,
23
+ hasPassword ? randomBytes(ENTROPY_BYTES) : new Uint8Array(0),
24
+ ];
25
+ }
7
26
  /**
8
27
  * The attributes of an element: an attribute object's own properties, with each camelCase name
9
- * written as the schema's kebab-case one (`fontSize` as `font-size`) and each value as a string.
10
- * Properties that are `undefined` are left out.
28
+ * written as the schema's kebab-case one (`fontSize` as `font-size`) and each value as it is given:
29
+ * `bold: true` and `cols: 3` stay a boolean and a number. The engine refuses a value of the wrong
30
+ * type, such as `cols: '3'` from plain JavaScript, when it renders. A `NaN` or an infinity, which
31
+ * JSON can't hold, is refused here with a `RangeError`. Properties that are `undefined` are left out.
32
+ *
33
+ * @throws {RangeError} when a value is a number that isn't finite.
11
34
  */
12
35
  function buildAttrs(options) {
13
36
  const result = {};
14
37
  for (const [key, value] of Object.entries(options ?? {})) {
15
38
  if (value !== undefined && value !== null) {
16
- result[key.replace(/[A-Z]/g, c => '-' + c.toLowerCase())] = String(value);
39
+ const name = key.replace(/[A-Z]/g, c => '-' + c.toLowerCase());
40
+ assertFinite(name, value);
41
+ result[name] = value;
17
42
  }
18
43
  }
19
44
  return result;
20
45
  }
46
+ /**
47
+ * Refuses a number a document can't hold, `NaN` or an infinity, naming where it is. JSON has no such number:
48
+ * `JSON.stringify` writes it as `null`, which the engine takes as an attribute that isn't there.
49
+ *
50
+ * @throws {RangeError} when `value` is a number that isn't finite.
51
+ */
52
+ function assertFinite(name, value) {
53
+ if (typeof value === 'number' && !Number.isFinite(value)) {
54
+ throw new RangeError(`${name} must be a finite number, not ${value}`);
55
+ }
56
+ }
57
+ /** Refuses a number that isn't finite anywhere in `value`, an object or a list, named by its path from `name`. */
58
+ function assertFiniteIn(name, value) {
59
+ if (Array.isArray(value)) {
60
+ value.forEach((item, index) => assertFiniteIn(`${name}[${index}]`, item));
61
+ }
62
+ else if (value !== null && typeof value === 'object') {
63
+ for (const [key, item] of Object.entries(value))
64
+ assertFiniteIn(`${name}.${key}`, item);
65
+ }
66
+ else {
67
+ assertFinite(name, value);
68
+ }
69
+ }