@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (139) hide show
  1. package/README.md +3 -3
  2. package/dist/api/args.d.ts +1 -1
  3. package/dist/api/args.js +2 -5
  4. package/dist/api/color.d.ts +4 -4
  5. package/dist/api/color.js +11 -17
  6. package/dist/api/content.d.ts +8 -8
  7. package/dist/api/content.js +23 -24
  8. package/dist/api/descriptor.d.ts +2 -2
  9. package/dist/api/descriptor.js +75 -31
  10. package/dist/api/index.d.ts +8 -8
  11. package/dist/api/index.js +8 -24
  12. package/dist/api/insets.js +4 -8
  13. package/dist/api/layout.d.ts +27 -12
  14. package/dist/api/layout.js +46 -45
  15. package/dist/api/structure.d.ts +60 -13
  16. package/dist/api/structure.js +132 -88
  17. package/dist/api/table.d.ts +5 -5
  18. package/dist/api/table.js +28 -24
  19. package/dist/api/text.d.ts +27 -2
  20. package/dist/api/text.js +45 -27
  21. package/dist/assets/font-data.d.ts +2 -0
  22. package/dist/assets/font-data.js +6 -0
  23. package/dist/assets/font-data.ts +7 -0
  24. package/dist/common/color.js +1 -5
  25. package/dist/constants/page-sizes.js +3 -6
  26. package/dist/constants/pdf-parts.js +1 -4
  27. package/dist/elements/container-element.d.ts +4 -4
  28. package/dist/elements/container-element.js +9 -13
  29. package/dist/elements/image-element.d.ts +18 -2
  30. package/dist/elements/image-element.js +81 -105
  31. package/dist/elements/index.d.ts +12 -10
  32. package/dist/elements/index.js +12 -28
  33. package/dist/elements/layout/default-text-style-element.d.ts +30 -0
  34. package/dist/elements/layout/default-text-style-element.js +47 -0
  35. package/dist/elements/layout/deferred-element.d.ts +3 -3
  36. package/dist/elements/layout/deferred-element.js +4 -8
  37. package/dist/elements/layout/expanded-element.d.ts +3 -3
  38. package/dist/elements/layout/expanded-element.js +10 -14
  39. package/dist/elements/layout/padding-element.d.ts +3 -3
  40. package/dist/elements/layout/padding-element.js +9 -14
  41. package/dist/elements/layout/positioned-element.d.ts +44 -0
  42. package/dist/elements/layout/positioned-element.js +61 -0
  43. package/dist/elements/layout/repeating-header-element.d.ts +3 -3
  44. package/dist/elements/layout/repeating-header-element.js +8 -12
  45. package/dist/elements/layout/sized-container-element.d.ts +2 -2
  46. package/dist/elements/layout/sized-container-element.js +6 -11
  47. package/dist/elements/line-element.d.ts +3 -3
  48. package/dist/elements/line-element.js +5 -10
  49. package/dist/elements/page-element.d.ts +8 -6
  50. package/dist/elements/page-element.js +31 -23
  51. package/dist/elements/pdf-document-element.d.ts +10 -4
  52. package/dist/elements/pdf-document-element.js +11 -10
  53. package/dist/elements/pdf-element.d.ts +28 -3
  54. package/dist/elements/pdf-element.js +10 -19
  55. package/dist/elements/rectangle-element.d.ts +14 -6
  56. package/dist/elements/rectangle-element.js +44 -21
  57. package/dist/elements/row-element.d.ts +3 -3
  58. package/dist/elements/row-element.js +7 -11
  59. package/dist/elements/text-element.d.ts +37 -11
  60. package/dist/elements/text-element.js +64 -39
  61. package/dist/index.d.ts +3 -3
  62. package/dist/index.js +3 -19
  63. package/dist/ir/display-list.d.ts +22 -3
  64. package/dist/ir/display-list.js +1 -2
  65. package/dist/layout/box-constraints.js +2 -6
  66. package/dist/layout/fragmentation.d.ts +8 -1
  67. package/dist/layout/fragmentation.js +22 -10
  68. package/dist/platform/browser-fs.d.ts +2 -0
  69. package/dist/platform/browser-fs.js +9 -0
  70. package/dist/platform/browser-image.d.ts +5 -0
  71. package/dist/platform/browser-image.js +13 -0
  72. package/dist/platform/node-fs.d.ts +2 -0
  73. package/dist/platform/node-fs.js +10 -0
  74. package/dist/platform/node-image.d.ts +5 -0
  75. package/dist/platform/node-image.js +9 -0
  76. package/dist/renderer/container-renderer.d.ts +3 -3
  77. package/dist/renderer/container-renderer.js +12 -27
  78. package/dist/renderer/default-text-style-renderer.d.ts +6 -0
  79. package/dist/renderer/default-text-style-renderer.js +10 -0
  80. package/dist/renderer/deferred-renderer.d.ts +3 -3
  81. package/dist/renderer/deferred-renderer.js +8 -23
  82. package/dist/renderer/expanded-renderer.d.ts +3 -3
  83. package/dist/renderer/expanded-renderer.js +6 -21
  84. package/dist/renderer/image-renderer.d.ts +3 -3
  85. package/dist/renderer/image-renderer.js +77 -75
  86. package/dist/renderer/index.d.ts +10 -10
  87. package/dist/renderer/index.js +10 -26
  88. package/dist/renderer/line-renderer.d.ts +3 -3
  89. package/dist/renderer/line-renderer.js +13 -28
  90. package/dist/renderer/padding-renderer.d.ts +3 -3
  91. package/dist/renderer/padding-renderer.js +6 -21
  92. package/dist/renderer/page-renderer.d.ts +2 -2
  93. package/dist/renderer/page-renderer.js +61 -77
  94. package/dist/renderer/pdf-backend.d.ts +2 -2
  95. package/dist/renderer/pdf-backend.js +34 -17
  96. package/dist/renderer/pdf-config.js +4 -7
  97. package/dist/renderer/pdf-document-class.d.ts +5 -5
  98. package/dist/renderer/pdf-document-class.js +24 -41
  99. package/dist/renderer/pdf-document-renderer.d.ts +3 -3
  100. package/dist/renderer/pdf-document-renderer.js +71 -85
  101. package/dist/renderer/pdf-renderer.d.ts +2 -2
  102. package/dist/renderer/pdf-renderer.js +83 -90
  103. package/dist/renderer/positioned-renderer.d.ts +6 -0
  104. package/dist/renderer/positioned-renderer.js +10 -0
  105. package/dist/renderer/rectangle-renderer.d.ts +3 -3
  106. package/dist/renderer/rectangle-renderer.js +45 -44
  107. package/dist/renderer/repeating-header-renderer.d.ts +3 -3
  108. package/dist/renderer/repeating-header-renderer.js +11 -26
  109. package/dist/renderer/row-renderer.d.ts +3 -3
  110. package/dist/renderer/row-renderer.js +12 -27
  111. package/dist/renderer/text-renderer.d.ts +6 -5
  112. package/dist/renderer/text-renderer.js +33 -42
  113. package/dist/text/line-breaker.d.ts +8 -5
  114. package/dist/text/line-breaker.js +67 -16
  115. package/dist/text/text-style.d.ts +25 -0
  116. package/dist/text/text-style.js +29 -0
  117. package/dist/utils/afm-parser.js +3 -13
  118. package/dist/utils/bytes.d.ts +24 -0
  119. package/dist/utils/bytes.js +76 -0
  120. package/dist/utils/flex-layout.d.ts +2 -2
  121. package/dist/utils/flex-layout.js +15 -20
  122. package/dist/utils/font-metrics.d.ts +1 -1
  123. package/dist/utils/font-metrics.js +1 -2
  124. package/dist/utils/font-path.js +3 -6
  125. package/dist/utils/image-helper.d.ts +6 -5
  126. package/dist/utils/image-helper.js +101 -111
  127. package/dist/utils/md5.d.ts +4 -0
  128. package/dist/utils/md5.js +80 -0
  129. package/dist/utils/pdf-object-manager.d.ts +10 -6
  130. package/dist/utils/pdf-object-manager.js +89 -94
  131. package/dist/utils/renderer-registry.js +1 -5
  132. package/dist/utils/ttf-parser.d.ts +2 -2
  133. package/dist/utils/ttf-parser.js +32 -36
  134. package/dist/utils/ttf-subsetter.d.ts +1 -1
  135. package/dist/utils/ttf-subsetter.js +40 -42
  136. package/dist/utils/utf8-to-windows1252-encoder.js +1 -4
  137. package/dist/validators/element-validator.d.ts +2 -2
  138. package/dist/validators/element-validator.js +17 -23
  139. package/package.json +14 -2
