takumi-pdf 0.2.0 → 0.3.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/README.md +76 -0
- package/dist/export.cjs +50 -5
- package/dist/export.d.cts +108 -2
- package/dist/export.d.mts +108 -2
- package/dist/export.mjs +50 -6
- package/package.json +3 -3
- package/pkg/takumi_pdf_wasm_bg.wasm +0 -0
- package/pkg/takumi_pdf_wasm_bg.wasm.d.ts +1 -0
package/README.md
CHANGED
|
@@ -166,10 +166,86 @@ const pdf = await render(
|
|
|
166
166
|
| `break-inside: avoid` | Keeps the element on one page when it fits. |
|
|
167
167
|
| `box-decoration-break: clone` | Repeats borders and backgrounds on every page fragment. |
|
|
168
168
|
|
|
169
|
+
## Links, outline, and metadata
|
|
170
|
+
|
|
171
|
+
Anchors with an `href` become clickable link annotations. `outline: true` builds PDF bookmarks from `h1`–`h6` headings. `metadata` fills the document properties:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
const pdf = await render(report, {
|
|
175
|
+
outline: true,
|
|
176
|
+
lang: "en",
|
|
177
|
+
metadata: {
|
|
178
|
+
title: "Annual report 2026",
|
|
179
|
+
authors: ["Acme Inc."],
|
|
180
|
+
creationDate: "2026-08-06",
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Omit `metadata` to keep output byte-identical across runs.
|
|
186
|
+
|
|
187
|
+
## Tagged output and PDF/A
|
|
188
|
+
|
|
189
|
+
Output is **tagged by default**: HTML semantics (`h1`–`h6`, `p`, `img` with `alt`, `a`, lists) become a PDF structure tree, like Chromium's print-to-PDF. Set `tagged: "ua1"` to validate against PDF/UA-1, or `tagged: false` to drop the tree when file size matters more than accessibility.
|
|
190
|
+
|
|
191
|
+
`pdfa` renders archival output. Validation runs during rendering. A document that cannot conform fails with the violated rule instead of writing a broken file. Every level, and PDF/UA-1, passes [veraPDF](https://verapdf.org).
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
const pdf = await render(report, {
|
|
195
|
+
pdfa: "2a",
|
|
196
|
+
tagged: "ua1",
|
|
197
|
+
lang: "en",
|
|
198
|
+
metadata: { title: "Annual report", creationDate: "2026-08-06" },
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Level | What it adds |
|
|
203
|
+
| ------------------------ | ------------------------------------- |
|
|
204
|
+
| `"2b"` / `"2u"` | Basic conformance / Unicode mapping. |
|
|
205
|
+
| `"2a"` / `"3a"` | A tagged structure tree. |
|
|
206
|
+
| `"3b"` / `"3u"` / `"3a"` | Arbitrary file attachments. |
|
|
207
|
+
| `"4"` | The PDF 2.0 revision of the standard. |
|
|
208
|
+
|
|
209
|
+
Invalid combinations are **TypeScript type errors**. See the [PDF/A docs](https://takumi.kane.tw/docs/pdf/pdf-a) for the structure-tree mapping and required metadata.
|
|
210
|
+
|
|
211
|
+
## Attachments
|
|
212
|
+
|
|
213
|
+
Attach files with `attachments`. They appear in the viewer's attachment panel. Combine with `pdfa: "3b"` for ZUGFeRD and Factur-X electronic invoices:
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
const pdf = await render(invoice, {
|
|
217
|
+
pdfa: "3b",
|
|
218
|
+
metadata: { title: "Invoice 1042", creationDate: "2026-08-06" },
|
|
219
|
+
attachments: [
|
|
220
|
+
{
|
|
221
|
+
name: "factur-x.xml",
|
|
222
|
+
data: xml,
|
|
223
|
+
mimeType: "application/xml",
|
|
224
|
+
description: "Factur-X invoice data",
|
|
225
|
+
relationship: "alternative",
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The PDF/A-3 levels require `mimeType`, `description`, and a modification date on each attachment. `metadata.creationDate` serves as the date fallback.
|
|
232
|
+
|
|
233
|
+
## Measuring
|
|
234
|
+
|
|
235
|
+
`measure()` lays out a tree without rendering and returns its size in CSS px. Use it to size a header or footer band before setting `margin`:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { measure } from "takumi-pdf";
|
|
239
|
+
|
|
240
|
+
const { height } = await measure(footer, { size: "a4" });
|
|
241
|
+
```
|
|
242
|
+
|
|
169
243
|
## Images and runtimes
|
|
170
244
|
|
|
171
245
|
`takumi-pdf` runs on Node.js, Bun, and Cloudflare Workers.
|
|
172
246
|
|
|
247
|
+
SVG images embed as vectors, not rasterized bitmaps.
|
|
248
|
+
|
|
173
249
|
The renderer does not fetch remote images. Pass pre-fetched bytes for image URLs in the document:
|
|
174
250
|
|
|
175
251
|
```tsx
|
package/dist/export.cjs
CHANGED
|
@@ -24,6 +24,26 @@ var PdfRenderer$1 = class {
|
|
|
24
24
|
wasm.__wbg_pdfrenderer_free(ptr, 0);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
+
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
28
|
+
* Page options lay out at the full page width, like a header/footer band;
|
|
29
|
+
* `pageNumber` / `totalPages` hooks are filled with three-digit counters.
|
|
30
|
+
* @param {Node} node
|
|
31
|
+
* @param {MeasureOptions | null} [options]
|
|
32
|
+
* @returns {MeasuredSize}
|
|
33
|
+
*/
|
|
34
|
+
measure(node, options) {
|
|
35
|
+
try {
|
|
36
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
37
|
+
wasm.pdfrenderer_measure(retptr, this.__wbg_ptr, addHeapObject(node), isLikeNone(options) ? 0 : addHeapObject(options));
|
|
38
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 0, true);
|
|
39
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4, true);
|
|
40
|
+
if (getDataViewMemory0().getInt32(retptr + 8, true)) throw takeObject(r1);
|
|
41
|
+
return takeObject(r0);
|
|
42
|
+
} finally {
|
|
43
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
27
47
|
* Creates a renderer with the bundled last-resort fonts.
|
|
28
48
|
*/
|
|
29
49
|
constructor() {
|
|
@@ -43,8 +63,8 @@ var PdfRenderer$1 = class {
|
|
|
43
63
|
/**
|
|
44
64
|
* Registers a font (raw bytes or a details object), returning the families
|
|
45
65
|
* it produced.
|
|
46
|
-
* @param {
|
|
47
|
-
* @returns {
|
|
66
|
+
* @param {Font} font
|
|
67
|
+
* @returns {RegisteredFamily[]}
|
|
48
68
|
*/
|
|
49
69
|
registerFont(font) {
|
|
50
70
|
try {
|
|
@@ -61,9 +81,9 @@ var PdfRenderer$1 = class {
|
|
|
61
81
|
/**
|
|
62
82
|
* Renders a node tree to PDF bytes. Without options the output is paged A4;
|
|
63
83
|
* `viewport` renders a single fixed page instead.
|
|
64
|
-
* @param {
|
|
65
|
-
* @param {
|
|
66
|
-
* @returns {Uint8Array}
|
|
84
|
+
* @param {Node} node
|
|
85
|
+
* @param {PdfRenderOptions | null} [options]
|
|
86
|
+
* @returns {Uint8Array<ArrayBuffer>}
|
|
67
87
|
*/
|
|
68
88
|
render(node, options) {
|
|
69
89
|
try {
|
|
@@ -513,6 +533,25 @@ var PdfRenderer = class {
|
|
|
513
533
|
fontFamilies: resources.fontFamilies
|
|
514
534
|
});
|
|
515
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
538
|
+
*
|
|
539
|
+
* With page options the tree lays out at the full page width with unbounded
|
|
540
|
+
* height, exactly how {@link render} measures a header or footer band
|
|
541
|
+
* (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
|
|
542
|
+
* so the height tells you how much margin a band needs.
|
|
543
|
+
*/
|
|
544
|
+
async measure(node, options = {}) {
|
|
545
|
+
const { fonts, images, stylesheets, fontFamilies, ...rest } = options;
|
|
546
|
+
const [main, resources] = await Promise.all([resolveNode(node), this.fonts.resolveResources(fonts, images, fontFamilies)]);
|
|
547
|
+
const sheets = [...stylesheets ?? [], ...main.stylesheets];
|
|
548
|
+
return this.inner.measure(main.node, {
|
|
549
|
+
...rest,
|
|
550
|
+
stylesheets: sheets.length > 0 ? sheets : void 0,
|
|
551
|
+
images: resources.images,
|
|
552
|
+
fontFamilies: resources.fontFamilies
|
|
553
|
+
});
|
|
554
|
+
}
|
|
516
555
|
/** Registers a font ahead of time, deduped against earlier registrations. */
|
|
517
556
|
registerFont(font) {
|
|
518
557
|
return this.fonts.register(font);
|
|
@@ -528,8 +567,14 @@ function render(node, options) {
|
|
|
528
567
|
shared ??= new PdfRenderer();
|
|
529
568
|
return shared.render(node, options);
|
|
530
569
|
}
|
|
570
|
+
/** Measures with a lazily created shared {@link PdfRenderer}. */
|
|
571
|
+
function measure(node, options) {
|
|
572
|
+
shared ??= new PdfRenderer();
|
|
573
|
+
return shared.measure(node, options);
|
|
574
|
+
}
|
|
531
575
|
//#endregion
|
|
532
576
|
exports.PdfRenderer = PdfRenderer;
|
|
533
577
|
exports.default = __wbg_init;
|
|
534
578
|
exports.initSync = initSync;
|
|
579
|
+
exports.measure = measure;
|
|
535
580
|
exports.render = render;
|
package/dist/export.d.cts
CHANGED
|
@@ -6,6 +6,7 @@ type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Modul
|
|
|
6
6
|
interface InitOutput {
|
|
7
7
|
readonly memory: WebAssembly.Memory;
|
|
8
8
|
readonly __wbg_pdfrenderer_free: (a: number, b: number) => void;
|
|
9
|
+
readonly pdfrenderer_measure: (a: number, b: number, c: number, d: number) => void;
|
|
9
10
|
readonly pdfrenderer_new: (a: number) => void;
|
|
10
11
|
readonly pdfrenderer_registerFont: (a: number, b: number, c: number) => void;
|
|
11
12
|
readonly pdfrenderer_render: (a: number, b: number, c: number, d: number) => void;
|
|
@@ -104,6 +105,35 @@ type ViewportOptions = {
|
|
|
104
105
|
header?: never;
|
|
105
106
|
footer?: never;
|
|
106
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* Options for {@link PdfRenderer.measure}: page geometry (or a viewport) plus
|
|
110
|
+
* layout resources. Margins do not affect the result.
|
|
111
|
+
*/
|
|
112
|
+
type MeasureOptions = ({
|
|
113
|
+
size?: PageSize;
|
|
114
|
+
landscape?: boolean;
|
|
115
|
+
viewport?: never;
|
|
116
|
+
} | {
|
|
117
|
+
viewport: ViewportInput;
|
|
118
|
+
size?: never;
|
|
119
|
+
landscape?: never;
|
|
120
|
+
}) & {
|
|
121
|
+
/** Fonts to register before layout, deduped across calls. */
|
|
122
|
+
fonts?: FontLoader$1[];
|
|
123
|
+
/** Pre-fetched images for `src` URLs in the tree. */
|
|
124
|
+
images?: ImagesInput$1;
|
|
125
|
+
/** CSS stylesheets to apply before layout. */
|
|
126
|
+
stylesheets?: string[];
|
|
127
|
+
/** Per-render font stack: ordered family names used as the fallback chain. */
|
|
128
|
+
fontFamilies?: string[];
|
|
129
|
+
/** Default BCP-47 language tag applied to the root. */
|
|
130
|
+
lang?: string;
|
|
131
|
+
};
|
|
132
|
+
/** A node tree's laid-out size in CSS px. */
|
|
133
|
+
type MeasuredSize = {
|
|
134
|
+
width: number;
|
|
135
|
+
height: number;
|
|
136
|
+
};
|
|
107
137
|
/** Document metadata written to the PDF's info dictionary. */
|
|
108
138
|
type PdfMetadata = {
|
|
109
139
|
/** The document title. */
|
|
@@ -116,8 +146,73 @@ type PdfMetadata = {
|
|
|
116
146
|
keywords?: string[];
|
|
117
147
|
/** The tool that created the source document. */
|
|
118
148
|
creator?: string;
|
|
149
|
+
/**
|
|
150
|
+
* UTC creation date, `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`. Tagged archival
|
|
151
|
+
* standards require one; supplying it keeps output deterministic.
|
|
152
|
+
*/
|
|
153
|
+
creationDate?: string;
|
|
119
154
|
};
|
|
120
|
-
|
|
155
|
+
/** A file attached to the PDF, shown in the viewer's attachment panel. */
|
|
156
|
+
type Attachment = {
|
|
157
|
+
/** File name in the PDF, e.g. "factur-x.xml". */
|
|
158
|
+
name: string;
|
|
159
|
+
/** The file's bytes, or a string encoded as UTF-8. */
|
|
160
|
+
data: Uint8Array | string;
|
|
161
|
+
/** IANA media type, e.g. "application/xml". The PDF/A-3 levels require one. */
|
|
162
|
+
mimeType?: string;
|
|
163
|
+
/** Human-readable description. The PDF/A-3 levels require one. */
|
|
164
|
+
description?: string;
|
|
165
|
+
/**
|
|
166
|
+
* How the file relates to the document (the PDF/A-3 AFRelationship).
|
|
167
|
+
* Defaults to "unspecified".
|
|
168
|
+
*/
|
|
169
|
+
relationship?: "source" | "data" | "alternative" | "supplement" | "unspecified";
|
|
170
|
+
/**
|
|
171
|
+
* UTC modification date, `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`; falls back
|
|
172
|
+
* to `metadata.creationDate`. The PDF/A-3 levels require one.
|
|
173
|
+
*/
|
|
174
|
+
modificationDate?: string;
|
|
175
|
+
};
|
|
176
|
+
/** An attachment under the PDF/A-3 levels, which require the descriptive fields. */
|
|
177
|
+
type ArchivalAttachment = Attachment & {
|
|
178
|
+
mimeType: string;
|
|
179
|
+
description: string;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Standards conformance. Invalid combinations are type errors: the `a` levels
|
|
183
|
+
* imply a structure tree so `tagged: false` is rejected, PDF/A-4 is PDF 2.0
|
|
184
|
+
* while PDF/UA-1 is PDF 1.7-only so they cannot combine, and only the
|
|
185
|
+
* PDF/A-3 levels (or plain PDF) accept attachments.
|
|
186
|
+
*/
|
|
187
|
+
type ConformanceOptions = {
|
|
188
|
+
pdfa?: never;
|
|
189
|
+
/** Structure tree: off, on (default), or validated against PDF/UA-1. */
|
|
190
|
+
tagged?: boolean | "ua1";
|
|
191
|
+
/** Files attached to the document. */
|
|
192
|
+
attachments?: Attachment[];
|
|
193
|
+
} | {
|
|
194
|
+
/** PDF/A conformance level. Validation failures reject the render. */
|
|
195
|
+
pdfa: "2b" | "2u";
|
|
196
|
+
tagged?: boolean | "ua1";
|
|
197
|
+
attachments?: never;
|
|
198
|
+
} | {
|
|
199
|
+
pdfa: "2a";
|
|
200
|
+
tagged?: true | "ua1";
|
|
201
|
+
attachments?: never;
|
|
202
|
+
} | {
|
|
203
|
+
pdfa: "3b" | "3u";
|
|
204
|
+
tagged?: boolean | "ua1";
|
|
205
|
+
attachments?: ArchivalAttachment[];
|
|
206
|
+
} | {
|
|
207
|
+
pdfa: "3a";
|
|
208
|
+
tagged?: true | "ua1";
|
|
209
|
+
attachments?: ArchivalAttachment[];
|
|
210
|
+
} | {
|
|
211
|
+
pdfa: "4";
|
|
212
|
+
tagged?: boolean;
|
|
213
|
+
attachments?: never;
|
|
214
|
+
};
|
|
215
|
+
type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
|
|
121
216
|
/** Fonts to register before rendering, deduped across calls. */
|
|
122
217
|
fonts?: FontLoader$1[];
|
|
123
218
|
/**
|
|
@@ -142,6 +237,15 @@ declare class PdfRenderer {
|
|
|
142
237
|
private fonts;
|
|
143
238
|
/** Renders a node tree or JSX to PDF bytes. See {@link RenderOptions}. */
|
|
144
239
|
render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
|
|
240
|
+
/**
|
|
241
|
+
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
242
|
+
*
|
|
243
|
+
* With page options the tree lays out at the full page width with unbounded
|
|
244
|
+
* height, exactly how {@link render} measures a header or footer band
|
|
245
|
+
* (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
|
|
246
|
+
* so the height tells you how much margin a band needs.
|
|
247
|
+
*/
|
|
248
|
+
measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
|
|
145
249
|
/** Registers a font ahead of time, deduped against earlier registrations. */
|
|
146
250
|
registerFont(font: FontLoader$1): Promise<RegisteredFamilyLike[]>;
|
|
147
251
|
/** Releases the underlying wasm renderer's memory. */
|
|
@@ -149,5 +253,7 @@ declare class PdfRenderer {
|
|
|
149
253
|
}
|
|
150
254
|
/** Renders with a lazily created shared {@link PdfRenderer}. */
|
|
151
255
|
declare function render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
|
|
256
|
+
/** Measures with a lazily created shared {@link PdfRenderer}. */
|
|
257
|
+
declare function measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
|
|
152
258
|
//#endregion
|
|
153
|
-
export { Dimensions, type FontLoader, type ImagesInput, NodeInput, PageMargin, PageSize, PdfMetadata, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, render };
|
|
259
|
+
export { ArchivalAttachment, Attachment, Dimensions, type FontLoader, type ImagesInput, MeasureOptions, MeasuredSize, NodeInput, PageMargin, PageSize, PdfMetadata, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, measure, render };
|
package/dist/export.d.mts
CHANGED
|
@@ -6,6 +6,7 @@ type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Modul
|
|
|
6
6
|
interface InitOutput {
|
|
7
7
|
readonly memory: WebAssembly.Memory;
|
|
8
8
|
readonly __wbg_pdfrenderer_free: (a: number, b: number) => void;
|
|
9
|
+
readonly pdfrenderer_measure: (a: number, b: number, c: number, d: number) => void;
|
|
9
10
|
readonly pdfrenderer_new: (a: number) => void;
|
|
10
11
|
readonly pdfrenderer_registerFont: (a: number, b: number, c: number) => void;
|
|
11
12
|
readonly pdfrenderer_render: (a: number, b: number, c: number, d: number) => void;
|
|
@@ -104,6 +105,35 @@ type ViewportOptions = {
|
|
|
104
105
|
header?: never;
|
|
105
106
|
footer?: never;
|
|
106
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* Options for {@link PdfRenderer.measure}: page geometry (or a viewport) plus
|
|
110
|
+
* layout resources. Margins do not affect the result.
|
|
111
|
+
*/
|
|
112
|
+
type MeasureOptions = ({
|
|
113
|
+
size?: PageSize;
|
|
114
|
+
landscape?: boolean;
|
|
115
|
+
viewport?: never;
|
|
116
|
+
} | {
|
|
117
|
+
viewport: ViewportInput;
|
|
118
|
+
size?: never;
|
|
119
|
+
landscape?: never;
|
|
120
|
+
}) & {
|
|
121
|
+
/** Fonts to register before layout, deduped across calls. */
|
|
122
|
+
fonts?: FontLoader$1[];
|
|
123
|
+
/** Pre-fetched images for `src` URLs in the tree. */
|
|
124
|
+
images?: ImagesInput$1;
|
|
125
|
+
/** CSS stylesheets to apply before layout. */
|
|
126
|
+
stylesheets?: string[];
|
|
127
|
+
/** Per-render font stack: ordered family names used as the fallback chain. */
|
|
128
|
+
fontFamilies?: string[];
|
|
129
|
+
/** Default BCP-47 language tag applied to the root. */
|
|
130
|
+
lang?: string;
|
|
131
|
+
};
|
|
132
|
+
/** A node tree's laid-out size in CSS px. */
|
|
133
|
+
type MeasuredSize = {
|
|
134
|
+
width: number;
|
|
135
|
+
height: number;
|
|
136
|
+
};
|
|
107
137
|
/** Document metadata written to the PDF's info dictionary. */
|
|
108
138
|
type PdfMetadata = {
|
|
109
139
|
/** The document title. */
|
|
@@ -116,8 +146,73 @@ type PdfMetadata = {
|
|
|
116
146
|
keywords?: string[];
|
|
117
147
|
/** The tool that created the source document. */
|
|
118
148
|
creator?: string;
|
|
149
|
+
/**
|
|
150
|
+
* UTC creation date, `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`. Tagged archival
|
|
151
|
+
* standards require one; supplying it keeps output deterministic.
|
|
152
|
+
*/
|
|
153
|
+
creationDate?: string;
|
|
119
154
|
};
|
|
120
|
-
|
|
155
|
+
/** A file attached to the PDF, shown in the viewer's attachment panel. */
|
|
156
|
+
type Attachment = {
|
|
157
|
+
/** File name in the PDF, e.g. "factur-x.xml". */
|
|
158
|
+
name: string;
|
|
159
|
+
/** The file's bytes, or a string encoded as UTF-8. */
|
|
160
|
+
data: Uint8Array | string;
|
|
161
|
+
/** IANA media type, e.g. "application/xml". The PDF/A-3 levels require one. */
|
|
162
|
+
mimeType?: string;
|
|
163
|
+
/** Human-readable description. The PDF/A-3 levels require one. */
|
|
164
|
+
description?: string;
|
|
165
|
+
/**
|
|
166
|
+
* How the file relates to the document (the PDF/A-3 AFRelationship).
|
|
167
|
+
* Defaults to "unspecified".
|
|
168
|
+
*/
|
|
169
|
+
relationship?: "source" | "data" | "alternative" | "supplement" | "unspecified";
|
|
170
|
+
/**
|
|
171
|
+
* UTC modification date, `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`; falls back
|
|
172
|
+
* to `metadata.creationDate`. The PDF/A-3 levels require one.
|
|
173
|
+
*/
|
|
174
|
+
modificationDate?: string;
|
|
175
|
+
};
|
|
176
|
+
/** An attachment under the PDF/A-3 levels, which require the descriptive fields. */
|
|
177
|
+
type ArchivalAttachment = Attachment & {
|
|
178
|
+
mimeType: string;
|
|
179
|
+
description: string;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Standards conformance. Invalid combinations are type errors: the `a` levels
|
|
183
|
+
* imply a structure tree so `tagged: false` is rejected, PDF/A-4 is PDF 2.0
|
|
184
|
+
* while PDF/UA-1 is PDF 1.7-only so they cannot combine, and only the
|
|
185
|
+
* PDF/A-3 levels (or plain PDF) accept attachments.
|
|
186
|
+
*/
|
|
187
|
+
type ConformanceOptions = {
|
|
188
|
+
pdfa?: never;
|
|
189
|
+
/** Structure tree: off, on (default), or validated against PDF/UA-1. */
|
|
190
|
+
tagged?: boolean | "ua1";
|
|
191
|
+
/** Files attached to the document. */
|
|
192
|
+
attachments?: Attachment[];
|
|
193
|
+
} | {
|
|
194
|
+
/** PDF/A conformance level. Validation failures reject the render. */
|
|
195
|
+
pdfa: "2b" | "2u";
|
|
196
|
+
tagged?: boolean | "ua1";
|
|
197
|
+
attachments?: never;
|
|
198
|
+
} | {
|
|
199
|
+
pdfa: "2a";
|
|
200
|
+
tagged?: true | "ua1";
|
|
201
|
+
attachments?: never;
|
|
202
|
+
} | {
|
|
203
|
+
pdfa: "3b" | "3u";
|
|
204
|
+
tagged?: boolean | "ua1";
|
|
205
|
+
attachments?: ArchivalAttachment[];
|
|
206
|
+
} | {
|
|
207
|
+
pdfa: "3a";
|
|
208
|
+
tagged?: true | "ua1";
|
|
209
|
+
attachments?: ArchivalAttachment[];
|
|
210
|
+
} | {
|
|
211
|
+
pdfa: "4";
|
|
212
|
+
tagged?: boolean;
|
|
213
|
+
attachments?: never;
|
|
214
|
+
};
|
|
215
|
+
type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
|
|
121
216
|
/** Fonts to register before rendering, deduped across calls. */
|
|
122
217
|
fonts?: FontLoader$1[];
|
|
123
218
|
/**
|
|
@@ -142,6 +237,15 @@ declare class PdfRenderer {
|
|
|
142
237
|
private fonts;
|
|
143
238
|
/** Renders a node tree or JSX to PDF bytes. See {@link RenderOptions}. */
|
|
144
239
|
render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
|
|
240
|
+
/**
|
|
241
|
+
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
242
|
+
*
|
|
243
|
+
* With page options the tree lays out at the full page width with unbounded
|
|
244
|
+
* height, exactly how {@link render} measures a header or footer band
|
|
245
|
+
* (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
|
|
246
|
+
* so the height tells you how much margin a band needs.
|
|
247
|
+
*/
|
|
248
|
+
measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
|
|
145
249
|
/** Registers a font ahead of time, deduped against earlier registrations. */
|
|
146
250
|
registerFont(font: FontLoader$1): Promise<RegisteredFamilyLike[]>;
|
|
147
251
|
/** Releases the underlying wasm renderer's memory. */
|
|
@@ -149,5 +253,7 @@ declare class PdfRenderer {
|
|
|
149
253
|
}
|
|
150
254
|
/** Renders with a lazily created shared {@link PdfRenderer}. */
|
|
151
255
|
declare function render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
|
|
256
|
+
/** Measures with a lazily created shared {@link PdfRenderer}. */
|
|
257
|
+
declare function measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
|
|
152
258
|
//#endregion
|
|
153
|
-
export { Dimensions, type FontLoader, type ImagesInput, NodeInput, PageMargin, PageSize, PdfMetadata, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, render };
|
|
259
|
+
export { ArchivalAttachment, Attachment, Dimensions, type FontLoader, type ImagesInput, MeasureOptions, MeasuredSize, NodeInput, PageMargin, PageSize, PdfMetadata, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, measure, render };
|
package/dist/export.mjs
CHANGED
|
@@ -20,6 +20,26 @@ var PdfRenderer$1 = class {
|
|
|
20
20
|
wasm.__wbg_pdfrenderer_free(ptr, 0);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
+
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
24
|
+
* Page options lay out at the full page width, like a header/footer band;
|
|
25
|
+
* `pageNumber` / `totalPages` hooks are filled with three-digit counters.
|
|
26
|
+
* @param {Node} node
|
|
27
|
+
* @param {MeasureOptions | null} [options]
|
|
28
|
+
* @returns {MeasuredSize}
|
|
29
|
+
*/
|
|
30
|
+
measure(node, options) {
|
|
31
|
+
try {
|
|
32
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
33
|
+
wasm.pdfrenderer_measure(retptr, this.__wbg_ptr, addHeapObject(node), isLikeNone(options) ? 0 : addHeapObject(options));
|
|
34
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 0, true);
|
|
35
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4, true);
|
|
36
|
+
if (getDataViewMemory0().getInt32(retptr + 8, true)) throw takeObject(r1);
|
|
37
|
+
return takeObject(r0);
|
|
38
|
+
} finally {
|
|
39
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
23
43
|
* Creates a renderer with the bundled last-resort fonts.
|
|
24
44
|
*/
|
|
25
45
|
constructor() {
|
|
@@ -39,8 +59,8 @@ var PdfRenderer$1 = class {
|
|
|
39
59
|
/**
|
|
40
60
|
* Registers a font (raw bytes or a details object), returning the families
|
|
41
61
|
* it produced.
|
|
42
|
-
* @param {
|
|
43
|
-
* @returns {
|
|
62
|
+
* @param {Font} font
|
|
63
|
+
* @returns {RegisteredFamily[]}
|
|
44
64
|
*/
|
|
45
65
|
registerFont(font) {
|
|
46
66
|
try {
|
|
@@ -57,9 +77,9 @@ var PdfRenderer$1 = class {
|
|
|
57
77
|
/**
|
|
58
78
|
* Renders a node tree to PDF bytes. Without options the output is paged A4;
|
|
59
79
|
* `viewport` renders a single fixed page instead.
|
|
60
|
-
* @param {
|
|
61
|
-
* @param {
|
|
62
|
-
* @returns {Uint8Array}
|
|
80
|
+
* @param {Node} node
|
|
81
|
+
* @param {PdfRenderOptions | null} [options]
|
|
82
|
+
* @returns {Uint8Array<ArrayBuffer>}
|
|
63
83
|
*/
|
|
64
84
|
render(node, options) {
|
|
65
85
|
try {
|
|
@@ -509,6 +529,25 @@ var PdfRenderer = class {
|
|
|
509
529
|
fontFamilies: resources.fontFamilies
|
|
510
530
|
});
|
|
511
531
|
}
|
|
532
|
+
/**
|
|
533
|
+
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
534
|
+
*
|
|
535
|
+
* With page options the tree lays out at the full page width with unbounded
|
|
536
|
+
* height, exactly how {@link render} measures a header or footer band
|
|
537
|
+
* (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
|
|
538
|
+
* so the height tells you how much margin a band needs.
|
|
539
|
+
*/
|
|
540
|
+
async measure(node, options = {}) {
|
|
541
|
+
const { fonts, images, stylesheets, fontFamilies, ...rest } = options;
|
|
542
|
+
const [main, resources] = await Promise.all([resolveNode(node), this.fonts.resolveResources(fonts, images, fontFamilies)]);
|
|
543
|
+
const sheets = [...stylesheets ?? [], ...main.stylesheets];
|
|
544
|
+
return this.inner.measure(main.node, {
|
|
545
|
+
...rest,
|
|
546
|
+
stylesheets: sheets.length > 0 ? sheets : void 0,
|
|
547
|
+
images: resources.images,
|
|
548
|
+
fontFamilies: resources.fontFamilies
|
|
549
|
+
});
|
|
550
|
+
}
|
|
512
551
|
/** Registers a font ahead of time, deduped against earlier registrations. */
|
|
513
552
|
registerFont(font) {
|
|
514
553
|
return this.fonts.register(font);
|
|
@@ -524,5 +563,10 @@ function render(node, options) {
|
|
|
524
563
|
shared ??= new PdfRenderer();
|
|
525
564
|
return shared.render(node, options);
|
|
526
565
|
}
|
|
566
|
+
/** Measures with a lazily created shared {@link PdfRenderer}. */
|
|
567
|
+
function measure(node, options) {
|
|
568
|
+
shared ??= new PdfRenderer();
|
|
569
|
+
return shared.measure(node, options);
|
|
570
|
+
}
|
|
527
571
|
//#endregion
|
|
528
|
-
export { PdfRenderer, __wbg_init as default, initSync, render };
|
|
572
|
+
export { PdfRenderer, __wbg_init as default, initSync, measure, render };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "takumi-pdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "HTML/JSX to paged, selectable-text PDF, powered by takumi. WebAssembly, no Chromium.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"publish-lint": "attw --pack . && publint --strict ."
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@takumi-rs/helpers": "2.5.
|
|
86
|
+
"@takumi-rs/helpers": "2.5.10"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@types/bun": "^1.3.14",
|
|
90
|
-
"@types/react": "19.2.
|
|
90
|
+
"@types/react": "19.2.18",
|
|
91
91
|
"tsdown": "0.22.14"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|
|
Binary file
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
export const memory: WebAssembly.Memory;
|
|
4
4
|
export const __wbg_pdfrenderer_free: (a: number, b: number) => void;
|
|
5
|
+
export const pdfrenderer_measure: (a: number, b: number, c: number, d: number) => void;
|
|
5
6
|
export const pdfrenderer_new: (a: number) => void;
|
|
6
7
|
export const pdfrenderer_registerFont: (a: number, b: number, c: number) => void;
|
|
7
8
|
export const pdfrenderer_render: (a: number, b: number, c: number, d: number) => void;
|