@jasy/pdf 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +21 -0
- package/README.md +24 -14
- package/dist/api/args.d.ts +1 -1
- package/dist/api/args.js +2 -5
- package/dist/api/color.d.ts +4 -4
- package/dist/api/color.js +11 -17
- package/dist/api/content.d.ts +8 -8
- package/dist/api/content.js +23 -24
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +75 -31
- package/dist/api/index.d.ts +8 -8
- package/dist/api/index.js +8 -24
- package/dist/api/insets.js +4 -8
- package/dist/api/layout.d.ts +18 -16
- package/dist/api/layout.js +41 -52
- package/dist/api/structure.d.ts +65 -13
- package/dist/api/structure.js +140 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +28 -24
- package/dist/api/text.d.ts +27 -2
- package/dist/api/text.js +45 -27
- package/dist/assets/font-data.d.ts +2 -0
- package/dist/assets/font-data.js +21 -0
- package/dist/assets/font-data.ts +37 -0
- package/dist/common/color.js +1 -5
- package/dist/constants/page-sizes.js +3 -6
- package/dist/constants/pdf-parts.js +1 -4
- package/dist/crypto/security-handler.d.ts +46 -0
- package/dist/crypto/security-handler.js +129 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/elements/container-element.d.ts +4 -4
- package/dist/elements/container-element.js +9 -13
- package/dist/elements/image-element.d.ts +18 -2
- package/dist/elements/image-element.js +81 -105
- package/dist/elements/index.d.ts +12 -11
- package/dist/elements/index.js +12 -29
- package/dist/elements/layout/default-text-style-element.d.ts +30 -0
- package/dist/elements/layout/default-text-style-element.js +47 -0
- package/dist/elements/layout/deferred-element.d.ts +3 -3
- package/dist/elements/layout/deferred-element.js +4 -8
- package/dist/elements/layout/expanded-element.d.ts +3 -3
- package/dist/elements/layout/expanded-element.js +10 -14
- package/dist/elements/layout/padding-element.d.ts +3 -3
- package/dist/elements/layout/padding-element.js +9 -14
- package/dist/elements/layout/positioned-element.d.ts +17 -4
- package/dist/elements/layout/positioned-element.js +29 -25
- package/dist/elements/layout/repeating-header-element.d.ts +3 -3
- package/dist/elements/layout/repeating-header-element.js +8 -12
- package/dist/elements/layout/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +8 -6
- package/dist/elements/page-element.js +20 -23
- package/dist/elements/pdf-document-element.d.ts +10 -4
- package/dist/elements/pdf-document-element.js +11 -10
- package/dist/elements/pdf-element.d.ts +12 -3
- package/dist/elements/pdf-element.js +10 -19
- package/dist/elements/rectangle-element.d.ts +5 -5
- package/dist/elements/rectangle-element.js +19 -25
- package/dist/elements/row-element.d.ts +3 -3
- package/dist/elements/row-element.js +7 -11
- package/dist/elements/text-element.d.ts +37 -11
- package/dist/elements/text-element.js +64 -39
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +4 -2
- package/dist/ir/display-list.js +1 -2
- package/dist/layout/box-constraints.js +2 -6
- package/dist/layout/fragmentation.d.ts +8 -1
- package/dist/layout/fragmentation.js +22 -10
- package/dist/platform/browser-fs.d.ts +2 -0
- package/dist/platform/browser-fs.js +9 -0
- package/dist/platform/browser-image.d.ts +5 -0
- package/dist/platform/browser-image.js +13 -0
- package/dist/platform/node-fs.d.ts +2 -0
- package/dist/platform/node-fs.js +10 -0
- package/dist/platform/node-image.d.ts +5 -0
- package/dist/platform/node-image.js +9 -0
- package/dist/renderer/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +12 -27
- package/dist/renderer/default-text-style-renderer.d.ts +6 -0
- package/dist/renderer/default-text-style-renderer.js +10 -0
- package/dist/renderer/deferred-renderer.d.ts +3 -3
- package/dist/renderer/deferred-renderer.js +8 -23
- package/dist/renderer/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/image-renderer.d.ts +3 -3
- package/dist/renderer/image-renderer.js +77 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +61 -77
- package/dist/renderer/pdf-backend.d.ts +2 -2
- package/dist/renderer/pdf-backend.js +21 -19
- package/dist/renderer/pdf-config.js +4 -7
- package/dist/renderer/pdf-document-class.d.ts +5 -5
- package/dist/renderer/pdf-document-class.js +24 -41
- package/dist/renderer/pdf-document-renderer.d.ts +3 -3
- package/dist/renderer/pdf-document-renderer.js +71 -85
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +85 -93
- package/dist/renderer/positioned-renderer.d.ts +3 -3
- package/dist/renderer/positioned-renderer.js +8 -23
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +45 -52
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +11 -26
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +12 -27
- package/dist/renderer/text-renderer.d.ts +6 -5
- package/dist/renderer/text-renderer.js +33 -42
- package/dist/text/line-breaker.d.ts +8 -5
- package/dist/text/line-breaker.js +67 -16
- package/dist/text/text-style.d.ts +25 -0
- package/dist/text/text-style.js +29 -0
- package/dist/utils/afm-parser.js +3 -13
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/flex-layout.d.ts +2 -2
- package/dist/utils/flex-layout.js +15 -20
- package/dist/utils/font-metrics.d.ts +1 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +79 -0
- package/dist/utils/pdf-object-manager.d.ts +18 -6
- package/dist/utils/pdf-object-manager.js +0 -0
- package/dist/utils/renderer-registry.js +1 -5
- package/dist/utils/ttf-parser.d.ts +2 -2
- package/dist/utils/ttf-parser.js +32 -36
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/utf8-to-windows1252-encoder.js +1 -4
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +9 -13
- package/package.json +14 -2
package/dist/api/layout.js
CHANGED
|
@@ -1,73 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const container_element_1 = require("../elements/container-element");
|
|
11
|
-
const row_element_1 = require("../elements/row-element");
|
|
12
|
-
const rectangle_element_1 = require("../elements/rectangle-element");
|
|
13
|
-
const expanded_element_1 = require("../elements/layout/expanded-element");
|
|
14
|
-
const padding_element_1 = require("../elements/layout/padding-element");
|
|
15
|
-
const positioned_element_1 = require("../elements/layout/positioned-element");
|
|
16
|
-
const color_1 = require("./color");
|
|
17
|
-
const insets_1 = require("./insets");
|
|
18
|
-
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";
|
|
19
10
|
// The public default for `cross` is `start` (locked §5) - i.e. don't stretch a child unless
|
|
20
11
|
// asked. The engine default is `stretch`; the factory sets the friendlier public one.
|
|
21
12
|
const DEFAULT_CROSS = "start";
|
|
22
|
-
function Column(a, b) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return new container_element_1.ContainerElement({
|
|
13
|
+
export function Column(a, b) {
|
|
14
|
+
const { opts, children } = splitArgs(a, b);
|
|
15
|
+
return new ContainerElement({
|
|
26
16
|
x: 0,
|
|
27
17
|
y: 0,
|
|
28
18
|
children,
|
|
29
19
|
gap: opts.gap,
|
|
30
20
|
main: opts.justify, // undefined → engine default `start` (matches §5)
|
|
31
|
-
cross:
|
|
21
|
+
cross: opts.align ?? DEFAULT_CROSS,
|
|
32
22
|
});
|
|
33
23
|
}
|
|
34
|
-
function Row(a, b) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return new row_element_1.RowElement({
|
|
24
|
+
export function Row(a, b) {
|
|
25
|
+
const { opts, children } = splitArgs(a, b);
|
|
26
|
+
return new RowElement({
|
|
38
27
|
children,
|
|
39
28
|
gap: opts.gap,
|
|
40
29
|
main: opts.justify,
|
|
41
|
-
cross:
|
|
30
|
+
cross: opts.align ?? DEFAULT_CROSS,
|
|
42
31
|
});
|
|
43
32
|
}
|
|
44
|
-
function Box(a, b) {
|
|
45
|
-
|
|
46
|
-
const { opts, children } = (0, args_1.splitArgs)(a, b);
|
|
33
|
+
export function Box(a, b) {
|
|
34
|
+
const { opts, children } = splitArgs(a, b);
|
|
47
35
|
const content = opts.padding !== undefined && children.length > 0
|
|
48
36
|
? [
|
|
49
|
-
new
|
|
50
|
-
margin:
|
|
37
|
+
new PaddingElement({
|
|
38
|
+
margin: toEdges(opts.padding),
|
|
51
39
|
child: children.length === 1 ? children[0] : Column(children),
|
|
52
40
|
}),
|
|
53
41
|
]
|
|
54
42
|
: children;
|
|
55
43
|
// A side is set if it (or the uniform `border`) is given. If ANY differs from a plain
|
|
56
|
-
// uniform border, we hand the engine per-side
|
|
44
|
+
// uniform border, we hand the engine per-side colors (which draws individual lines).
|
|
57
45
|
const sideKeys = [opts.borderTop, opts.borderRight, opts.borderBottom, opts.borderLeft];
|
|
58
46
|
const hasPerSide = sideKeys.some((s) => s !== undefined);
|
|
59
47
|
const side = (s) => {
|
|
60
|
-
const c = s
|
|
61
|
-
return c !== undefined ?
|
|
48
|
+
const c = s ?? opts.border;
|
|
49
|
+
return c !== undefined ? toColor(c) : undefined;
|
|
62
50
|
};
|
|
63
51
|
const hasBorder = opts.border !== undefined || opts.borderWidth !== undefined || hasPerSide;
|
|
64
|
-
return new
|
|
52
|
+
return new RectangleElement({
|
|
65
53
|
x: 0,
|
|
66
54
|
y: 0,
|
|
67
55
|
children: content,
|
|
68
|
-
color: opts.border !== undefined ?
|
|
69
|
-
backgroundColor: opts.bg !== undefined ?
|
|
70
|
-
borderWidth: hasBorder ? (
|
|
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,
|
|
71
59
|
width: opts.width,
|
|
72
60
|
height: opts.height,
|
|
73
61
|
radius: opts.radius,
|
|
@@ -84,28 +72,29 @@ function Box(a, b) {
|
|
|
84
72
|
});
|
|
85
73
|
}
|
|
86
74
|
/** Insets a single child by `padding` (a number / `{x,y}` / `{top,…}` / 4-tuple). */
|
|
87
|
-
function Padding(padding, child) {
|
|
88
|
-
return new
|
|
75
|
+
export function Padding(padding, child) {
|
|
76
|
+
return new PaddingElement({ margin: toEdges(padding), child });
|
|
89
77
|
}
|
|
90
78
|
/**
|
|
91
|
-
* Places a child OUT OF FLOW, relative to the nearest enclosing `relative` Box.
|
|
92
|
-
*
|
|
93
|
-
*
|
|
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.
|
|
94
84
|
*/
|
|
95
|
-
function Positioned(opts, child) {
|
|
96
|
-
return new
|
|
85
|
+
export function Positioned(opts, child) {
|
|
86
|
+
return new PositionedElement({ child, ...opts });
|
|
97
87
|
}
|
|
98
88
|
/**
|
|
99
89
|
* A flexible empty gap that pushes its siblings apart - `Row([a, Spacer(), b])` sends `a`
|
|
100
90
|
* and `b` to the edges. `flex` weights it against other flex children (default 1).
|
|
101
91
|
*/
|
|
102
|
-
function Spacer(flex = 1) {
|
|
103
|
-
return new
|
|
92
|
+
export function Spacer(flex = 1) {
|
|
93
|
+
return new ExpandedElement({ flex, child: Column([]) });
|
|
104
94
|
}
|
|
105
|
-
function Expanded(a, b) {
|
|
106
|
-
var _a;
|
|
95
|
+
export function Expanded(a, b) {
|
|
107
96
|
const isOptsForm = b !== undefined;
|
|
108
97
|
const opts = (isOptsForm ? a : {});
|
|
109
98
|
const child = (isOptsForm ? b : a);
|
|
110
|
-
return new
|
|
99
|
+
return new ExpandedElement({ flex: opts.flex ?? 1, child });
|
|
111
100
|
}
|
package/dist/api/structure.d.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import { PDFDocumentElement } from "../elements/pdf-document-element";
|
|
2
|
-
import { PageElement } from "../elements/page-element";
|
|
3
|
-
import { PDFElement } from "../elements/pdf-element";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
export type
|
|
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 { type EncryptOptions } from "../crypto/security-handler.js";
|
|
8
|
+
export type { EncryptOptions, Permissions } from "../crypto/security-handler.js";
|
|
9
|
+
import { StackOptions } from "./layout.js";
|
|
10
|
+
import { Insets } from "./insets.js";
|
|
11
|
+
import { TextDefaults } from "./text.js";
|
|
12
|
+
/** A custom page size. Use the `mm()` helper for millimetres, or pass points directly. */
|
|
13
|
+
export interface CustomSize {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
unit?: "pt" | "mm";
|
|
17
|
+
}
|
|
18
|
+
/** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
|
|
19
|
+
export declare function mm(width: number, height: number): CustomSize;
|
|
20
|
+
/** A page size: a named `PageSize`/string (`"A4"`, `"letter"`, any case), or a `CustomSize`. */
|
|
21
|
+
export type PageSizeInput = PageSize | string | CustomSize;
|
|
9
22
|
export interface PageOptions extends StackOptions {
|
|
10
23
|
/** Page size (default A4). */
|
|
11
24
|
size?: PageSizeInput;
|
|
@@ -24,17 +37,50 @@ export interface PageOptions extends StackOptions {
|
|
|
24
37
|
*/
|
|
25
38
|
export declare function Page(children: PDFElement[]): PageElement;
|
|
26
39
|
export declare function Page(opts: PageOptions, children: PDFElement[]): PageElement;
|
|
27
|
-
|
|
40
|
+
/** Document options: PDF metadata plus the inheritable text defaults (font, size, color, lineHeight,
|
|
41
|
+
* align, bold/italic) every `Text` inherits unless it sets its own - Flutter's `DefaultTextStyle`. */
|
|
42
|
+
export interface DocumentOptions extends TextDefaults {
|
|
28
43
|
/** PDF metadata. */
|
|
29
44
|
meta?: {
|
|
30
45
|
title?: string;
|
|
31
46
|
author?: string;
|
|
32
47
|
};
|
|
33
48
|
}
|
|
49
|
+
/** A single font file for `addFont`: a `.ttf` path (read on Node) or its raw bytes (e.g. a browser
|
|
50
|
+
* upload). */
|
|
51
|
+
export type FontFileSource = string | Uint8Array;
|
|
52
|
+
/** A styled font family for `addFont`: one file per style, only `normal` required. `Text({ bold,
|
|
53
|
+
* italic })` then picks the right face, falling back to `normal`. */
|
|
54
|
+
export interface FontFamilyInput {
|
|
55
|
+
normal: FontFileSource;
|
|
56
|
+
bold?: FontFileSource;
|
|
57
|
+
italic?: FontFileSource;
|
|
58
|
+
boldItalic?: FontFileSource;
|
|
59
|
+
}
|
|
60
|
+
/** What `addFont` accepts: one file (path or bytes), or a styled family. */
|
|
61
|
+
export type FontSource = FontFileSource | FontFamilyInput;
|
|
62
|
+
/** The object the `Document(...)` factory returns: the element tree plus a managed font registry. */
|
|
63
|
+
export interface JasyDocument extends PDFDocumentElement {
|
|
64
|
+
/** Register a font under `name`, then use it via `Text({ font: name })`. The source is a `.ttf`
|
|
65
|
+
* path (read now, on Node), raw bytes, or a styled family. Re-adding a name overwrites it.
|
|
66
|
+
* A registered font that no `Text` actually uses is dropped at render and costs nothing. */
|
|
67
|
+
addFont(name: string, source: FontSource): this;
|
|
68
|
+
/** The names of the registered fonts. */
|
|
69
|
+
getFonts(): string[];
|
|
70
|
+
/** Whether a font is registered under `name`. */
|
|
71
|
+
hasFont(name: string): boolean;
|
|
72
|
+
}
|
|
34
73
|
/** The document root. `Document(pages)` or `Document(opts, pages)`. */
|
|
35
|
-
export declare function Document(pages: PageElement[]):
|
|
36
|
-
export declare function Document(opts: DocumentOptions, pages: PageElement[]):
|
|
37
|
-
|
|
74
|
+
export declare function Document(pages: PageElement[]): JasyDocument;
|
|
75
|
+
export declare function Document(opts: DocumentOptions, pages: PageElement[]): JasyDocument;
|
|
76
|
+
/**
|
|
77
|
+
* Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
|
|
78
|
+
* Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
|
|
79
|
+
* inherit them unless they set their own, and they layer onto whatever is inherited from above.
|
|
80
|
+
* `DefaultTextStyle(opts, children)`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function DefaultTextStyle(opts: TextDefaults, children: PDFElement[]): DefaultTextStyleElement;
|
|
83
|
+
export type FontBytes = Uint8Array;
|
|
38
84
|
/** A font family: one `.ttf` per style. Only `normal` is required; `bold`/`italic`/`boldItalic`
|
|
39
85
|
* are picked up automatically by `Text({ bold, italic })`, falling back to `normal` if absent. */
|
|
40
86
|
export interface FontFamily {
|
|
@@ -73,8 +119,14 @@ export interface RenderOptions {
|
|
|
73
119
|
standardFonts?: boolean;
|
|
74
120
|
/** FlateDecode-compress the streams (default true). Set false for a greppable, uncompressed PDF. */
|
|
75
121
|
compress?: boolean;
|
|
122
|
+
/** What to do when content is taller than a page and cannot break: `"error"` throws (default),
|
|
123
|
+
* `"warn"` logs and clips, `"ignore"` clips silently. It is always clipped either way. */
|
|
124
|
+
onOverflow?: OverflowPolicy;
|
|
125
|
+
/** Encrypt the PDF with a password (AES-256, the newest standard). NOT compatible with PDF/A
|
|
126
|
+
* (ZUGFeRD invoices) - encrypting one throws, since PDF/A forbids encryption. */
|
|
127
|
+
encrypt?: EncryptOptions;
|
|
76
128
|
}
|
|
77
129
|
/** Renders a `Document(...)` tree to the raw PDF string. */
|
|
78
130
|
export declare function renderPdf(doc: PDFDocumentElement, options?: RenderOptions): Promise<string>;
|
|
79
131
|
/** 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
|
|
132
|
+
export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array<ArrayBuffer>>;
|
package/dist/api/structure.js
CHANGED
|
@@ -1,125 +1,177 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
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 { createSecurityHandler } from "../crypto/security-handler.js";
|
|
11
|
+
import { Column } from "./layout.js";
|
|
12
|
+
import { toEdges } from "./insets.js";
|
|
13
|
+
import { toTextStyleOverride } from "./text.js";
|
|
14
|
+
const MM_TO_PT = 72 / 25.4; // 1 mm in PDF points
|
|
15
|
+
/** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
|
|
16
|
+
export function mm(width, height) {
|
|
17
|
+
return { width, height, unit: "mm" };
|
|
18
|
+
}
|
|
19
|
+
const PAGE_SIZE_VALUES = new Set(Object.values(PageSize));
|
|
26
20
|
function toPageSize(input) {
|
|
27
21
|
const v = String(input).toLowerCase();
|
|
28
22
|
if (!PAGE_SIZE_VALUES.has(v))
|
|
29
23
|
throw new Error(`Unknown page size: "${input}"`);
|
|
30
24
|
return v;
|
|
31
25
|
}
|
|
26
|
+
/** Resolve `size` into config fields: a named `pageSize`, or an explicit [width, height] in points. */
|
|
27
|
+
function resolveSize(input) {
|
|
28
|
+
if (input !== undefined && typeof input === "object") {
|
|
29
|
+
const f = input.unit === "mm" ? MM_TO_PT : 1;
|
|
30
|
+
return { pageSize: PageSize.A4, customSize: [input.width * f, input.height * f] };
|
|
31
|
+
}
|
|
32
|
+
return { pageSize: input !== undefined ? toPageSize(input) : PageSize.A4 };
|
|
33
|
+
}
|
|
32
34
|
/** Default page margin (all sides, points) when a `Page` doesn't set one. */
|
|
33
35
|
const DEFAULT_MARGIN = 56;
|
|
34
|
-
function Page(a, b) {
|
|
35
|
-
var _a;
|
|
36
|
+
export function Page(a, b) {
|
|
36
37
|
const isOpts = !Array.isArray(a);
|
|
37
38
|
const opts = (isOpts ? a : {});
|
|
38
|
-
const children = (isOpts ? (b
|
|
39
|
-
const [top, right, bottom, left] =
|
|
39
|
+
const children = (isOpts ? (b ?? []) : a);
|
|
40
|
+
const [top, right, bottom, left] = toEdges(opts.margin ?? DEFAULT_MARGIN);
|
|
40
41
|
const config = {
|
|
41
|
-
|
|
42
|
-
orientation: opts.orientation === "landscape" ?
|
|
42
|
+
...resolveSize(opts.size),
|
|
43
|
+
orientation: opts.orientation === "landscape" ? Orientation.landscape : Orientation.portrait,
|
|
43
44
|
margin: { top, right, bottom, left },
|
|
44
45
|
};
|
|
45
|
-
return new
|
|
46
|
+
return new PageElement({
|
|
46
47
|
config,
|
|
47
48
|
header: opts.header,
|
|
48
49
|
footer: opts.footer,
|
|
49
|
-
children: [
|
|
50
|
+
children: [Column({ gap: opts.gap, justify: opts.justify, align: opts.align }, children)],
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
// Document metadata is a document-render concern, not part of the element tree, so it is
|
|
53
54
|
// kept beside the returned element and picked up by `renderPdf`.
|
|
54
55
|
const docMeta = new WeakMap();
|
|
55
|
-
|
|
56
|
+
// Fonts registered via doc.addFont(...), kept beside the element (like meta) and registered on the
|
|
57
|
+
// object manager at render time. Path sources are read to bytes in addFont, so the map holds bytes.
|
|
58
|
+
const docFonts = new WeakMap();
|
|
59
|
+
/** Reads any path sources to bytes, leaving bytes / families as-is. */
|
|
60
|
+
function resolveFontSource(source) {
|
|
61
|
+
const read = (s) => (typeof s === "string" ? readFileBytes(s) : s);
|
|
62
|
+
if (typeof source === "string" || source instanceof Uint8Array) {
|
|
63
|
+
return read(source);
|
|
64
|
+
}
|
|
65
|
+
const family = { normal: read(source.normal) };
|
|
66
|
+
if (source.bold)
|
|
67
|
+
family.bold = read(source.bold);
|
|
68
|
+
if (source.italic)
|
|
69
|
+
family.italic = read(source.italic);
|
|
70
|
+
if (source.boldItalic)
|
|
71
|
+
family.boldItalic = read(source.boldItalic);
|
|
72
|
+
return family;
|
|
73
|
+
}
|
|
74
|
+
export function Document(a, b) {
|
|
56
75
|
const isOpts = !Array.isArray(a);
|
|
57
76
|
const opts = (isOpts ? a : {});
|
|
58
|
-
const pages = (isOpts ? (b
|
|
59
|
-
const doc = new
|
|
77
|
+
const pages = (isOpts ? (b ?? []) : a);
|
|
78
|
+
const doc = new PDFDocumentElement({
|
|
79
|
+
children: pages,
|
|
80
|
+
defaultTextStyle: toTextStyleOverride(opts),
|
|
81
|
+
});
|
|
60
82
|
if (opts.meta)
|
|
61
83
|
docMeta.set(doc, opts.meta);
|
|
84
|
+
// Managed font registry: addFont registers, getFonts/hasFont query, render reads it (below).
|
|
85
|
+
const registry = new Map();
|
|
86
|
+
docFonts.set(doc, registry);
|
|
87
|
+
doc.addFont = (name, source) => {
|
|
88
|
+
registry.set(name, resolveFontSource(source));
|
|
89
|
+
return doc;
|
|
90
|
+
};
|
|
91
|
+
doc.getFonts = () => [...registry.keys()];
|
|
92
|
+
doc.hasFont = (name) => registry.has(name);
|
|
62
93
|
return doc;
|
|
63
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
|
|
97
|
+
* Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
|
|
98
|
+
* inherit them unless they set their own, and they layer onto whatever is inherited from above.
|
|
99
|
+
* `DefaultTextStyle(opts, children)`.
|
|
100
|
+
*/
|
|
101
|
+
export function DefaultTextStyle(opts, children) {
|
|
102
|
+
return new DefaultTextStyleElement({ style: toTextStyleOverride(opts), child: Column(children) });
|
|
103
|
+
}
|
|
64
104
|
function isFontBytes(v) {
|
|
65
|
-
return
|
|
105
|
+
return v instanceof Uint8Array;
|
|
66
106
|
}
|
|
67
107
|
/** Renders a `Document(...)` tree to the raw PDF string. */
|
|
68
|
-
function renderPdf(doc, options) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
export async function renderPdf(doc, options) {
|
|
109
|
+
const meta = docMeta.get(doc);
|
|
110
|
+
const config = {
|
|
111
|
+
...(meta ? { metaData: { title: meta.title, author: meta.author, keywords: [] } } : {}),
|
|
112
|
+
...(options?.standardFonts === false ? { registerStandardFonts: false } : {}),
|
|
113
|
+
};
|
|
114
|
+
// Fonts registered via doc.addFont(...) plus any passed in options (options win on a name clash).
|
|
115
|
+
const registered = docFonts.get(doc);
|
|
116
|
+
const fonts = {
|
|
117
|
+
...Object.fromEntries(registered ?? []),
|
|
118
|
+
...options?.fonts,
|
|
119
|
+
};
|
|
120
|
+
const attachments = options?.attachments ?? [];
|
|
121
|
+
// The security handler's key derivation is async (WebCrypto), so build it here, before the sync
|
|
122
|
+
// PDFDocument constructor wires it into the object manager.
|
|
123
|
+
const securityHandler = options?.encrypt
|
|
124
|
+
? await createSecurityHandler(options.encrypt)
|
|
125
|
+
: undefined;
|
|
126
|
+
// A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
|
|
127
|
+
// font registration + config handling (the constructor does both). Custom fonts are
|
|
128
|
+
// registered here, before layout/render, so both the metrics and the backend see them.
|
|
129
|
+
const Anon = class extends PDFDocument {
|
|
130
|
+
constructor() {
|
|
131
|
+
super(config);
|
|
132
|
+
const om = this.objectManager;
|
|
133
|
+
om.setCompress(options?.compress !== false); // FlateDecode streams by default
|
|
134
|
+
om.setOverflowPolicy(options?.onOverflow ?? "error");
|
|
135
|
+
for (const [name, value] of Object.entries(fonts)) {
|
|
136
|
+
if (isFontBytes(value)) {
|
|
137
|
+
om.registerCustomFont(name, value);
|
|
96
138
|
}
|
|
97
|
-
|
|
98
|
-
om.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
139
|
+
else {
|
|
140
|
+
om.registerCustomFont(name, value.normal, FontStyle.Normal);
|
|
141
|
+
if (value.bold)
|
|
142
|
+
om.registerCustomFont(name, value.bold, FontStyle.Bold);
|
|
143
|
+
if (value.italic)
|
|
144
|
+
om.registerCustomFont(name, value.italic, FontStyle.Italic);
|
|
145
|
+
if (value.boldItalic)
|
|
146
|
+
om.registerCustomFont(name, value.boldItalic, FontStyle.BoldItalic);
|
|
103
147
|
}
|
|
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
148
|
}
|
|
113
|
-
|
|
114
|
-
|
|
149
|
+
for (const a of attachments) {
|
|
150
|
+
om.attachFile(a.name, a.data, {
|
|
151
|
+
relationship: a.relationship,
|
|
152
|
+
mimeType: a.mimeType,
|
|
153
|
+
description: a.description,
|
|
154
|
+
});
|
|
115
155
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
156
|
+
if (options?.xmp)
|
|
157
|
+
om.setXmpMetadata(options.xmp);
|
|
158
|
+
if (options?.outputIntent)
|
|
159
|
+
om.setOutputIntent(options.outputIntent);
|
|
160
|
+
if (options?.pdfVersion)
|
|
161
|
+
om.setPdfVersion(options.pdfVersion);
|
|
162
|
+
if (options?.documentId)
|
|
163
|
+
om.enableDocumentId();
|
|
164
|
+
if (securityHandler)
|
|
165
|
+
om.setSecurityHandler(securityHandler);
|
|
166
|
+
}
|
|
167
|
+
build() {
|
|
168
|
+
return doc;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
return Anon.render();
|
|
119
172
|
}
|
|
120
173
|
/** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
|
|
121
|
-
function renderToBytes(doc, options) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
});
|
|
174
|
+
export async function renderToBytes(doc, options) {
|
|
175
|
+
// Plain ArrayBuffer-backed, so the bytes drop straight into a Blob/Response (BlobPart needs <ArrayBuffer>).
|
|
176
|
+
return new Uint8Array(getArrayBuffer(await renderPdf(doc, options)));
|
|
125
177
|
}
|
package/dist/api/table.d.ts
CHANGED
|
@@ -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
|
|
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-
|
|
28
|
+
* light-gray separator). Independent of `cellBorder`, which draws the full grid. */
|
|
29
29
|
rule?: ColorInput;
|
|
30
30
|
}
|
|
31
31
|
/**
|