@@ -1,71 +1,61 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Column = Column;
4
- exports.Row = Row;
5
- exports.Box = Box;
6
- exports.Padding = Padding;
7
- exports.Spacer = Spacer;
8
- exports.Expanded = Expanded;
9
- const container_element_1 = require("../elements/container-element");
10
- const row_element_1 = require("../elements/row-element");
11
- const rectangle_element_1 = require("../elements/rectangle-element");
12
- const expanded_element_1 = require("../elements/layout/expanded-element");
13
- const padding_element_1 = require("../elements/layout/padding-element");
14
- const color_1 = require("./color");
15
- const insets_1 = require("./insets");
16
- const args_1 = require("./args");
1
+ import { ContainerElement } from "../elements/container-element.js";
2
+ import { RowElement } from "../elements/row-element.js";
3
+ import { RectangleElement } from "../elements/rectangle-element.js";
4
+ import { ExpandedElement } from "../elements/layout/expanded-element.js";
5
+ import { PaddingElement } from "../elements/layout/padding-element.js";
6
+ import { PositionedElement } from "../elements/layout/positioned-element.js";
7
+ import { toColor } from "./color.js";
8
+ import { toEdges } from "./insets.js";
9
+ import { splitArgs } from "./args.js";
17
10
  // The public default for `cross` is `start` (locked §5) - i.e. don't stretch a child unless
18
11
  // asked. The engine default is `stretch`; the factory sets the friendlier public one.
19
12
  const DEFAULT_CROSS = "start";
