takumi-pdf 0.14.2 → 0.15.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 +26 -219
- package/dist/export.cjs +192 -186
- package/dist/export.d.cts +44 -13
- package/dist/export.d.mts +44 -13
- package/dist/export.mjs +192 -186
- package/package.json +4 -4
- package/pkg/takumi_pdf_wasm_bg.wasm +0 -0
- package/pkg/takumi_pdf_wasm_bg.wasm.d.ts +13 -10
package/README.md
CHANGED
|
@@ -7,22 +7,22 @@
|
|
|
7
7
|
|
|
8
8
|
Build invoices and reports with CSS or Tailwind. The renderer writes vector PDF with selectable text.
|
|
9
9
|
|
|
10
|
-
[
|
|
10
|
+
[PDF documentation](https://takumi.kane.tw/docs/pdf) · [Playground](https://takumi.kane.tw/playground)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## How it works
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
`takumi-pdf` runs Takumi's layout and PDF engine in WebAssembly on Node.js, Bun, and Cloudflare Workers. It does not launch a browser process.
|
|
17
17
|
|
|
18
|
-
Pass JSX or a Takumi node tree with CSS. The renderer returns vector PDF bytes with searchable text and embedded subset fonts.
|
|
18
|
+
Pass JSX, an HTML string, 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
|
|
|
22
22
|
```bash
|
|
23
|
-
npm install takumi-pdf
|
|
23
|
+
npm install takumi-pdf @takumi-rs/helpers
|
|
24
24
|
# or
|
|
25
|
-
bun add takumi-pdf
|
|
25
|
+
bun add takumi-pdf @takumi-rs/helpers
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Quick start
|
|
@@ -55,224 +55,31 @@ const pdf = await render(
|
|
|
55
55
|
await writeFile("invoice.pdf", pdf);
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
`render()` returns `Promise<Uint8Array>`. Paged output defaults to A4
|
|
58
|
+
`render()` returns `Promise<Uint8Array>`. Paged output defaults to A4, and `margin` defaults to `"auto"` on all sides. Without headers or footers, margins are 37.8px. Top and bottom margins can expand to fit header or footer bands. Content flows across pages automatically.
|
|
59
59
|
|
|
60
|
-
##
|
|
60
|
+
## Choose the next guide
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
| `landscape` | `boolean` | `false` | Swaps page width and height, including explicit sizes. |
|
|
74
|
-
| `margin` | `number` or `{ top?, right?, bottom?, left? }` | `48` | A number applies to all sides. Missing object sides are `0`. |
|
|
75
|
-
|
|
76
|
-
## Headers and footers
|
|
77
|
-
|
|
78
|
-
Headers and footers repeat on every page. `<PageNumber />` and `<TotalPages />` place the counters; the `format` prop picks a CSS counter style.
|
|
79
|
-
|
|
80
|
-
```tsx
|
|
81
|
-
import { render } from "takumi-pdf";
|
|
82
|
-
import { PageNumber, TotalPages } from "takumi-pdf/primitives";
|
|
83
|
-
|
|
84
|
-
const pdf = await render(report, {
|
|
85
|
-
footer: (
|
|
86
|
-
<div style={{ fontSize: 12 }}>
|
|
87
|
-
第 <PageNumber format="trad-chinese-informal" /> 頁,共{" "}
|
|
88
|
-
<TotalPages format="trad-chinese-informal" /> 頁
|
|
89
|
-
</div>
|
|
90
|
-
),
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
The primitives render class hooks, the same `pageNumber` / `totalPages` names Chromium's print templates use, so HTML input writes `<span class="pageNumber"></span>` directly.
|
|
95
|
-
|
|
96
|
-
| Counter style | Example |
|
|
97
|
-
| ------------------------------------------- | ------------- |
|
|
98
|
-
| `decimal` (default) | `12` |
|
|
99
|
-
| `decimal-leading-zero` | `07` |
|
|
100
|
-
| `lower-roman` / `upper-roman` | `xii` / `XII` |
|
|
101
|
-
| `cjk-decimal` | `一二` |
|
|
102
|
-
| `trad-chinese-informal` / `cjk-ideographic` | `十二` |
|
|
62
|
+
| Task | Guide |
|
|
63
|
+
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| Set page size, margins, and runtime imports | [PDF getting started](https://takumi.kane.tw/docs/pdf) |
|
|
65
|
+
| Control page breaks and keep content together | [Pagination](https://takumi.kane.tw/docs/pdf/pagination) |
|
|
66
|
+
| Reserve space for repeated headers and page numbers | [Headers and footers](https://takumi.kane.tw/docs/pdf/headers-and-footers) |
|
|
67
|
+
| Create a certificate, ticket, or receipt | [Single-page output](https://takumi.kane.tw/docs/pdf/single-page) |
|
|
68
|
+
| Load fonts and remote image bytes | [Fonts and images](https://takumi.kane.tw/docs/pdf/fonts-and-images) |
|
|
69
|
+
| Add hyperlinks, bookmarks, and a table of contents | [Links and metadata](https://takumi.kane.tw/docs/pdf/links-and-outline) |
|
|
70
|
+
| Configure archival or accessible output | [PDF/A and PDF/UA](https://takumi.kane.tw/docs/pdf/pdf-a) |
|
|
71
|
+
| Embed invoice XML or another file | [Attachments](https://takumi.kane.tw/docs/pdf/attachments) |
|
|
72
|
+
| Replace a browser or document renderer | [From Puppeteer](https://takumi.kane.tw/docs/pdf/from-puppeteer) · [From react-pdf](https://takumi.kane.tw/docs/pdf/from-react-pdf) |
|
|
103
73
|
|
|
104
|
-
##
|
|
105
|
-
|
|
106
|
-
Use `viewport` for a fixed one-page PDF, such as a certificate or card. Percentage heights resolve against the viewport and overflow is clipped, like an image render.
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
const pdf = await render(<div style={{ width: "100%", height: "100%" }}>Certificate</div>, {
|
|
110
|
-
viewport: { width: 1123, height: 794 },
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Omit `height` to size the single page to its content, like a thermal receipt. Percentage heights do not resolve there.
|
|
115
|
-
|
|
116
|
-
```tsx
|
|
117
|
-
const pdf = await render(receipt, { viewport: { width: 302 } });
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`viewport` cannot be combined with `size`, `landscape`, `margin`, `header`, or `footer`.
|
|
121
|
-
|
|
122
|
-
## Fonts
|
|
123
|
-
|
|
124
|
-
Pass a font URL, font bytes, or the `googleFonts` helper. Registered fonts are deduplicated across calls.
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
import { googleFonts } from "@takumi-rs/helpers";
|
|
128
|
-
import { render } from "takumi-pdf";
|
|
129
|
-
|
|
130
|
-
const pdf = await render(doc, {
|
|
131
|
-
fonts: [...(await googleFonts(["Inter"])), { name: "Brand Sans", weight: 700, data: fontBytes }],
|
|
132
|
-
fontFamilies: ["Brand Sans", "Inter", "sans-serif"],
|
|
133
|
-
});
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Reuse a `PdfRenderer` when an application renders many documents:
|
|
137
|
-
|
|
138
|
-
```tsx
|
|
139
|
-
import { PdfRenderer } from "takumi-pdf";
|
|
140
|
-
|
|
141
|
-
const renderer = new PdfRenderer();
|
|
142
|
-
await renderer.registerFont("https://example.com/Inter-Regular.woff2");
|
|
143
|
-
|
|
144
|
-
const pdf = await renderer.render(doc);
|
|
145
|
-
renderer.free();
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Pagination CSS
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
const pdf = await render(
|
|
152
|
-
<article>
|
|
153
|
-
<h1 style={{ breakBefore: "page" }}>Chapter two</h1>
|
|
154
|
-
<section style={{ breakInside: "avoid" }}>Keep this together.</section>
|
|
155
|
-
</article>,
|
|
156
|
-
);
|
|
157
|
-
```
|
|
74
|
+
## Before using an existing template
|
|
158
75
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
| `box-decoration-break: clone` | Repeats borders and backgrounds on every page fragment. |
|
|
165
|
-
|
|
166
|
-
## Tables
|
|
167
|
-
|
|
168
|
-
`<table>` markup lays out on shared column tracks, so column x positions stay identical across pages. A `<thead>` paints again at the top of every page its table continues onto, when it is at most a quarter of the page tall and no header cell spans into the body.
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
const pdf = await render(
|
|
172
|
-
<table>
|
|
173
|
-
<thead>
|
|
174
|
-
<tr>
|
|
175
|
-
<th>Name</th>
|
|
176
|
-
<th>Qty</th>
|
|
177
|
-
</tr>
|
|
178
|
-
</thead>
|
|
179
|
-
<tbody>{rows}</tbody>
|
|
180
|
-
</table>,
|
|
181
|
-
);
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Links, outline, and metadata
|
|
185
|
-
|
|
186
|
-
Anchors with an `href` become clickable link annotations. `<TargetPageNumber />` prints the page a link's target lands on, which is what a table of contents needs. `outline: true` builds PDF bookmarks from `h1` through `h6` headings. `metadata` fills the document properties:
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
const pdf = await render(report, {
|
|
190
|
-
outline: true,
|
|
191
|
-
lang: "en",
|
|
192
|
-
metadata: {
|
|
193
|
-
title: "Annual report 2026",
|
|
194
|
-
authors: ["Acme Inc."],
|
|
195
|
-
creationDate: "2026-08-06",
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Omit `metadata` to keep output byte-identical across runs.
|
|
201
|
-
|
|
202
|
-
## Tagged output and PDF/A
|
|
203
|
-
|
|
204
|
-
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.
|
|
205
|
-
|
|
206
|
-
`<table>` markup lays out and paints, but carries no `Table` structure elements yet.
|
|
207
|
-
|
|
208
|
-
`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).
|
|
209
|
-
|
|
210
|
-
```tsx
|
|
211
|
-
const pdf = await render(report, {
|
|
212
|
-
pdfa: "2a",
|
|
213
|
-
tagged: "ua1",
|
|
214
|
-
lang: "en",
|
|
215
|
-
metadata: { title: "Annual report", creationDate: "2026-08-06" },
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
| Level | What it adds |
|
|
220
|
-
| ------------------------ | ------------------------------------- |
|
|
221
|
-
| `"2b"` / `"2u"` | Basic conformance / Unicode mapping. |
|
|
222
|
-
| `"2a"` / `"3a"` | A tagged structure tree. |
|
|
223
|
-
| `"3b"` / `"3u"` / `"3a"` | Arbitrary file attachments. |
|
|
224
|
-
| `"4"` | The PDF 2.0 revision of the standard. |
|
|
225
|
-
| `"4f"` | PDF 2.0 with file attachments. |
|
|
226
|
-
|
|
227
|
-
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.
|
|
228
|
-
|
|
229
|
-
## Attachments
|
|
230
|
-
|
|
231
|
-
Attach files with `attachments`. They appear in the viewer's attachment panel. Combine with `pdfa: "3b"` for ZUGFeRD and Factur-X electronic invoices:
|
|
232
|
-
|
|
233
|
-
```tsx
|
|
234
|
-
const pdf = await render(invoice, {
|
|
235
|
-
pdfa: "3b",
|
|
236
|
-
metadata: { title: "Invoice 1042", creationDate: "2026-08-06" },
|
|
237
|
-
attachments: [
|
|
238
|
-
{
|
|
239
|
-
name: "factur-x.xml",
|
|
240
|
-
data: xml,
|
|
241
|
-
mimeType: "application/xml",
|
|
242
|
-
description: "Factur-X invoice data",
|
|
243
|
-
relationship: "alternative",
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
});
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
The PDF/A-3 levels require `mimeType`, `description`, and a modification date on each attachment. `metadata.creationDate` serves as the date fallback.
|
|
250
|
-
|
|
251
|
-
## Measuring
|
|
252
|
-
|
|
253
|
-
`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`:
|
|
254
|
-
|
|
255
|
-
```tsx
|
|
256
|
-
import { measure } from "takumi-pdf";
|
|
257
|
-
|
|
258
|
-
const { height } = await measure(footer, { size: "a4" });
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Images and runtimes
|
|
262
|
-
|
|
263
|
-
`takumi-pdf` runs on Node.js, Bun, and Cloudflare Workers.
|
|
264
|
-
|
|
265
|
-
SVG images embed as vectors, not rasterized bitmaps.
|
|
266
|
-
|
|
267
|
-
The renderer does not fetch remote images. Pass pre-fetched bytes for image URLs in the document:
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
270
|
-
const pdf = await render(doc, {
|
|
271
|
-
images: [{ src: "https://example.com/logo.png", data: logoBytes }],
|
|
272
|
-
});
|
|
273
|
-
```
|
|
76
|
+
- **Fonts:** Takumi does not read system fonts. Register fonts that cover your text.
|
|
77
|
+
- **Images:** Fetch remote document images yourself and pass their bytes through `images`.
|
|
78
|
+
- **Page geometry:** Use `size`, `landscape`, and `margin`. CSS `@page` rules are not supported.
|
|
79
|
+
- **Effects:** PDF output rejects CSS `filter: blur()`, `drop-shadow()`, and `backdrop-filter`. Prepare those effects as images before rendering.
|
|
80
|
+
- **Validation:** Tagged PDF is enabled by default. Choose the conformance options your document needs and validate the result.
|
|
274
81
|
|
|
275
|
-
|
|
82
|
+
See the [PDF renderer comparison](https://takumi.kane.tw/docs/pdf/comparison) for recorded benchmarks and rendering differences. Runnable examples cover [invoices and receipts](https://github.com/kane50613/takumi/tree/master/example/generate-invoice) and [Factur-X invoices](https://github.com/kane50613/takumi/tree/master/example/e-invoice).
|
|
276
83
|
|
|
277
84
|
## License
|
|
278
85
|
|