takumi-pdf 0.4.2 → 0.6.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 +6 -6
- package/dist/export.cjs +69 -8
- package/dist/export.d.cts +16 -11
- package/dist/export.d.mts +16 -11
- package/dist/export.mjs +69 -8
- package/package.json +9 -4
- package/pkg/takumi_pdf_wasm_bg.wasm +0 -0
- package/pkg/takumi_pdf_wasm_bg.wasm.d.ts +1 -0
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
# takumi-pdf
|
|
5
5
|
|
|
6
|
-
**Render
|
|
6
|
+
**Render paged PDFs from JSX, HTML, and CSS. No headless browser.**
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Build invoices and reports with CSS or Tailwind. The renderer writes vector PDF with selectable text.
|
|
9
9
|
|
|
10
10
|
[Documentation](https://takumi.kane.tw/docs/) · [Playground](https://takumi.kane.tw/playground)
|
|
11
11
|
|
|
@@ -13,9 +13,9 @@ Invoices, reports, and receipts from the Takumi renderer, with CSS layout and ve
|
|
|
13
13
|
|
|
14
14
|
## Why
|
|
15
15
|
|
|
16
|
-
Browser-based PDF generation
|
|
16
|
+
Browser-based PDF generation starts a browser process and ships a Chrome installation. `takumi-pdf` compiles Takumi's layout and PDF code to WebAssembly.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Pass JSX or a Takumi node tree with CSS. The renderer returns vector PDF bytes with searchable text and embedded subset fonts.
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -168,7 +168,7 @@ const pdf = await render(
|
|
|
168
168
|
|
|
169
169
|
## Links, outline, and metadata
|
|
170
170
|
|
|
171
|
-
Anchors with an `href` become clickable link annotations. `outline: true` builds PDF bookmarks from `h1
|
|
171
|
+
Anchors with an `href` become clickable link annotations. `outline: true` builds PDF bookmarks from `h1` through `h6` headings. `metadata` fills the document properties:
|
|
172
172
|
|
|
173
173
|
```tsx
|
|
174
174
|
const pdf = await render(report, {
|
|
@@ -186,7 +186,7 @@ Omit `metadata` to keep output byte-identical across runs.
|
|
|
186
186
|
|
|
187
187
|
## Tagged output and PDF/A
|
|
188
188
|
|
|
189
|
-
Output is **tagged by default**: HTML semantics (`h1
|
|
189
|
+
Output is **tagged by default**: HTML semantics (`h1` through `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
190
|
|
|
191
191
|
`<table>` markup is not supported, and a table built out of flex rows carries no `Table` structure elements.
|
|
192
192
|
|
package/dist/export.cjs
CHANGED
|
@@ -5,6 +5,7 @@ Object.defineProperties(exports, {
|
|
|
5
5
|
let _takumi_rs_helpers_html = require("@takumi-rs/helpers/html");
|
|
6
6
|
let _takumi_rs_helpers_jsx = require("@takumi-rs/helpers/jsx");
|
|
7
7
|
let _takumi_rs_helpers_renderer = require("@takumi-rs/helpers/renderer");
|
|
8
|
+
let _takumi_rs_helpers = require("@takumi-rs/helpers");
|
|
8
9
|
//#region pkg/takumi_pdf_wasm.js
|
|
9
10
|
/**
|
|
10
11
|
* A PDF renderer holding registered fonts and a decoded-resource cache.
|
|
@@ -103,6 +104,32 @@ var PdfRenderer$1 = class {
|
|
|
103
104
|
}
|
|
104
105
|
};
|
|
105
106
|
if (Symbol.dispose) PdfRenderer$1.prototype[Symbol.dispose] = PdfRenderer$1.prototype.free;
|
|
107
|
+
/**
|
|
108
|
+
* The characters the page counters named by these class names can produce.
|
|
109
|
+
*
|
|
110
|
+
* A counter's characters appear nowhere in the document, so a caller choosing
|
|
111
|
+
* which faces to load has no other way to learn it needs, say, Thai digits.
|
|
112
|
+
* @param {string[]} classes
|
|
113
|
+
* @returns {string}
|
|
114
|
+
*/
|
|
115
|
+
function counterCharacters(classes) {
|
|
116
|
+
let deferred2_0;
|
|
117
|
+
let deferred2_1;
|
|
118
|
+
try {
|
|
119
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
120
|
+
const ptr0 = passArrayJsValueToWasm0(classes, wasm.__wbindgen_export);
|
|
121
|
+
const len0 = WASM_VECTOR_LEN;
|
|
122
|
+
wasm.counterCharacters(retptr, ptr0, len0);
|
|
123
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 0, true);
|
|
124
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4, true);
|
|
125
|
+
deferred2_0 = r0;
|
|
126
|
+
deferred2_1 = r1;
|
|
127
|
+
return getStringFromWasm0(r0, r1);
|
|
128
|
+
} finally {
|
|
129
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
130
|
+
wasm.__wbindgen_export4(deferred2_0, deferred2_1, 1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
106
133
|
function __wbg_get_imports() {
|
|
107
134
|
return {
|
|
108
135
|
__proto__: null,
|
|
@@ -382,6 +409,13 @@ let heap_next = heap.length;
|
|
|
382
409
|
function isLikeNone(x) {
|
|
383
410
|
return x === void 0 || x === null;
|
|
384
411
|
}
|
|
412
|
+
function passArrayJsValueToWasm0(array, malloc) {
|
|
413
|
+
const ptr = malloc(array.length * 4, 4) >>> 0;
|
|
414
|
+
const mem = getDataViewMemory0();
|
|
415
|
+
for (let i = 0; i < array.length; i++) mem.setUint32(ptr + 4 * i, addHeapObject(array[i]), true);
|
|
416
|
+
WASM_VECTOR_LEN = array.length;
|
|
417
|
+
return ptr;
|
|
418
|
+
}
|
|
385
419
|
function passStringToWasm0(arg, malloc, realloc) {
|
|
386
420
|
if (realloc === void 0) {
|
|
387
421
|
const buf = cachedTextEncoder.encode(arg);
|
|
@@ -497,6 +531,18 @@ async function __wbg_init(module_or_path) {
|
|
|
497
531
|
}
|
|
498
532
|
//#endregion
|
|
499
533
|
//#region src/export.ts
|
|
534
|
+
/** Every class name in a tree, so the renderer can say what its counters draw. */
|
|
535
|
+
function classNames(node) {
|
|
536
|
+
const into = [];
|
|
537
|
+
collectClassNames(node, into);
|
|
538
|
+
return into;
|
|
539
|
+
}
|
|
540
|
+
function collectClassNames(node, into) {
|
|
541
|
+
if (typeof node !== "object" || node === null) return;
|
|
542
|
+
const { className, children } = node;
|
|
543
|
+
if (typeof className === "string") into.push(...className.split(/\s+/).filter(Boolean));
|
|
544
|
+
if (Array.isArray(children)) for (const child of children) collectClassNames(child, into);
|
|
545
|
+
}
|
|
500
546
|
function isNode(value) {
|
|
501
547
|
return typeof value === "object" && value !== null && "type" in value && !("$$typeof" in value);
|
|
502
548
|
}
|
|
@@ -515,12 +561,20 @@ var PdfRenderer = class {
|
|
|
515
561
|
/** Renders a node tree, JSX, or an HTML string to PDF bytes. See {@link RenderOptions}. */
|
|
516
562
|
async render(node, options = {}) {
|
|
517
563
|
const { fonts, images, header, footer, stylesheets, fontFamilies, ...rest } = options;
|
|
518
|
-
const [main, headerResult, footerResult
|
|
564
|
+
const [main, headerResult, footerResult] = await Promise.all([
|
|
519
565
|
resolveNode(node),
|
|
520
566
|
header === void 0 ? void 0 : resolveNode(header),
|
|
521
|
-
footer === void 0 ? void 0 : resolveNode(footer)
|
|
522
|
-
this.fonts.resolveResources(fonts, images, fontFamilies)
|
|
567
|
+
footer === void 0 ? void 0 : resolveNode(footer)
|
|
523
568
|
]);
|
|
569
|
+
const bands = [headerResult?.node, footerResult?.node].filter((band) => band !== void 0);
|
|
570
|
+
const resources = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
|
|
571
|
+
fonts,
|
|
572
|
+
source: [
|
|
573
|
+
main.node,
|
|
574
|
+
...bands,
|
|
575
|
+
counterCharacters([main.node, ...bands].flatMap((tree) => classNames(tree)))
|
|
576
|
+
]
|
|
577
|
+
}), images, fontFamilies);
|
|
524
578
|
const sheets = [
|
|
525
579
|
...stylesheets ?? [],
|
|
526
580
|
...main.stylesheets,
|
|
@@ -539,14 +593,21 @@ var PdfRenderer = class {
|
|
|
539
593
|
/**
|
|
540
594
|
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
541
595
|
*
|
|
542
|
-
* With page options the tree lays out
|
|
543
|
-
* height, exactly how {@link render} measures a header or footer
|
|
544
|
-
* (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
545
|
-
* so the height tells you how much margin a band needs.
|
|
596
|
+
* With page options the tree lays out against the full page width with
|
|
597
|
+
* unbounded height, exactly how {@link render} measures a header or footer
|
|
598
|
+
* band (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
599
|
+
* counters), so the height tells you how much margin a band needs.
|
|
600
|
+
*
|
|
601
|
+
* The returned size is the tree's own, not the space it was laid out
|
|
602
|
+
* against: a box with `width: 100px` measures 100 wide on any page.
|
|
546
603
|
*/
|
|
547
604
|
async measure(node, options = {}) {
|
|
548
605
|
const { fonts, images, stylesheets, fontFamilies, ...rest } = options;
|
|
549
|
-
const
|
|
606
|
+
const main = await resolveNode(node);
|
|
607
|
+
const resources = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
|
|
608
|
+
fonts,
|
|
609
|
+
source: [main.node, counterCharacters(classNames(main.node))]
|
|
610
|
+
}), images, fontFamilies);
|
|
550
611
|
const sheets = [...stylesheets ?? [], ...main.stylesheets];
|
|
551
612
|
return this.inner.measure(main.node, {
|
|
552
613
|
...rest,
|
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 counterCharacters: (a: number, b: number, c: number) => void;
|
|
9
10
|
readonly pdfrenderer_measure: (a: number, b: number, c: number, d: number) => void;
|
|
10
11
|
readonly pdfrenderer_new: (a: number) => void;
|
|
11
12
|
readonly pdfrenderer_registerFont: (a: number, b: number, c: number) => void;
|
|
@@ -211,14 +212,15 @@ type ArchivalAttachment = Attachment & {
|
|
|
211
212
|
};
|
|
212
213
|
/**
|
|
213
214
|
* Standards conformance. Invalid combinations are type errors: the `a` levels
|
|
214
|
-
* imply a structure tree so `tagged: false` is rejected, PDF/
|
|
215
|
-
*
|
|
216
|
-
* PDF/A-3 levels, PDF/A-4f (or plain PDF) accept
|
|
215
|
+
* imply a structure tree so `tagged: false` is rejected, PDF/UA-1 is PDF
|
|
216
|
+
* 1.7-only and PDF/UA-2 is PDF 2.0-only so each pairs with its own PDF/A
|
|
217
|
+
* levels, and only the PDF/A-3 levels, PDF/A-4f (or plain PDF) accept
|
|
218
|
+
* attachments.
|
|
217
219
|
*/
|
|
218
220
|
type ConformanceOptions = {
|
|
219
221
|
pdfa?: never;
|
|
220
|
-
/** Structure tree: off, on (default), or validated against PDF/UA
|
|
221
|
-
tagged?: boolean | "ua1";
|
|
222
|
+
/** Structure tree: off, on (default), or validated against PDF/UA. */
|
|
223
|
+
tagged?: boolean | "ua1" | "ua2";
|
|
222
224
|
/** Files attached to the document. */
|
|
223
225
|
attachments?: Attachment[];
|
|
224
226
|
} | {
|
|
@@ -240,11 +242,11 @@ type ConformanceOptions = {
|
|
|
240
242
|
attachments?: ArchivalAttachment[];
|
|
241
243
|
} | {
|
|
242
244
|
pdfa: "4";
|
|
243
|
-
tagged?: boolean;
|
|
245
|
+
tagged?: boolean | "ua2";
|
|
244
246
|
attachments?: never;
|
|
245
247
|
} | {
|
|
246
248
|
pdfa: "4f";
|
|
247
|
-
tagged?: boolean;
|
|
249
|
+
tagged?: boolean | "ua2";
|
|
248
250
|
attachments?: ArchivalAttachment[];
|
|
249
251
|
};
|
|
250
252
|
type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
|
|
@@ -275,10 +277,13 @@ declare class PdfRenderer {
|
|
|
275
277
|
/**
|
|
276
278
|
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
277
279
|
*
|
|
278
|
-
* With page options the tree lays out
|
|
279
|
-
* height, exactly how {@link render} measures a header or footer
|
|
280
|
-
* (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
281
|
-
* so the height tells you how much margin a band needs.
|
|
280
|
+
* With page options the tree lays out against the full page width with
|
|
281
|
+
* unbounded height, exactly how {@link render} measures a header or footer
|
|
282
|
+
* band (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
283
|
+
* counters), so the height tells you how much margin a band needs.
|
|
284
|
+
*
|
|
285
|
+
* The returned size is the tree's own, not the space it was laid out
|
|
286
|
+
* against: a box with `width: 100px` measures 100 wide on any page.
|
|
282
287
|
*/
|
|
283
288
|
measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
|
|
284
289
|
/** Registers a font ahead of time, deduped against earlier registrations. */
|
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 counterCharacters: (a: number, b: number, c: number) => void;
|
|
9
10
|
readonly pdfrenderer_measure: (a: number, b: number, c: number, d: number) => void;
|
|
10
11
|
readonly pdfrenderer_new: (a: number) => void;
|
|
11
12
|
readonly pdfrenderer_registerFont: (a: number, b: number, c: number) => void;
|
|
@@ -211,14 +212,15 @@ type ArchivalAttachment = Attachment & {
|
|
|
211
212
|
};
|
|
212
213
|
/**
|
|
213
214
|
* Standards conformance. Invalid combinations are type errors: the `a` levels
|
|
214
|
-
* imply a structure tree so `tagged: false` is rejected, PDF/
|
|
215
|
-
*
|
|
216
|
-
* PDF/A-3 levels, PDF/A-4f (or plain PDF) accept
|
|
215
|
+
* imply a structure tree so `tagged: false` is rejected, PDF/UA-1 is PDF
|
|
216
|
+
* 1.7-only and PDF/UA-2 is PDF 2.0-only so each pairs with its own PDF/A
|
|
217
|
+
* levels, and only the PDF/A-3 levels, PDF/A-4f (or plain PDF) accept
|
|
218
|
+
* attachments.
|
|
217
219
|
*/
|
|
218
220
|
type ConformanceOptions = {
|
|
219
221
|
pdfa?: never;
|
|
220
|
-
/** Structure tree: off, on (default), or validated against PDF/UA
|
|
221
|
-
tagged?: boolean | "ua1";
|
|
222
|
+
/** Structure tree: off, on (default), or validated against PDF/UA. */
|
|
223
|
+
tagged?: boolean | "ua1" | "ua2";
|
|
222
224
|
/** Files attached to the document. */
|
|
223
225
|
attachments?: Attachment[];
|
|
224
226
|
} | {
|
|
@@ -240,11 +242,11 @@ type ConformanceOptions = {
|
|
|
240
242
|
attachments?: ArchivalAttachment[];
|
|
241
243
|
} | {
|
|
242
244
|
pdfa: "4";
|
|
243
|
-
tagged?: boolean;
|
|
245
|
+
tagged?: boolean | "ua2";
|
|
244
246
|
attachments?: never;
|
|
245
247
|
} | {
|
|
246
248
|
pdfa: "4f";
|
|
247
|
-
tagged?: boolean;
|
|
249
|
+
tagged?: boolean | "ua2";
|
|
248
250
|
attachments?: ArchivalAttachment[];
|
|
249
251
|
};
|
|
250
252
|
type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
|
|
@@ -275,10 +277,13 @@ declare class PdfRenderer {
|
|
|
275
277
|
/**
|
|
276
278
|
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
277
279
|
*
|
|
278
|
-
* With page options the tree lays out
|
|
279
|
-
* height, exactly how {@link render} measures a header or footer
|
|
280
|
-
* (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
281
|
-
* so the height tells you how much margin a band needs.
|
|
280
|
+
* With page options the tree lays out against the full page width with
|
|
281
|
+
* unbounded height, exactly how {@link render} measures a header or footer
|
|
282
|
+
* band (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
283
|
+
* counters), so the height tells you how much margin a band needs.
|
|
284
|
+
*
|
|
285
|
+
* The returned size is the tree's own, not the space it was laid out
|
|
286
|
+
* against: a box with `width: 100px` measures 100 wide on any page.
|
|
282
287
|
*/
|
|
283
288
|
measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
|
|
284
289
|
/** Registers a font ahead of time, deduped against earlier registrations. */
|
package/dist/export.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fromHtml } from "@takumi-rs/helpers/html";
|
|
2
2
|
import { fromJsx } from "@takumi-rs/helpers/jsx";
|
|
3
3
|
import { FontRegistry } from "@takumi-rs/helpers/renderer";
|
|
4
|
+
import { subsetFonts } from "@takumi-rs/helpers";
|
|
4
5
|
//#region pkg/takumi_pdf_wasm.js
|
|
5
6
|
/**
|
|
6
7
|
* A PDF renderer holding registered fonts and a decoded-resource cache.
|
|
@@ -99,6 +100,32 @@ var PdfRenderer$1 = class {
|
|
|
99
100
|
}
|
|
100
101
|
};
|
|
101
102
|
if (Symbol.dispose) PdfRenderer$1.prototype[Symbol.dispose] = PdfRenderer$1.prototype.free;
|
|
103
|
+
/**
|
|
104
|
+
* The characters the page counters named by these class names can produce.
|
|
105
|
+
*
|
|
106
|
+
* A counter's characters appear nowhere in the document, so a caller choosing
|
|
107
|
+
* which faces to load has no other way to learn it needs, say, Thai digits.
|
|
108
|
+
* @param {string[]} classes
|
|
109
|
+
* @returns {string}
|
|
110
|
+
*/
|
|
111
|
+
function counterCharacters(classes) {
|
|
112
|
+
let deferred2_0;
|
|
113
|
+
let deferred2_1;
|
|
114
|
+
try {
|
|
115
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
116
|
+
const ptr0 = passArrayJsValueToWasm0(classes, wasm.__wbindgen_export);
|
|
117
|
+
const len0 = WASM_VECTOR_LEN;
|
|
118
|
+
wasm.counterCharacters(retptr, ptr0, len0);
|
|
119
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 0, true);
|
|
120
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4, true);
|
|
121
|
+
deferred2_0 = r0;
|
|
122
|
+
deferred2_1 = r1;
|
|
123
|
+
return getStringFromWasm0(r0, r1);
|
|
124
|
+
} finally {
|
|
125
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
126
|
+
wasm.__wbindgen_export4(deferred2_0, deferred2_1, 1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
102
129
|
function __wbg_get_imports() {
|
|
103
130
|
return {
|
|
104
131
|
__proto__: null,
|
|
@@ -378,6 +405,13 @@ let heap_next = heap.length;
|
|
|
378
405
|
function isLikeNone(x) {
|
|
379
406
|
return x === void 0 || x === null;
|
|
380
407
|
}
|
|
408
|
+
function passArrayJsValueToWasm0(array, malloc) {
|
|
409
|
+
const ptr = malloc(array.length * 4, 4) >>> 0;
|
|
410
|
+
const mem = getDataViewMemory0();
|
|
411
|
+
for (let i = 0; i < array.length; i++) mem.setUint32(ptr + 4 * i, addHeapObject(array[i]), true);
|
|
412
|
+
WASM_VECTOR_LEN = array.length;
|
|
413
|
+
return ptr;
|
|
414
|
+
}
|
|
381
415
|
function passStringToWasm0(arg, malloc, realloc) {
|
|
382
416
|
if (realloc === void 0) {
|
|
383
417
|
const buf = cachedTextEncoder.encode(arg);
|
|
@@ -493,6 +527,18 @@ async function __wbg_init(module_or_path) {
|
|
|
493
527
|
}
|
|
494
528
|
//#endregion
|
|
495
529
|
//#region src/export.ts
|
|
530
|
+
/** Every class name in a tree, so the renderer can say what its counters draw. */
|
|
531
|
+
function classNames(node) {
|
|
532
|
+
const into = [];
|
|
533
|
+
collectClassNames(node, into);
|
|
534
|
+
return into;
|
|
535
|
+
}
|
|
536
|
+
function collectClassNames(node, into) {
|
|
537
|
+
if (typeof node !== "object" || node === null) return;
|
|
538
|
+
const { className, children } = node;
|
|
539
|
+
if (typeof className === "string") into.push(...className.split(/\s+/).filter(Boolean));
|
|
540
|
+
if (Array.isArray(children)) for (const child of children) collectClassNames(child, into);
|
|
541
|
+
}
|
|
496
542
|
function isNode(value) {
|
|
497
543
|
return typeof value === "object" && value !== null && "type" in value && !("$$typeof" in value);
|
|
498
544
|
}
|
|
@@ -511,12 +557,20 @@ var PdfRenderer = class {
|
|
|
511
557
|
/** Renders a node tree, JSX, or an HTML string to PDF bytes. See {@link RenderOptions}. */
|
|
512
558
|
async render(node, options = {}) {
|
|
513
559
|
const { fonts, images, header, footer, stylesheets, fontFamilies, ...rest } = options;
|
|
514
|
-
const [main, headerResult, footerResult
|
|
560
|
+
const [main, headerResult, footerResult] = await Promise.all([
|
|
515
561
|
resolveNode(node),
|
|
516
562
|
header === void 0 ? void 0 : resolveNode(header),
|
|
517
|
-
footer === void 0 ? void 0 : resolveNode(footer)
|
|
518
|
-
this.fonts.resolveResources(fonts, images, fontFamilies)
|
|
563
|
+
footer === void 0 ? void 0 : resolveNode(footer)
|
|
519
564
|
]);
|
|
565
|
+
const bands = [headerResult?.node, footerResult?.node].filter((band) => band !== void 0);
|
|
566
|
+
const resources = await this.fonts.resolveResources(fonts && subsetFonts({
|
|
567
|
+
fonts,
|
|
568
|
+
source: [
|
|
569
|
+
main.node,
|
|
570
|
+
...bands,
|
|
571
|
+
counterCharacters([main.node, ...bands].flatMap((tree) => classNames(tree)))
|
|
572
|
+
]
|
|
573
|
+
}), images, fontFamilies);
|
|
520
574
|
const sheets = [
|
|
521
575
|
...stylesheets ?? [],
|
|
522
576
|
...main.stylesheets,
|
|
@@ -535,14 +589,21 @@ var PdfRenderer = class {
|
|
|
535
589
|
/**
|
|
536
590
|
* Lays out a node tree without rendering and returns its size in CSS px.
|
|
537
591
|
*
|
|
538
|
-
* With page options the tree lays out
|
|
539
|
-
* height, exactly how {@link render} measures a header or footer
|
|
540
|
-
* (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
541
|
-
* so the height tells you how much margin a band needs.
|
|
592
|
+
* With page options the tree lays out against the full page width with
|
|
593
|
+
* unbounded height, exactly how {@link render} measures a header or footer
|
|
594
|
+
* band (`pageNumber` / `totalPages` hooks are filled with three-digit
|
|
595
|
+
* counters), so the height tells you how much margin a band needs.
|
|
596
|
+
*
|
|
597
|
+
* The returned size is the tree's own, not the space it was laid out
|
|
598
|
+
* against: a box with `width: 100px` measures 100 wide on any page.
|
|
542
599
|
*/
|
|
543
600
|
async measure(node, options = {}) {
|
|
544
601
|
const { fonts, images, stylesheets, fontFamilies, ...rest } = options;
|
|
545
|
-
const
|
|
602
|
+
const main = await resolveNode(node);
|
|
603
|
+
const resources = await this.fonts.resolveResources(fonts && subsetFonts({
|
|
604
|
+
fonts,
|
|
605
|
+
source: [main.node, counterCharacters(classNames(main.node))]
|
|
606
|
+
}), images, fontFamilies);
|
|
546
607
|
const sheets = [...stylesheets ?? [], ...main.stylesheets];
|
|
547
608
|
return this.inner.measure(main.node, {
|
|
548
609
|
...rest,
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "takumi-pdf",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Render paged PDFs from JSX, HTML, and CSS. No headless browser.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"chromium-free",
|
|
7
|
+
"cjk",
|
|
6
8
|
"css",
|
|
9
|
+
"html-to-pdf",
|
|
7
10
|
"invoice",
|
|
8
11
|
"jsx",
|
|
9
12
|
"pdf",
|
|
13
|
+
"pdf-generation",
|
|
10
14
|
"react",
|
|
11
|
-
"
|
|
15
|
+
"receipt",
|
|
16
|
+
"tailwind",
|
|
12
17
|
"webassembly"
|
|
13
18
|
],
|
|
14
19
|
"homepage": "https://takumi.kane.tw/docs/",
|
|
@@ -83,7 +88,7 @@
|
|
|
83
88
|
"publish-lint": "attw --pack . && publint --strict ."
|
|
84
89
|
},
|
|
85
90
|
"dependencies": {
|
|
86
|
-
"@takumi-rs/helpers": "2.
|
|
91
|
+
"@takumi-rs/helpers": "2.6.1"
|
|
87
92
|
},
|
|
88
93
|
"devDependencies": {
|
|
89
94
|
"@types/bun": "^1.3.14",
|
|
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 counterCharacters: (a: number, b: number, c: number) => void;
|
|
5
6
|
export const pdfrenderer_measure: (a: number, b: number, c: number, d: number) => void;
|
|
6
7
|
export const pdfrenderer_new: (a: number) => void;
|
|
7
8
|
export const pdfrenderer_registerFont: (a: number, b: number, c: number) => void;
|