20
- function Column(a, b) {
21
- var _a;
22
- const { opts, children } = (0, args_1.splitArgs)(a, b);
23
- return new container_element_1.ContainerElement({
13
+ export function Column(a, b) {
14
+ const { opts, children } = splitArgs(a, b);
15
+ return new ContainerElement({
24
16
  x: 0,
25
17
  y: 0,
26
18
  children,
27
19
  gap: opts.gap,
28
20
  main: opts.justify, // undefined → engine default `start` (matches §5)
29
- cross: (_a = opts.align) !== null && _a !== void 0 ? _a : DEFAULT_CROSS,
21
+ cross: opts.align ?? DEFAULT_CROSS,
30
22
  });
31
23
  }
32
- function Row(a, b) {
33
- var _a;
34
- const { opts, children } = (0, args_1.splitArgs)(a, b);
35
- return new row_element_1.RowElement({
24
+ export function Row(a, b) {
25
+ const { opts, children } = splitArgs(a, b);
26
+ return new RowElement({
36
27
  children,
37
28
  gap: opts.gap,
38
29
  main: opts.justify,
39
- cross: (_a = opts.align) !== null && _a !== void 0 ? _a : DEFAULT_CROSS,
30
+ cross: opts.align ?? DEFAULT_CROSS,
40
31
  });
41
32
  }
42
- function Box(a, b) {
43
- var _a;
44
- const { opts, children } = (0, args_1.splitArgs)(a, b);
33
+ export function Box(a, b) {
34
+ const { opts, children } = splitArgs(a, b);
45
35
  const content = opts.padding !== undefined && children.length > 0
46
36
  ? [
47
- new padding_element_1.PaddingElement({
48
- margin: (0, insets_1.toEdges)(opts.padding),
37
+ new PaddingElement({
38
+ margin: toEdges(opts.padding),
49
39
  child: children.length === 1 ? children[0] : Column(children),
50
40
  }),
51
41
  ]
52
42
  : children;
53
43
  // A side is set if it (or the uniform `border`) is given. If ANY differs from a plain
54
- // uniform border, we hand the engine per-side colours (which draws individual lines).
44
+ // uniform border, we hand the engine per-side colors (which draws individual lines).
55
45
  const sideKeys = [opts.borderTop, opts.borderRight, opts.borderBottom, opts.borderLeft];
56
46
  const hasPerSide = sideKeys.some((s) => s !== undefined);
57
47
  const side = (s) => {
58
- const c = s !== null && s !== void 0 ? s : opts.border;
59
- return c !== undefined ? (0, color_1.toColor)(c) : undefined;
48
+ const c = s ?? opts.border;
49
+ return c !== undefined ? toColor(c) : undefined;
60
50
  };
61
51
  const hasBorder = opts.border !== undefined || opts.borderWidth !== undefined || hasPerSide;
62
- return new rectangle_element_1.RectangleElement({
52
+ return new RectangleElement({
63
53
  x: 0,
64
54
  y: 0,
65
55
  children: content,
66
- color: opts.border !== undefined ? (0, color_1.toColor)(opts.border) : undefined,
67
- backgroundColor: opts.bg !== undefined ? (0, color_1.toColor)(opts.bg) : undefined,
68
- borderWidth: hasBorder ? ((_a = opts.borderWidth) !== null && _a !== void 0 ? _a : 1) : 0,
56
+ color: opts.border !== undefined ? toColor(opts.border) : undefined,
57
+ backgroundColor: opts.bg !== undefined ? toColor(opts.bg) : undefined,
58
+ borderWidth: hasBorder ? (opts.borderWidth ?? 1) : 0,
69
59
  width: opts.width,
70
60
  height: opts.height,
71
61
  radius: opts.radius,
@@ -77,23 +67,34 @@ function Box(a, b) {
77
67
  left: side(opts.borderLeft),
78
68
  }
79
69
  : undefined,
70
+ relative: opts.relative,
71
+ overflow: opts.overflow,
80
72
  });
81
73
  }
82
74
  /** Insets a single child by `padding` (a number / `{x,y}` / `{top,…}` / 4-tuple). */
83
- function Padding(padding, child) {
84
- return new padding_element_1.PaddingElement({ margin: (0, insets_1.toEdges)(padding), child });
75
+ export function Padding(padding, child) {
76
+ return new PaddingElement({ margin: toEdges(padding), child });
77
+ }
78
+ /**
79
+ * Places a child OUT OF FLOW, relative to the nearest enclosing `relative` Box. Two ways, pick per
80
+ * axis: pin to EDGES - `Positioned({ top, left, right, bottom }, child)`, where a negative value
81
+ * pokes into / out of the corner (a badge, a tab, a ribbon) and pinning both sides stretches; or
82
+ * ANCHOR + nudge - `Positioned({ h: "center", v: "end", x: -10, y: -8 }, child)`, i.e. centered /
83
+ * end-aligned with a pixel offset. An edge wins over an anchor on the same axis.
84
+ */
85
+ export function Positioned(opts, child) {
86
+ return new PositionedElement({ child, ...opts });
85
87
  }
86
88
  /**
87
89
  * A flexible empty gap that pushes its siblings apart - `Row([a, Spacer(), b])` sends `a`
88
90
  * and `b` to the edges. `flex` weights it against other flex children (default 1).
89
91
  */
90
- function Spacer(flex = 1) {
91
- return new expanded_element_1.ExpandedElement({ flex, child: Column([]) });
92
+ export function Spacer(flex = 1) {
93
+ return new ExpandedElement({ flex, child: Column([]) });
92
94
  }
93
- function Expanded(a, b) {
94
- var _a;
95
+ export function Expanded(a, b) {
95
96
  const isOptsForm = b !== undefined;
96
97
  const opts = (isOptsForm ? a : {});
97
98
  const child = (isOptsForm ? b : a);
98
- return new expanded_element_1.ExpandedElement({ flex: (_a = opts.flex) !== null && _a !== void 0 ? _a : 1, child });
99
+ return new ExpandedElement({ flex: opts.flex ?? 1, child });
99
100
  }
@@ -1,11 +1,22 @@
1
- import { PDFDocumentElement } from "../elements/pdf-document-element";
2
- import { PageElement } from "../elements/page-element";
3
- import { PDFElement } from "../elements/pdf-element";
4
- import { PageSize } from "../constants/page-sizes";
5
- import { StackOptions } from "./layout";
6
- import { Insets } from "./insets";
7
- /** A page size: a `PageSize` enum, or a friendly name like `"A4"` / `"letter"` (any case). */
8
- export type PageSizeInput = PageSize | string;
1
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
2
+ import { PageElement } from "../elements/page-element.js";
3
+ import { PDFElement } from "../elements/pdf-element.js";
4
+ import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
5
+ import type { OverflowPolicy } from "../layout/fragmentation.js";
6
+ import { PageSize } from "../constants/page-sizes.js";
7
+ import { StackOptions } from "./layout.js";
8
+ import { Insets } from "./insets.js";
9
+ import { TextDefaults } from "./text.js";
10
+ /** A custom page size. Use the `mm()` helper for millimetres, or pass points directly. */
11
+ export interface CustomSize {
12
+ width: number;
13
+ height: number;
14
+ unit?: "pt" | "mm";
15
+ }
16
+ /** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
17
+ export declare function mm(width: number, height: number): CustomSize;
18
+ /** A page size: a named `PageSize`/string (`"A4"`, `"letter"`, any case), or a `CustomSize`. */
19
+ export type PageSizeInput = PageSize | string | CustomSize;
9
20
  export interface PageOptions extends StackOptions {
10
21
  /** Page size (default A4). */
11
22
  size?: PageSizeInput;
@@ -24,17 +35,50 @@ export interface PageOptions extends StackOptions {
24
35
  */
25
36
  export declare function Page(children: PDFElement[]): PageElement;
26
37
  export declare function Page(opts: PageOptions, children: PDFElement[]): PageElement;
27
- export interface DocumentOptions {
38
+ /** Document options: PDF metadata plus the inheritable text defaults (font, size, color, lineHeight,
39
+ * align, bold/italic) every `Text` inherits unless it sets its own - Flutter's `DefaultTextStyle`. */
40
+ export interface DocumentOptions extends TextDefaults {
28
41
  /** PDF metadata. */
29
42
  meta?: {
30
43
  title?: string;
31
44
  author?: string;
32
45
  };
33
46
  }
47
+ /** A single font file for `addFont`: a `.ttf` path (read on Node) or its raw bytes (e.g. a browser
48
+ * upload). */
49
+ export type FontFileSource = string | Uint8Array;
50
+ /** A styled font family for `addFont`: one file per style, only `normal` required. `Text({ bold,
51
+ * italic })` then picks the right face, falling back to `normal`. */
52
+ export interface FontFamilyInput {
53
+ normal: FontFileSource;
54
+ bold?: FontFileSource;
55
+ italic?: FontFileSource;
56
+ boldItalic?: FontFileSource;
57
+ }
58
+ /** What `addFont` accepts: one file (path or bytes), or a styled family. */
59
+ export type FontSource = FontFileSource | FontFamilyInput;
60
+ /** The object the `Document(...)` factory returns: the element tree plus a managed font registry. */
61
+ export interface JasyDocument extends PDFDocumentElement {
62
+ /** Register a font under `name`, then use it via `Text({ font: name })`. The source is a `.ttf`
63
+ * path (read now, on Node), raw bytes, or a styled family. Re-adding a name overwrites it.
64
+ * A registered font that no `Text` actually uses is dropped at render and costs nothing. */
65
+ addFont(name: string, source: FontSource): this;
66
+ /** The names of the registered fonts. */
67
+ getFonts(): string[];
68
+ /** Whether a font is registered under `name`. */
69
+ hasFont(name: string): boolean;
70
+ }
34
71
  /** The document root. `Document(pages)` or `Document(opts, pages)`. */
35
- export declare function Document(pages: PageElement[]): PDFDocumentElement;
36
- export declare function Document(opts: DocumentOptions, pages: PageElement[]): PDFDocumentElement;
37
- export type FontBytes = Buffer | Uint8Array;
72
+ export declare function Document(pages: PageElement[]): JasyDocument;
73
+ export declare function Document(opts: DocumentOptions, pages: PageElement[]): JasyDocument;
74
+ /**
75
+ * Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
76
+ * Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
77
+ * inherit them unless they set their own, and they layer onto whatever is inherited from above.
78
+ * `DefaultTextStyle(opts, children)`.
79
+ */
80
+ export declare function DefaultTextStyle(opts: TextDefaults, children: PDFElement[]): DefaultTextStyleElement;
81
+ export type FontBytes = Uint8Array;
38
82
  /** A font family: one `.ttf` per style. Only `normal` is required; `bold`/`italic`/`boldItalic`
39
83
  * are picked up automatically by `Text({ bold, italic })`, falling back to `normal` if absent. */
40
84
  export interface FontFamily {
@@ -73,8 +117,11 @@ export interface RenderOptions {
73
117
  standardFonts?: boolean;
74
118
  /** FlateDecode-compress the streams (default true). Set false for a greppable, uncompressed PDF. */
75
119
  compress?: boolean;
120
+ /** What to do when content is taller than a page and cannot break: `"error"` throws (default),
121
+ * `"warn"` logs and clips, `"ignore"` clips silently. It is always clipped either way. */
122
+ onOverflow?: OverflowPolicy;
76
123
  }
77
124
  /** Renders a `Document(...)` tree to the raw PDF string. */
78
125
  export declare function renderPdf(doc: PDFDocumentElement, options?: RenderOptions): Promise<string>;
79
126
  /** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
80
- export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array>;
127
+ export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array<ArrayBuffer>>;
@@ -1,125 +1,169 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.Page = Page;
13
- exports.Document = Document;
14
- exports.renderPdf = renderPdf;
15
- exports.renderToBytes = renderToBytes;
16
- const pdf_document_element_1 = require("../elements/pdf-document-element");
17
- const page_element_1 = require("../elements/page-element");
18
- const page_sizes_1 = require("../constants/page-sizes");
19
- const pdf_config_1 = require("../renderer/pdf-config");
20
- const pdf_document_class_1 = require("../renderer/pdf-document-class");
21
- const pdf_object_manager_1 = require("../utils/pdf-object-manager");
22
- const utf8_to_windows1252_encoder_1 = require("../utils/utf8-to-windows1252-encoder");
23
- const layout_1 = require("./layout");
24
- const insets_1 = require("./insets");
25
- const PAGE_SIZE_VALUES = new Set(Object.values(page_sizes_1.PageSize));
1
+ import { readFileBytes } from "../platform/node-fs.js";
2
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
3
+ import { PageElement } from "../elements/page-element.js";
4
+ import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
5
+ import { PageSize } from "../constants/page-sizes.js";
6
+ import { Orientation } from "../renderer/pdf-config.js";
7
+ import { PDFDocument } from "../renderer/pdf-document-class.js";
8
+ import { FontStyle } from "../utils/pdf-object-manager.js";
9
+ import { getArrayBuffer } from "../utils/utf8-to-windows1252-encoder.js";
10
+ import { Column } from "./layout.js";
11
+ import { toEdges } from "./insets.js";
12
+ import { toTextStyleOverride } from "./text.js";
13
+ const MM_TO_PT = 72 / 25.4; // 1 mm in PDF points
14
+ /** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
15
+ export function mm(width, height) {
16
+ return { width, height, unit: "mm" };
17
+ }
18
+ const PAGE_SIZE_VALUES = new Set(Object.values(PageSize));
26
19
  function toPageSize(input) {
27
20
  const v = String(input).toLowerCase();
28
21
  if (!PAGE_SIZE_VALUES.has(v))
29
22
  throw new Error(`Unknown page size: "${input}"`);
30
23
  return v;
31
24
  }
25
+ /** Resolve `size` into config fields: a named `pageSize`, or an explicit [width, height] in points. */
26
+ function resolveSize(input) {
27
+ if (input !== undefined && typeof input === "object") {
28
+ const f = input.unit === "mm" ? MM_TO_PT : 1;
29
+ return { pageSize: PageSize.A4, customSize: [input.width * f, input.height * f] };
30
+ }
31
+ return { pageSize: input !== undefined ? toPageSize(input) : PageSize.A4 };
32
+ }
32
33
  /** Default page margin (all sides, points) when a `Page` doesn't set one. */
33
34
  const DEFAULT_MARGIN = 56;
34
- function Page(a, b) {
35
- var _a;
35
+ export function Page(a, b) {
36
36
  const isOpts = !Array.isArray(a);
37
37
  const opts = (isOpts ? a : {});
38
- const children = (isOpts ? (b !== null && b !== void 0 ? b : []) : a);
39
- const [top, right, bottom, left] = (0, insets_1.toEdges)((_a = opts.margin) !== null && _a !== void 0 ? _a : DEFAULT_MARGIN);
38
+ const children = (isOpts ? (b ?? []) : a);
39
+ const [top, right, bottom, left] = toEdges(opts.margin ?? DEFAULT_MARGIN);
40
40
  const config = {
41
- pageSize: opts.size !== undefined ? toPageSize(opts.size) : page_sizes_1.PageSize.A4,
42
- orientation: opts.orientation === "landscape" ? pdf_config_1.Orientation.landscape : pdf_config_1.Orientation.portrait,
41
+ ...resolveSize(opts.size),
42
+ orientation: opts.orientation === "landscape" ? Orientation.landscape : Orientation.portrait,
43
43
  margin: { top, right, bottom, left },
44
44
  };
45
- return new page_element_1.PageElement({
45
+ return new PageElement({
46
46
  config,
47
47
  header: opts.header,
48
48
  footer: opts.footer,
49
- children: [(0, layout_1.Column)({ gap: opts.gap, justify: opts.justify, align: opts.align }, children)],
49
+ children: [Column({ gap: opts.gap, justify: opts.justify, align: opts.align }, children)],
50
50
  });
51
51
  }
52
52
  // Document metadata is a document-render concern, not part of the element tree, so it is
53
53
  // kept beside the returned element and picked up by `renderPdf`.
54
54
  const docMeta = new WeakMap();
55
- function Document(a, b) {
55
+ // Fonts registered via doc.addFont(...), kept beside the element (like meta) and registered on the
56
+ // object manager at render time. Path sources are read to bytes in addFont, so the map holds bytes.
57
+ const docFonts = new WeakMap();
58
+ /** Reads any path sources to bytes, leaving bytes / families as-is. */
59
+ function resolveFontSource(source) {
60
+ const read = (s) => (typeof s === "string" ? readFileBytes(s) : s);
61
+ if (typeof source === "string" || source instanceof Uint8Array) {
62
+ return read(source);
63
+ }
64
+ const family = { normal: read(source.normal) };
65
+ if (source.bold)
66
+ family.bold = read(source.bold);
67
+ if (source.italic)
68
+ family.italic = read(source.italic);
69
+ if (source.boldItalic)
70
+ family.boldItalic = read(source.boldItalic);
71
+ return family;
72
+ }
73
+ export function Document(a, b) {
56
74
  const isOpts = !Array.isArray(a);
57
75
  const opts = (isOpts ? a : {});
58
- const pages = (isOpts ? (b !== null && b !== void 0 ? b : []) : a);
59
- const doc = new pdf_document_element_1.PDFDocumentElement({ children: pages });
76
+ const pages = (isOpts ? (b ?? []) : a);
77
+ const doc = new PDFDocumentElement({
78
+ children: pages,
79
+ defaultTextStyle: toTextStyleOverride(opts),
80
+ });
60
81
  if (opts.meta)
61
82
  docMeta.set(doc, opts.meta);
83
+ // Managed font registry: addFont registers, getFonts/hasFont query, render reads it (below).
84
+ const registry = new Map();
85
+ docFonts.set(doc, registry);
86
+ doc.addFont = (name, source) => {
87
+ registry.set(name, resolveFontSource(source));
88
+ return doc;
89
+ };
90
+ doc.getFonts = () => [...registry.keys()];
91
+ doc.hasFont = (name) => registry.has(name);
62
92
  return doc;
63
93
  }
94
+ /**
95
+ * Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
96
+ * Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
97
+ * inherit them unless they set their own, and they layer onto whatever is inherited from above.
98
+ * `DefaultTextStyle(opts, children)`.
99
+ */
100
+ export function DefaultTextStyle(opts, children) {
101
+ return new DefaultTextStyleElement({ style: toTextStyleOverride(opts), child: Column(children) });
102
+ }
64
103
  function isFontBytes(v) {
65
- return Buffer.isBuffer(v) || v instanceof Uint8Array;
104
+ return v instanceof Uint8Array;
66
105
  }
67
106
  /** Renders a `Document(...)` tree to the raw PDF string. */
68
- function renderPdf(doc, options) {
69
- return __awaiter(this, void 0, void 0, function* () {
70
- var _a, _b;
71
- const meta = docMeta.get(doc);
72
- const config = Object.assign(Object.assign({}, (meta ? { metaData: { title: meta.title, author: meta.author, keywords: [] } } : {})), ((options === null || options === void 0 ? void 0 : options.standardFonts) === false ? { registerStandardFonts: false } : {}));
73
- const fonts = (_a = options === null || options === void 0 ? void 0 : options.fonts) !== null && _a !== void 0 ? _a : {};
74
- const attachments = (_b = options === null || options === void 0 ? void 0 : options.attachments) !== null && _b !== void 0 ? _b : [];
75
- // A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
76
- // font registration + config handling (the constructor does both). Custom fonts are
77
- // registered here, before layout/render, so both the metrics and the backend see them.
78
- const Anon = class extends pdf_document_class_1.PDFDocument {
79
- constructor() {
80
- super(config);
81
- const om = this.objectManager;
82
- om.setCompress((options === null || options === void 0 ? void 0 : options.compress) !== false); // FlateDecode streams by default
83
- for (const [name, value] of Object.entries(fonts)) {
84
- if (isFontBytes(value)) {
85
- om.registerCustomFont(name, Buffer.from(value));
86
- }
87
- else {
88
- om.registerCustomFont(name, Buffer.from(value.normal), pdf_object_manager_1.FontStyle.Normal);
89
- if (value.bold)
90
- om.registerCustomFont(name, Buffer.from(value.bold), pdf_object_manager_1.FontStyle.Bold);
91
- if (value.italic)
92
- om.registerCustomFont(name, Buffer.from(value.italic), pdf_object_manager_1.FontStyle.Italic);
93
- if (value.boldItalic)
94
- om.registerCustomFont(name, Buffer.from(value.boldItalic), pdf_object_manager_1.FontStyle.BoldItalic);
95
- }
107
+ export async function renderPdf(doc, options) {
108
+ const meta = docMeta.get(doc);
109
+ const config = {
110
+ ...(meta ? { metaData: { title: meta.title, author: meta.author, keywords: [] } } : {}),
111
+ ...(options?.standardFonts === false ? { registerStandardFonts: false } : {}),
112
+ };
113
+ // Fonts registered via doc.addFont(...) plus any passed in options (options win on a name clash).
114
+ const registered = docFonts.get(doc);
115
+ const fonts = {
116
+ ...Object.fromEntries(registered ?? []),
117
+ ...options?.fonts,
118
+ };
119
+ const attachments = options?.attachments ?? [];
120
+ // A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
121
+ // font registration + config handling (the constructor does both). Custom fonts are
122
+ // registered here, before layout/render, so both the metrics and the backend see them.
123
+ const Anon = class extends PDFDocument {
124
+ constructor() {
125
+ super(config);
126
+ const om = this.objectManager;
127
+ om.setCompress(options?.compress !== false); // FlateDecode streams by default
128
+ om.setOverflowPolicy(options?.onOverflow ?? "error");
129
+ for (const [name, value] of Object.entries(fonts)) {
130
+ if (isFontBytes(value)) {
131
+ om.registerCustomFont(name, value);
96
132
  }
97
- for (const a of attachments) {
98
- om.attachFile(a.name, Buffer.from(a.data), {
99
- relationship: a.relationship,
100
- mimeType: a.mimeType,
101
- description: a.description,
102
- });
133
+ else {
134
+ om.registerCustomFont(name, value.normal, FontStyle.Normal);
135
+ if (value.bold)
136
+ om.registerCustomFont(name, value.bold, FontStyle.Bold);
137
+ if (value.italic)
138
+ om.registerCustomFont(name, value.italic, FontStyle.Italic);
139
+ if (value.boldItalic)
140
+ om.registerCustomFont(name, value.boldItalic, FontStyle.BoldItalic);
103
141
  }
104
- if (options === null || options === void 0 ? void 0 : options.xmp)
105
- om.setXmpMetadata(options.xmp);
106
- if (options === null || options === void 0 ? void 0 : options.outputIntent)
107
- om.setOutputIntent(Buffer.from(options.outputIntent));
108
- if (options === null || options === void 0 ? void 0 : options.pdfVersion)
109
- om.setPdfVersion(options.pdfVersion);
110
- if (options === null || options === void 0 ? void 0 : options.documentId)
111
- om.enableDocumentId();
112
142
  }
113
- build() {
114
- return doc;
143
+ for (const a of attachments) {
144
+ om.attachFile(a.name, a.data, {
145
+ relationship: a.relationship,
146
+ mimeType: a.mimeType,
147
+ description: a.description,
148
+ });
115
149
  }
116
- };
117
- return Anon.render();
118
- });
150
+ if (options?.xmp)
151
+ om.setXmpMetadata(options.xmp);
152
+ if (options?.outputIntent)
153
+ om.setOutputIntent(options.outputIntent);
154
+ if (options?.pdfVersion)
155
+ om.setPdfVersion(options.pdfVersion);
156
+ if (options?.documentId)
157
+ om.enableDocumentId();
158
+ }
159
+ build() {
160
+ return doc;
161
+ }
162
+ };
163
+ return Anon.render();
119
164
  }
120
165
  /** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
121
- function renderToBytes(doc, options) {
122
- return __awaiter(this, void 0, void 0, function* () {
123
- return new Uint8Array((0, utf8_to_windows1252_encoder_1.getArrayBuffer)(yield renderPdf(doc, options)));
124
- });
166
+ export async function renderToBytes(doc, options) {
167
+ // Plain ArrayBuffer-backed, so the bytes drop straight into a Blob/Response (BlobPart needs <ArrayBuffer>).
168
+ return new Uint8Array(getArrayBuffer(await renderPdf(doc, options)));
125
169
  }
@@ -1,6 +1,6 @@
1
- import { PDFElement } from "../elements/pdf-element";
2
- import { Insets } from "./insets";
3
- import { ColorInput } from "./color";
1
+ import { PDFElement } from "../elements/pdf-element.js";
2
+ import { Insets } from "./insets.js";
3
+ import { ColorInput } from "./color.js";
4
4
  /**
5
5
  * A column width: a `number` of points (fixed), an `"Nfr"` fraction (splits the leftover
6
6
  * width), or `"auto"` (as wide as the widest cell in that column - measured at layout time).
@@ -21,11 +21,11 @@ export interface TableOptions {
21
21
  colGap?: number;
22
22
  /** Padding inside every cell (default none). */
23
23
  cellPadding?: Insets;
24
- /** Grid line colour around every cell. Draws the complete grid once - don't add your own
24
+ /** Grid line color around every cell. Draws the complete grid once - don't add your own
25
25
  * frame Box, or the outer edges double up. */
26
26
  cellBorder?: ColorInput;
27
27
  /** A single thin horizontal rule under the header row and along the foot of the table (e.g. a
28
- * light-grey separator). Independent of `cellBorder`, which draws the full grid. */
28
+ * light-gray separator). Independent of `cellBorder`, which draws the full grid. */
29
29
  rule?: ColorInput;
30
30
  }
31
31
  /**
package/dist/api/table.js CHANGED
@@ -1,13 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Table = Table;
4
- const box_constraints_1 = require("../layout/box-constraints");
5
- const repeating_header_element_1 = require("../elements/layout/repeating-header-element");
6
- const deferred_element_1 = require("../elements/layout/deferred-element");
7
- const layout_1 = require("./layout");
8
- const text_1 = require("./text");
1
+ import { BoxConstraints } from "../layout/box-constraints.js";
2
+ import { RepeatingHeaderElement } from "../elements/layout/repeating-header-element.js";
3
+ import { DeferredElement } from "../elements/layout/deferred-element.js";
4
+ import { Column, Row, Box, Expanded, Padding } from "./layout.js";
5
+ import { Text } from "./text.js";
9
6
  function asElement(c) {
10
- return typeof c === "string" ? (0, text_1.Text)(c) : c;
7
+ return typeof c === "string" ? Text(c) : c;
11
8
  }
12
9
  /** `"2fr"` → 2, `"fr"` → 1; not a fraction → null. */
13
10
  function fractionOf(col) {
@@ -18,8 +15,8 @@ function fractionOf(col) {
18
15
  }
19
16
  /** The cell's unbounded (max-content) width - what an `"auto"` column sizes to. */
20
17
  function naturalWidth(cell, cellPadding, ctx) {
21
- const el = cellPadding !== undefined ? (0, layout_1.Padding)(cellPadding, asElement(cell)) : asElement(cell);
22
- return el.calculateLayout(box_constraints_1.BoxConstraints.loose(Infinity, Infinity), { x: 0, y: 0 }, ctx).width;
18
+ const el = cellPadding !== undefined ? Padding(cellPadding, asElement(cell)) : asElement(cell);
19
+ return el.calculateLayout(BoxConstraints.loose(Infinity, Infinity), { x: 0, y: 0 }, ctx).width;
23
20
  }
24
21
  /** Replaces every `"auto"` column with the widest cell width measured down that column. */
25
22
  function resolveAutoColumns(opts, rows, ctx) {
@@ -40,37 +37,44 @@ function resolveAutoColumns(opts, rows, ctx) {
40
37
  }
41
38
  /** Builds the table tree for already-resolved column widths (no `"auto"`). */
42
39
  function composeTable(opts, rows, columns) {
43
- var _a, _b, _c, _d;
44
40
  const { cellPadding } = opts;
45
- const colGap = (_b = (_a = opts.colGap) !== null && _a !== void 0 ? _a : opts.gap) !== null && _b !== void 0 ? _b : 0;
46
- const rowGap = (_d = (_c = opts.rowGap) !== null && _c !== void 0 ? _c : opts.gap) !== null && _d !== void 0 ? _d : 0;
41
+ const colGap = opts.colGap ?? opts.gap ?? 0;
42
+ const rowGap = opts.rowGap ?? opts.gap ?? 0;
47
43
  const cb = opts.cellBorder;
48
44
  const rule = opts.rule;
49
45
  // The complete grid, once: every cell bottom+right, first row also top, first col also left.
50
46
  const borderFor = (firstRow, firstCol) => cb === undefined
51
47
  ? {}
52
- : Object.assign(Object.assign({ borderBottom: cb, borderRight: cb }, (firstRow ? { borderTop: cb } : {})), (firstCol ? { borderLeft: cb } : {}));
48
+ : {
49
+ borderBottom: cb,
50
+ borderRight: cb,
51
+ ...(firstRow ? { borderTop: cb } : {}),
52
+ ...(firstCol ? { borderLeft: cb } : {}),
53
+ };
53
54
  // `ruled` rows get just a bottom line (the header underline / the table foot), without the full grid.
54
55
  const wrap = (cell, col, firstRow, firstCol, ruled) => {
55
- const inner = cellPadding !== undefined ? (0, layout_1.Padding)(cellPadding, asElement(cell)) : asElement(cell);
56
+ const inner = cellPadding !== undefined ? Padding(cellPadding, asElement(cell)) : asElement(cell);
56
57
  // The border lives on the wrapper, which stretches to the row height (crisp lines).
57
- const border = Object.assign(Object.assign({}, borderFor(firstRow, firstCol)), (ruled && rule !== undefined ? { borderBottom: rule } : {}));
58
+ const border = {
59
+ ...borderFor(firstRow, firstCol),
60
+ ...(ruled && rule !== undefined ? { borderBottom: rule } : {}),
61
+ };
58
62
  const boxed = cb !== undefined || (ruled && rule !== undefined);
59
63
  if (typeof col === "number")
60
- return (0, layout_1.Box)(Object.assign({ width: col }, border), [inner]);
64
+ return Box({ width: col, ...border }, [inner]);
61
65
  const fr = fractionOf(col);
62
66
  if (fr !== null)
63
- return (0, layout_1.Expanded)({ flex: fr }, boxed ? (0, layout_1.Box)(Object.assign({}, border), [inner]) : inner);
67
+ return Expanded({ flex: fr }, boxed ? Box({ ...border }, [inner]) : inner);
64
68
  throw new Error(`Unsupported column width "${col}" - use a number of points or "<n>fr"`);
65
69
  };
66
70
  // align:stretch → equal-height cells, so a wrapping cell keeps the row's bottom rule straight.
67
- const buildRow = (cells, firstRow, ruled = false) => (0, layout_1.Row)({ gap: colGap, align: "stretch" }, cells.map((cell, i) => { var _a; return wrap(cell, (_a = columns[i]) !== null && _a !== void 0 ? _a : "1fr", firstRow, i === 0, ruled); }));
71
+ const buildRow = (cells, firstRow, ruled = false) => Row({ gap: colGap, align: "stretch" }, cells.map((cell, i) => wrap(cell, columns[i] ?? "1fr", firstRow, i === 0, ruled)));
68
72
  // The first row (which gets the top border) is the header if present, else body row 0.
69
73
  // `rule` underlines the header and the last body row (the table foot).
70
74
  const last = rows.length - 1;
71
- const body = (0, layout_1.Column)({ gap: rowGap }, rows.map((cells, idx) => buildRow(cells, !opts.header && idx === 0, rule !== undefined && idx === last)));
75
+ const body = Column({ gap: rowGap }, rows.map((cells, idx) => buildRow(cells, !opts.header && idx === 0, rule !== undefined && idx === last)));
72
76
  return opts.header
73
- ? new repeating_header_element_1.RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined), body, rowGap)
77
+ ? new RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined), body, rowGap)
74
78
  : body;
75
79
  }
76
80
  /**
@@ -79,9 +83,9 @@ function composeTable(opts, rows, columns) {
79
83
  * paginates at row boundaries for free (a row that doesn't fit moves whole). `"auto"`
80
84
  * columns are resolved at layout time (a `DeferredElement`), once metrics are available.
81
85
  */
82
- function Table(opts, rows) {
86
+ export function Table(opts, rows) {
83
87
  if (opts.columns.some((c) => c === "auto")) {
84
- return new deferred_element_1.DeferredElement((ctx) => composeTable(opts, rows, resolveAutoColumns(opts, rows, ctx)));
88
+ return new DeferredElement((ctx) => composeTable(opts, rows, resolveAutoColumns(opts, rows, ctx)));
85
89
  }
86
90
  return composeTable(opts, rows, opts.columns);
87
91
  }