takumi-pdf 0.1.2 → 0.1.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.
- package/README.md +48 -13
- package/dist/export.d.cts +18 -1
- package/dist/export.d.mts +18 -1
- package/package.json +2 -2
- package/pkg/takumi_pdf_wasm_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://takumi.kane.tw/logo.svg" alt="Takumi" width="64" />
|
|
3
|
+
|
|
1
4
|
# takumi-pdf
|
|
2
5
|
|
|
3
|
-
Render JSX to paged PDF
|
|
6
|
+
**Render JSX to paged, selectable-text PDF. WebAssembly, no Chromium.**
|
|
7
|
+
|
|
8
|
+
Invoices, reports, and receipts from the Takumi renderer, with CSS layout and vector PDF output.
|
|
9
|
+
|
|
10
|
+
[Documentation](https://takumi.kane.tw/docs/) · [Playground](https://takumi.kane.tw/playground)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
Browser-based PDF generation brings the Chromium serverless tax: browser cold starts, browser memory, a large browser binary, and headless-browser work before a document can render. It is a capable screenshot pipeline, but it is still a browser.
|
|
17
|
+
|
|
18
|
+
`takumi-pdf` uses Takumi's own layout and PDF rendering primitives, compiled to WebAssembly. There is no browser process. Render JSX or a Takumi node tree with CSS and Tailwind classes, then receive vector PDF bytes with selectable, searchable text and embedded subset fonts.
|
|
4
19
|
|
|
5
20
|
## Install
|
|
6
21
|
|
|
@@ -13,17 +28,33 @@ bun add takumi-pdf
|
|
|
13
28
|
## Quick start
|
|
14
29
|
|
|
15
30
|
```tsx
|
|
16
|
-
import { render } from "takumi-pdf";
|
|
17
31
|
import { writeFile } from "node:fs/promises";
|
|
32
|
+
import { googleFonts } from "@takumi-rs/helpers";
|
|
33
|
+
import { render } from "takumi-pdf";
|
|
18
34
|
|
|
19
|
-
const pdf = await render(
|
|
35
|
+
const pdf = await render(
|
|
36
|
+
<main tw="flex flex-col gap-4">
|
|
37
|
+
<h1 tw="text-2xl font-bold">Invoice INV-2026-001</h1>
|
|
38
|
+
<div tw="flex justify-between border-t border-gray-200 pt-2 font-bold">
|
|
39
|
+
<span>Total</span>
|
|
40
|
+
<span>$1,250.00</span>
|
|
41
|
+
</div>
|
|
42
|
+
</main>,
|
|
43
|
+
{
|
|
44
|
+
size: "a4",
|
|
45
|
+
fonts: await googleFonts(["Inter"]),
|
|
46
|
+
footer: (
|
|
47
|
+
<div tw="flex w-full justify-center text-[10px] text-gray-500">
|
|
48
|
+
Page <span className="pageNumber" /> of <span className="totalPages" />
|
|
49
|
+
</div>
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
);
|
|
20
53
|
|
|
21
54
|
await writeFile("invoice.pdf", pdf);
|
|
22
55
|
```
|
|
23
56
|
|
|
24
|
-
`render()` returns `Uint8Array
|
|
25
|
-
|
|
26
|
-
See the [Takumi documentation](https://takumi.kane.tw/docs/) for the shared node and JSX model.
|
|
57
|
+
`render()` returns `Promise<Uint8Array>`. Paged output defaults to A4 with a uniform 48px margin; content flows across as many pages as it needs.
|
|
27
58
|
|
|
28
59
|
## Page setup
|
|
29
60
|
|
|
@@ -43,7 +74,7 @@ const pdf = await render(report, {
|
|
|
43
74
|
|
|
44
75
|
## Headers and footers
|
|
45
76
|
|
|
46
|
-
Headers and footers repeat on every page. Elements
|
|
77
|
+
Headers and footers repeat on every page. Elements whose class list includes `pageNumber` or `totalPages` receive the counter as text.
|
|
47
78
|
|
|
48
79
|
```tsx
|
|
49
80
|
const pdf = await render(report, {
|
|
@@ -94,15 +125,14 @@ const pdf = await render(receipt, { viewport: { width: 302 } });
|
|
|
94
125
|
|
|
95
126
|
## Fonts
|
|
96
127
|
|
|
97
|
-
Pass a font URL or
|
|
128
|
+
Pass a font URL, font bytes, or the `googleFonts` helper. Registered fonts are deduplicated across calls.
|
|
98
129
|
|
|
99
130
|
```tsx
|
|
131
|
+
import { googleFonts } from "@takumi-rs/helpers";
|
|
132
|
+
|
|
100
133
|
const pdf = await render(doc, {
|
|
101
|
-
fonts: [
|
|
102
|
-
|
|
103
|
-
{ name: "Brand Sans", weight: 700, data: fontBytes },
|
|
104
|
-
],
|
|
105
|
-
fontFamilies: ["Brand Sans", "sans-serif"],
|
|
134
|
+
fonts: [...(await googleFonts(["Inter"])), { name: "Brand Sans", weight: 700, data: fontBytes }],
|
|
135
|
+
fontFamilies: ["Brand Sans", "Inter", "sans-serif"],
|
|
106
136
|
});
|
|
107
137
|
```
|
|
108
138
|
|
|
@@ -115,6 +145,7 @@ const renderer = new PdfRenderer();
|
|
|
115
145
|
await renderer.registerFont("https://example.com/Inter-Regular.woff2");
|
|
116
146
|
|
|
117
147
|
const pdf = await renderer.render(doc);
|
|
148
|
+
renderer.free();
|
|
118
149
|
```
|
|
119
150
|
|
|
120
151
|
## Pagination CSS
|
|
@@ -148,3 +179,7 @@ const pdf = await render(doc, {
|
|
|
148
179
|
```
|
|
149
180
|
|
|
150
181
|
`@page` CSS rules are not supported. Set page geometry with `size`, `landscape`, and `margin`.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT or Apache-2.0
|
package/dist/export.d.cts
CHANGED
|
@@ -104,6 +104,19 @@ type ViewportOptions = {
|
|
|
104
104
|
header?: never;
|
|
105
105
|
footer?: never;
|
|
106
106
|
};
|
|
107
|
+
/** Document metadata written to the PDF's info dictionary. */
|
|
108
|
+
type PdfMetadata = {
|
|
109
|
+
/** The document title. */
|
|
110
|
+
title?: string;
|
|
111
|
+
/** The document description (the info dictionary's subject). */
|
|
112
|
+
description?: string;
|
|
113
|
+
/** The document authors. */
|
|
114
|
+
authors?: string[];
|
|
115
|
+
/** The document keywords. */
|
|
116
|
+
keywords?: string[];
|
|
117
|
+
/** The tool that created the source document. */
|
|
118
|
+
creator?: string;
|
|
119
|
+
};
|
|
107
120
|
type RenderOptions = (PagedOptions | ViewportOptions) & {
|
|
108
121
|
/** Fonts to register before rendering, deduped across calls. */
|
|
109
122
|
fonts?: FontLoader$1[];
|
|
@@ -118,6 +131,10 @@ type RenderOptions = (PagedOptions | ViewportOptions) & {
|
|
|
118
131
|
fontFamilies?: string[];
|
|
119
132
|
/** Default BCP-47 language tag applied to the root. */
|
|
120
133
|
lang?: string;
|
|
134
|
+
/** Document metadata; `lang` doubles as the metadata language. */
|
|
135
|
+
metadata?: PdfMetadata;
|
|
136
|
+
/** Generates a PDF outline (bookmarks) from `h1`–`h6` headings. */
|
|
137
|
+
outline?: boolean;
|
|
121
138
|
};
|
|
122
139
|
/** A PDF renderer holding registered fonts. Reuse one instance across renders. */
|
|
123
140
|
declare class PdfRenderer {
|
|
@@ -133,4 +150,4 @@ declare class PdfRenderer {
|
|
|
133
150
|
/** Renders with a lazily created shared {@link PdfRenderer}. */
|
|
134
151
|
declare function render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
|
|
135
152
|
//#endregion
|
|
136
|
-
export { Dimensions, type FontLoader, type ImagesInput, NodeInput, PageMargin, PageSize, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, render };
|
|
153
|
+
export { Dimensions, type FontLoader, type ImagesInput, NodeInput, PageMargin, PageSize, PdfMetadata, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, render };
|
package/dist/export.d.mts
CHANGED
|
@@ -104,6 +104,19 @@ type ViewportOptions = {
|
|
|
104
104
|
header?: never;
|
|
105
105
|
footer?: never;
|
|
106
106
|
};
|
|
107
|
+
/** Document metadata written to the PDF's info dictionary. */
|
|
108
|
+
type PdfMetadata = {
|
|
109
|
+
/** The document title. */
|
|
110
|
+
title?: string;
|
|
111
|
+
/** The document description (the info dictionary's subject). */
|
|
112
|
+
description?: string;
|
|
113
|
+
/** The document authors. */
|
|
114
|
+
authors?: string[];
|
|
115
|
+
/** The document keywords. */
|
|
116
|
+
keywords?: string[];
|
|
117
|
+
/** The tool that created the source document. */
|
|
118
|
+
creator?: string;
|
|
119
|
+
};
|
|
107
120
|
type RenderOptions = (PagedOptions | ViewportOptions) & {
|
|
108
121
|
/** Fonts to register before rendering, deduped across calls. */
|
|
109
122
|
fonts?: FontLoader$1[];
|
|
@@ -118,6 +131,10 @@ type RenderOptions = (PagedOptions | ViewportOptions) & {
|
|
|
118
131
|
fontFamilies?: string[];
|
|
119
132
|
/** Default BCP-47 language tag applied to the root. */
|
|
120
133
|
lang?: string;
|
|
134
|
+
/** Document metadata; `lang` doubles as the metadata language. */
|
|
135
|
+
metadata?: PdfMetadata;
|
|
136
|
+
/** Generates a PDF outline (bookmarks) from `h1`–`h6` headings. */
|
|
137
|
+
outline?: boolean;
|
|
121
138
|
};
|
|
122
139
|
/** A PDF renderer holding registered fonts. Reuse one instance across renders. */
|
|
123
140
|
declare class PdfRenderer {
|
|
@@ -133,4 +150,4 @@ declare class PdfRenderer {
|
|
|
133
150
|
/** Renders with a lazily created shared {@link PdfRenderer}. */
|
|
134
151
|
declare function render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
|
|
135
152
|
//#endregion
|
|
136
|
-
export { Dimensions, type FontLoader, type ImagesInput, NodeInput, PageMargin, PageSize, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, render };
|
|
153
|
+
export { Dimensions, type FontLoader, type ImagesInput, NodeInput, PageMargin, PageSize, PdfMetadata, PdfRenderer, RenderOptions, ViewportInput, __wbg_init as default, initSync, render };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "takumi-pdf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "HTML/JSX to paged, selectable-text PDF, powered by takumi. WebAssembly, no Chromium.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -83,7 +83,7 @@
|
|
|
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.7"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@types/bun": "^1.3.14",
|
|
Binary file
|