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 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
- [Documentation](https://takumi.kane.tw/docs/) · [Playground](https://takumi.kane.tw/playground)
10
+ [PDF documentation](https://takumi.kane.tw/docs/pdf) · [Playground](https://takumi.kane.tw/playground)
11
11
 
12
12
  </div>
13
13
 
14
- ## Why
14
+ ## How it works
15
15
 
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.
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 with a uniform 48px margin; content flows across as many pages as it needs.
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
- ## Page setup
60
+ ## Choose the next guide
61
61
 
62
- ```tsx
63
- const pdf = await render(report, {
64
- size: "letter",
65
- landscape: true,
66
- margin: { top: 48, right: 32, bottom: 48, left: 32 },
67
- });
68
- ```
69
-
70
- | Option | Type | Default | Description |
71
- | ----------- | ---------------------------------------------- | ------- | ------------------------------------------------------------ |
72
- | `size` | `"a4"`, `"letter"`, or `{ width, height }` | `"a4"` | Page size in CSS px at 96 dpi. Presets ignore case. |
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
- ## Single-page viewport
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
- | Property | Effect |
160
- | ----------------------------- | ------------------------------------------------------- |
161
- | `break-before: page` | Starts the element on a new page. |
162
- | `break-after: page` | Starts the following content on a new page. |
163
- | `break-inside: avoid` | Keeps the element on one page when it fits. |
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
- `@page` CSS rules are not supported. Set page geometry with `size`, `landscape`, and `margin`.
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