renderpdf-js 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/API.md +204 -0
- package/README.md +275 -307
- package/dist/compose-pdf.d.ts +26 -0
- package/dist/document-colors.d.ts +8 -0
- package/dist/document-page.d.ts +115 -0
- package/dist/document-types.d.ts +135 -0
- package/dist/document.d.ts +64 -0
- package/dist/fonts.d.ts +4 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +2051 -552
- package/dist/jspdf-adapter.d.ts +159 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -4,150 +4,197 @@
|
|
|
4
4
|
|
|
5
5
|
# RenderPDF.js
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
to PDF in the browser. HTML exports retain selectable text, local raster fallbacks
|
|
9
|
-
and automatic pagination. The browser calculates HTML layout; RenderPDF.js uses
|
|
10
|
-
its measured text, box, image and table coordinates to write the PDF. Standalone
|
|
11
|
-
PNG/JPEG and multipage TIFF files use a separate image API. Conversion runs
|
|
12
|
-
entirely on the client.
|
|
7
|
+
**Create PDFs from HTML, images or code, and merge or split existing PDFs.**
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
[](https://www.npmjs.com/package/renderpdf-js)
|
|
10
|
+
[](https://github.com/MichalekJan93/renderPDF.js/actions/workflows/check.yml)
|
|
11
|
+
[](https://github.com/MichalekJan93/renderPDF.js/blob/main/LICENSE)
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
RenderPDF.js is a JavaScript and TypeScript library for creating PDF documents,
|
|
14
|
+
converting HTML and images, and working with existing PDF pages. It runs locally,
|
|
15
|
+
without a conversion server.
|
|
16
|
+
|
|
17
|
+
HTML exports use the browser's layout, keep text selectable and split content
|
|
18
|
+
across pages. Document creation and PDF composition also work in Node.js,
|
|
19
|
+
without a DOM.
|
|
20
|
+
|
|
21
|
+
**Experimental.** HTML rendering is verified in Chromium; Firefox and Safari have
|
|
22
|
+
not been verified. Supported HTML and CSS are [described below](#supported-scope).
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- Create documents with text, images, vector graphics, links and form fields.
|
|
27
|
+
- Merge PDFs, extract selected pages and split documents without rasterizing them.
|
|
28
|
+
- Export supported HTML/CSS with selectable text and automatic pagination.
|
|
29
|
+
- Convert PNG/JPEG and multipage TIFF files to PDF.
|
|
30
|
+
- Add repeated table headers, page text, page numbers and document properties.
|
|
31
|
+
- Use the DevExtreme PDF exporter through an optional compatibility adapter.
|
|
32
|
+
- ESM package with TypeScript declarations, cancellation and conversion progress.
|
|
19
33
|
|
|
20
|
-
|
|
34
|
+
See the [API guide](https://github.com/MichalekJan93/renderPDF.js/blob/main/API.md)
|
|
35
|
+
for methods, coordinate conventions and compatibility limits.
|
|
36
|
+
|
|
37
|
+
## Getting started
|
|
21
38
|
|
|
22
39
|
```sh
|
|
23
40
|
npm install renderpdf-js
|
|
24
41
|
```
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
[playground](https://github.com/MichalekJan93/renderPDF.js/tree/main/playground).
|
|
43
|
+
For browser applications, use an ESM bundler such as Vite.
|
|
44
|
+
The [playground](https://github.com/MichalekJan93/renderPDF.js/tree/main/playground)
|
|
45
|
+
provides an HTML editor, preview and PDF download from a source checkout.
|
|
30
46
|
|
|
31
|
-
##
|
|
47
|
+
## Create a document
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
```ts
|
|
50
|
+
import { createDocument } from 'renderpdf-js';
|
|
51
|
+
|
|
52
|
+
const document = await createDocument({ unit: 'mm', format: 'A4' });
|
|
53
|
+
const page = document.addPage();
|
|
54
|
+
page.drawText('Invoice', { x: 20, y: 25, fontSize: 18 });
|
|
55
|
+
page.drawRectangle({ x: 20, y: 35, width: 170, height: 20, stroke: '#333333' });
|
|
56
|
+
|
|
57
|
+
const pdf = await document.toResult();
|
|
58
|
+
await pdf.download('invoice.pdf');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Coordinates start at the top-left; text `y` is its baseline. Font sizes always use
|
|
62
|
+
points. The document API works without a DOM. Built-in Helvetica, Times and Courier
|
|
63
|
+
cover basic text; provide static TTF/OTF bytes in `fonts` for Czech and other Unicode
|
|
64
|
+
characters. Fonts are prepared before drawing. After `toResult()`, the document is
|
|
65
|
+
immutable.
|
|
66
|
+
|
|
67
|
+
Use `pdf.toUint8Array()` or `pdf.toBlob()` outside the browser; `download()` is a
|
|
68
|
+
browser action.
|
|
69
|
+
|
|
70
|
+
## Combine and split PDFs
|
|
36
71
|
|
|
37
72
|
```ts
|
|
38
|
-
import {
|
|
73
|
+
import { mergePDFs, extractPDFPages, splitPDF } from 'renderpdf-js';
|
|
39
74
|
|
|
40
|
-
const
|
|
41
|
-
|
|
75
|
+
const combined = await mergePDFs([firstFile, secondFile]);
|
|
76
|
+
await combined.download('combined.pdf');
|
|
42
77
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
margin: 24,
|
|
46
|
-
metadata: { title: 'Scanned document' },
|
|
47
|
-
onProgress: ({ stage, completed, total }) => {
|
|
48
|
-
statusElement.textContent = `${stage}: ${completed}/${total}`;
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
await pdf.download('document.pdf');
|
|
78
|
+
const selected = await extractPDFPages(firstFile, [3, 1]);
|
|
79
|
+
const individualPages = await splitPDF(firstFile);
|
|
52
80
|
```
|
|
53
81
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
the
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Supported TIFF input is classic TIFF with strips and either byte order, using
|
|
71
|
-
uncompressed, CCITT RLE/Group 3/Group 4, LZW, PackBits or Deflate compression.
|
|
72
|
-
The bounded decoder supports common bilevel/grayscale, palette and 8-bit RGB/RGBA
|
|
73
|
-
profiles, including associated alpha normalization. Other modes, including
|
|
74
|
-
BigTIFF, tiles, SubIFDs/RAW layouts, planar-separated samples, 16-bit/float samples
|
|
75
|
-
and JPEG-compressed TIFF, fail with `UNSUPPORTED_IMAGE`. Frames are limited to
|
|
76
|
-
40 million pixels each and TIFF files to 1,000 main frames. PNG must be static;
|
|
77
|
-
APNG is rejected explicitly. JPEG supports 8-bit baseline/extended/progressive
|
|
78
|
-
grayscale, RGB and CMYK frames.
|
|
79
|
-
|
|
80
|
-
Malformed headers, checksums and out-of-file offsets are rejected. JPEG entropy
|
|
81
|
-
validation and detection of every corrupt CCITT bitstream remain decoder-limited;
|
|
82
|
-
structural checks do not prove that every damaged scan is recoverable. TIFF
|
|
83
|
-
pixels are decoded one frame at a time with the MIT-licensed `utif2` dependency,
|
|
84
|
-
which loads only when TIFF decoding is requested. The HTML `<img>` pipeline still
|
|
85
|
-
accepts native PNG/JPEG resources; standalone TIFF support does not change it.
|
|
82
|
+
Inputs can be `File`/`Blob`, `ArrayBuffer`, `Uint8Array` or a completed export result.
|
|
83
|
+
Page numbers are one-based. Pages retain their native text, vectors, sizes and
|
|
84
|
+
rotation. Flatten interactive forms before importing them; encrypted PDFs and
|
|
85
|
+
unsupported destinations fail explicitly. Document-level bookmarks, signatures and
|
|
86
|
+
metadata are not merged. See the [composition limits](https://github.com/MichalekJan93/renderPDF.js/blob/main/API.md#pdf-composition).
|
|
87
|
+
|
|
88
|
+
## DevExtreme integration
|
|
89
|
+
|
|
90
|
+
`createJsPDFAdapter()` provides the synchronous drawing and text measurement
|
|
91
|
+
contract used by the DevExtreme PDF exporter. Supply fonts when creating it, pass
|
|
92
|
+
the result as `jsPDFDocument`, then **await** `save()` or `output('blob')`.
|
|
93
|
+
The adapter covers the tested exporter contract; it does not emulate every jsPDF
|
|
94
|
+
plugin or its synchronous serialization. The [API guide](https://github.com/MichalekJan93/renderPDF.js/blob/main/API.md#devextreme-adapter)
|
|
95
|
+
includes an example and the optional integration test command. DevExtreme is not
|
|
96
|
+
a dependency of this library.
|
|
86
97
|
|
|
87
98
|
## HTML usage
|
|
88
99
|
|
|
89
|
-
|
|
100
|
+
Register a font, select an existing element and export it:
|
|
90
101
|
|
|
91
102
|
```ts
|
|
92
|
-
import { registerFont, renderPDF
|
|
103
|
+
import { registerFont, renderPDF } from 'renderpdf-js';
|
|
93
104
|
|
|
94
|
-
//
|
|
105
|
+
// Supply a licensed static TTF/OTF file from your application.
|
|
95
106
|
const response = await fetch('/fonts/MyFont-Regular.ttf');
|
|
96
107
|
if (!response.ok) throw new Error('The document font could not be loaded.');
|
|
97
|
-
|
|
108
|
+
|
|
98
109
|
const regular = await registerFont({
|
|
99
110
|
family: 'Document Sans',
|
|
100
111
|
data: await response.arrayBuffer(),
|
|
101
112
|
});
|
|
102
|
-
|
|
113
|
+
|
|
103
114
|
const source = document.querySelector<HTMLElement>('#invoice');
|
|
104
115
|
if (!source) throw new Error('Invoice element was not found.');
|
|
105
116
|
source.style.fontFamily = 'Document Sans';
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
rasterScale: 2,
|
|
115
|
-
fallback: 'auto',
|
|
116
|
-
pageBreak: 'css',
|
|
117
|
-
onWarning: (warning) => console.info(warning.code, warning.message),
|
|
118
|
-
});
|
|
119
|
-
await pdf.download('invoice.pdf');
|
|
120
|
-
// Independent byte and Blob forms do not render the source again.
|
|
121
|
-
const bytes = await pdf.toUint8Array();
|
|
122
|
-
// Applications can preview, store or upload this Blob themselves if required.
|
|
123
|
-
const blob = await pdf.toBlob();
|
|
124
|
-
console.info(pdf.pageCount, bytes.byteLength, blob.type);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (error instanceof RenderPDFError) console.error(error.code, error.message);
|
|
127
|
-
else throw error;
|
|
128
|
-
}
|
|
118
|
+
const pdf = await renderPDF(source, {
|
|
119
|
+
fonts: [regular],
|
|
120
|
+
format: 'A4',
|
|
121
|
+
margin: 24,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await pdf.download('invoice.pdf');
|
|
129
125
|
```
|
|
130
126
|
|
|
131
|
-
Register
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
registrations. Importing the module is safe without a DOM; conversion and font
|
|
137
|
-
registration require a browser document.
|
|
138
|
-
Variable fonts, WOFF/WOFF2 and font collections are rejected; supply static
|
|
139
|
-
TTF/OTF instances. Do not remove or replace registered browser faces during use.
|
|
127
|
+
Register every font family, weight and style used by the source, including bold
|
|
128
|
+
and italic faces. For example, add `weight: 700` when registering a bold font.
|
|
129
|
+
The first computed CSS font family must match a registered face. Static TTF/OTF
|
|
130
|
+
files are required; system fonts, variable fonts, WOFF/WOFF2 and font collections
|
|
131
|
+
are unsupported.
|
|
140
132
|
|
|
141
|
-
|
|
133
|
+
Keep the source connected to the document and unchanged during export. Its width
|
|
134
|
+
and wrapping are preserved, so it must already fit the page's content width:
|
|
135
|
+
about **745.7 CSS pixels** for A4 with the default margins. Oversized HTML is not
|
|
136
|
+
automatically scaled down.
|
|
142
137
|
|
|
143
|
-
|
|
138
|
+
## Standalone image files
|
|
139
|
+
|
|
140
|
+
`imageToPDF` accepts a `File`, `Blob`, `ArrayBuffer` or `Uint8Array`. It detects
|
|
141
|
+
the format from the file contents and requires no HTML element or fonts.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { imageToPDF } from 'renderpdf-js';
|
|
145
|
+
|
|
146
|
+
const fileInput = document.querySelector<HTMLInputElement>('#image');
|
|
147
|
+
const file = fileInput?.files?.[0];
|
|
148
|
+
if (!file) throw new Error('Select a PNG, JPEG or TIFF file.');
|
|
149
|
+
|
|
150
|
+
const pdf = await imageToPDF(file, { format: 'A4', margin: 24 });
|
|
151
|
+
await pdf.download('document.pdf');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
PNG and JPEG produce one page. Each main TIFF frame produces a page in file order.
|
|
155
|
+
Images are centered and scaled to fit within the margins, preserving their aspect
|
|
156
|
+
ratio; small images can be enlarged. TIFF/EXIF orientations, including mirrored
|
|
157
|
+
orientations and PNG `eXIf`, are applied. JPEG compressed bytes are preserved.
|
|
158
|
+
Image text remains a bitmap; there is no OCR.
|
|
159
|
+
|
|
160
|
+
See [image format limits](#image-format-limits) for supported TIFF profiles.
|
|
161
|
+
TIFF support applies to this API; HTML `<img>` elements accept PNG/JPEG.
|
|
162
|
+
|
|
163
|
+
## Options
|
|
164
|
+
|
|
165
|
+
Both conversion functions accept these settings:
|
|
166
|
+
|
|
167
|
+
| Option | Default | Description |
|
|
168
|
+
| ------------- | ----------------------- | --------------------------------------------------------------------- |
|
|
169
|
+
| `format` | `'A4'` | `'A4'`, `'A3'`, `'Letter'` or `{ width, height }`. |
|
|
170
|
+
| `orientation` | `'portrait'` | `'portrait'` or `'landscape'`. |
|
|
171
|
+
| `margin` | `24` | One value or `{ top, right, bottom, left }`. |
|
|
172
|
+
| `metadata` | Title: `'RenderPDF.js'` | Unicode `title`, `author`, `subject` and `keywords` (a string array). |
|
|
173
|
+
| `onProgress` | — | Receives `{ stage, completed, total }`. |
|
|
174
|
+
| `signal` | — | An `AbortSignal` for cancellation. |
|
|
175
|
+
|
|
176
|
+
Dimensions and margins use **CSS pixels**: `1 px = 0.75 PDF pt`. Custom paper
|
|
177
|
+
dimensions include margins; landscape swaps the supplied width and height.
|
|
178
|
+
|
|
179
|
+
HTML exports also accept:
|
|
180
|
+
|
|
181
|
+
| Option | Default | Description |
|
|
182
|
+
| -------------------- | -------- | -------------------------------------------------------------------------------------- |
|
|
183
|
+
| `fonts` | Required | Registered faces used by the source and page text. |
|
|
184
|
+
| `pageBreak` | `'css'` | Honor supported CSS breaks, or use `'auto'` for geometry alone. |
|
|
185
|
+
| `repeatTableHeaders` | `false` | Repeat native `thead` groups on continuation pages. |
|
|
186
|
+
| `header`, `footer` | — | One plain text line in a reserved area on every page. |
|
|
187
|
+
| `fallback` | `'auto'` | Rasterize supported local effects; `'strict'` requires explicit `data-raster` regions. |
|
|
188
|
+
| `rasterScale` | `2` | Raster pixels per CSS pixel. |
|
|
189
|
+
| `onWarning` | — | Receives export warnings. |
|
|
190
|
+
|
|
191
|
+
### Repeated table headers and page text
|
|
144
192
|
|
|
145
193
|
```ts
|
|
146
194
|
const pdf = await renderPDF(source, {
|
|
147
195
|
fonts: [regular],
|
|
148
|
-
margin: 24,
|
|
149
196
|
repeatTableHeaders: true,
|
|
150
|
-
header: { text: 'Account statement', height: 32
|
|
197
|
+
header: { text: 'Account statement', height: 32 },
|
|
151
198
|
footer: {
|
|
152
199
|
text: 'Page {pageNumber} of {pageCount}',
|
|
153
200
|
height: 28,
|
|
@@ -158,233 +205,154 @@ const pdf = await renderPDF(source, {
|
|
|
158
205
|
await pdf.download('statement.pdf');
|
|
159
206
|
```
|
|
160
207
|
|
|
161
|
-
`
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
visible `thead` followed by body rows; raster headers, overlapping table ranges,
|
|
168
|
-
header rowspans and cells spanning the header/body boundary are unsupported. Table footers are
|
|
169
|
-
not repeated.
|
|
170
|
-
Header paint must fit inside the measured `thead` box; text overflowing into its
|
|
171
|
-
surrounding spacing fails explicitly instead of being clipped on continuation pages.
|
|
172
|
-
|
|
173
|
-
`header` and `footer` each reserve their required `height` in CSS pixels **in
|
|
174
|
-
addition to** the page's outer margins. They accept one plain Latin text line,
|
|
175
|
-
including Czech diacritics, with `{pageNumber}` and `{pageCount}` placeholders.
|
|
176
|
-
The text is black, vertically centered, and remains selectable. `fontSize`
|
|
177
|
-
defaults to 12 CSS pixels and `align` to `'left'`; `'center'` and `'right'` are
|
|
178
|
-
also supported. `font` can select a registered handle included in `fonts`, and
|
|
179
|
-
otherwise defaults to the first supplied face. Band text never wraps or shrinks
|
|
180
|
-
silently: text that exceeds its reserved height or available width fails with
|
|
181
|
-
`SOURCE_OVERFLOW`. Invalid settings or bands leaving no content area fail with
|
|
182
|
-
`INVALID_OPTIONS`. HTML templates and per-page callbacks are not supported.
|
|
208
|
+
Header and footer `height` is required and reserves space **in addition to** the
|
|
209
|
+
outer margins. Each accepts one plain Latin text line, including Czech diacritics,
|
|
210
|
+
with `{pageNumber}` and `{pageCount}` placeholders. Text stays selectable and does
|
|
211
|
+
not wrap. `fontSize` defaults to `12`, `align` to `'left'`, and `font` to the first
|
|
212
|
+
face in `fonts`. Alignment can also be `'center'` or `'right'`. Text that does not
|
|
213
|
+
fit fails explicitly. HTML page templates and per-page callbacks are unsupported.
|
|
183
214
|
|
|
184
215
|
### Export progress and document properties
|
|
185
216
|
|
|
186
|
-
Progress reporting and document properties are available from version 0.2.0.
|
|
187
|
-
|
|
188
217
|
```ts
|
|
189
218
|
const pdf = await renderPDF(source, {
|
|
190
219
|
fonts: [regular],
|
|
191
|
-
metadata: {
|
|
192
|
-
title: 'Account statement',
|
|
193
|
-
author: 'Example Company',
|
|
194
|
-
subject: 'Quarterly overview',
|
|
195
|
-
keywords: ['statement', 'quarterly'],
|
|
196
|
-
},
|
|
220
|
+
metadata: { title: 'Account statement', author: 'Example Company' },
|
|
197
221
|
onProgress: ({ stage, completed, total }) => {
|
|
198
|
-
|
|
199
|
-
statusElement.textContent = `${stage}: ${completed}/${total}`;
|
|
222
|
+
console.log(stage, completed, total);
|
|
200
223
|
},
|
|
201
224
|
});
|
|
202
|
-
await pdf.download('statement.pdf');
|
|
203
225
|
```
|
|
204
226
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
content or change font requirements.
|
|
227
|
+
Counters describe work within the current stage, rather than elapsed-time
|
|
228
|
+
percentages. HTML stages are `preparing`, `layout`, `pagination`, `resources`,
|
|
229
|
+
`pages`, `saving` and `complete`. Image conversion omits `layout` and `pagination`.
|
|
230
|
+
Callbacks are synchronous; a thrown callback error rejects the export. Cancellation
|
|
231
|
+
is cooperative between stages and rejects with `AbortError`, without a partial PDF.
|
|
232
|
+
|
|
233
|
+
## Output and errors
|
|
234
|
+
|
|
235
|
+
Both functions return the same result:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
await pdf.download('document.pdf');
|
|
239
|
+
const blob = await pdf.toBlob();
|
|
240
|
+
const bytes = await pdf.toUint8Array();
|
|
241
|
+
console.log(pdf.pageCount, pdf.warnings);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
These output methods reuse the completed PDF. `RenderPDFError`, exported from the
|
|
245
|
+
package, exposes a `code` and `message` for conversion failures. Warnings are
|
|
246
|
+
available through `onWarning` for HTML exports and `pdf.warnings`. Unsupported
|
|
247
|
+
input fails explicitly instead of silently producing a whole-page screenshot.
|
|
227
248
|
|
|
228
249
|
## Supported scope
|
|
229
250
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
Collapsed
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
and the immutable `result.warnings` collection, without runtime console logging.
|
|
305
|
-
|
|
306
|
-
PDF text uses browser word origins and fontkit's shaped glyph advances and offsets;
|
|
307
|
-
glyph outlines retain their original proportions. Default OpenType ligatures,
|
|
308
|
-
kerning (including `font-kerning: none`) and combining marks retain a selectable
|
|
309
|
-
Unicode text layer. Baselines still use Canvas font metrics. Nondefault font
|
|
310
|
-
features/variants, stretched faces, font-size adjustment, automatic hyphenation
|
|
311
|
-
and soft hyphens fail explicitly. Keep shaped words in one text node; cross-inline
|
|
312
|
-
ligatures and zero-width text fragments are unsupported and reject explicitly.
|
|
313
|
-
Exact output across shaping engines, arbitrary ligatures, complex scripts, bidi
|
|
314
|
-
and variable font instances are not a fidelity promise. There is no arbitrary CSS/stacking
|
|
315
|
-
engine. Collapsed tables, repeated table headers and plain page text
|
|
316
|
-
use the explicit options described above; arbitrary HTML page templates are unsupported.
|
|
317
|
-
Pagination is a measured source-to-page mapping, not full CSS Paged Media: parity
|
|
318
|
-
breaks (`left/right/recto/verso`), column breaks, inline/cell break directives,
|
|
319
|
-
margin-break rules, widow/orphan line-count policies and cloned page decorations
|
|
320
|
-
are unsupported. Nondefault native `widows`/`orphans` and
|
|
321
|
-
`box-decoration-break: clone` fail explicitly. Default line-count values are not
|
|
322
|
-
enforced. Unsupported visible behavior must fail rather than silently produce an
|
|
323
|
-
apparently successful export.
|
|
324
|
-
Direct text overlapping later visible paint is also rejected when the simple
|
|
325
|
-
ordered paint list cannot reproduce the browser's block/inline paint phases.
|
|
251
|
+
HTML rendering supports static blocks, inline content, nonoverlapping Flexbox and
|
|
252
|
+
Grid layouts, horizontal Latin text (including Czech diacritics), solid backgrounds,
|
|
253
|
+
rectangular borders and native tables with `rowspan` and `colspan`.
|
|
254
|
+
|
|
255
|
+
PNG/JPEG resources can use data, blob, same-origin or CORS-readable URLs. External
|
|
256
|
+
images may be fetched by the browser; documents are never sent to a conversion
|
|
257
|
+
service. JPEG orientation in HTML must be absent or `1`; normalize rotated pixels
|
|
258
|
+
before using them in an `<img>` element.
|
|
259
|
+
|
|
260
|
+
Gradients, rounded corners, inline bitmap backgrounds, inset shadows and opacity
|
|
261
|
+
can use local raster fallback on supported rectangular blocks. `data-raster`
|
|
262
|
+
selects a region explicitly. Regions are captured once in paint order; descendant
|
|
263
|
+
text gets an extraction layer where safe. Raster resources must be inline bitmaps;
|
|
264
|
+
external resources, transforms, backdrop effects and effects outside the border
|
|
265
|
+
box are unsupported. Wrapped inline backgrounds or borders require a containing
|
|
266
|
+
raster region; inline and table-internal raster boundaries are unsupported.
|
|
267
|
+
|
|
268
|
+
### Pagination and tables
|
|
269
|
+
|
|
270
|
+
The default `pageBreak: 'css'` respects block/table-row `break-before: page`,
|
|
271
|
+
`break-after: page`, their legacy `page-break-*: always` aliases and
|
|
272
|
+
`break-inside: avoid/avoid-page`. Before/after avoidance keeps adjacent complete
|
|
273
|
+
block siblings together when they fit.
|
|
274
|
+
|
|
275
|
+
Text lines, images, raster regions, table rows and spanning cells stay whole.
|
|
276
|
+
Content taller than a page, or a forced break through an indivisible item, fails
|
|
277
|
+
with `PAGINATION_FAILED`. An avoid preference that cannot be honored can be relaxed
|
|
278
|
+
with a `BREAK_AVOID_RELAXED` warning. Parity/column breaks, inline/cell break rules,
|
|
279
|
+
widow/orphan policies and cloned page decorations are unsupported.
|
|
280
|
+
|
|
281
|
+
<details>
|
|
282
|
+
<summary>Table restrictions</summary>
|
|
283
|
+
|
|
284
|
+
Collapsed tables support solid, none and hidden borders on cells and the outer
|
|
285
|
+
table, including different widths, colors and transparency. Shared borders are
|
|
286
|
+
painted once. Row, row-group, column and column-group border contributions, other
|
|
287
|
+
border styles, captions, nested collapsed tables and altered table-part display
|
|
288
|
+
modes are unsupported.
|
|
289
|
+
|
|
290
|
+
Repeated headers require a visible native `thead` followed by body rows. The
|
|
291
|
+
header reserves space on continuation pages and stays with the first body row.
|
|
292
|
+
Headers and rows must fit on a page. Nested tables, raster headers, overlapping
|
|
293
|
+
table ranges, header rowspans and cells spanning the header/body boundary are
|
|
294
|
+
unsupported. Header paint must fit its measured box. Table footers are not repeated.
|
|
295
|
+
Collapsed tables also require matching resolved horizontal borders at every
|
|
296
|
+
possible header/body continuation boundary.
|
|
297
|
+
|
|
298
|
+
</details>
|
|
299
|
+
|
|
300
|
+
### Text and layout limits
|
|
301
|
+
|
|
302
|
+
Default kerning, ligatures and combining marks are supported. Keep shaped words in
|
|
303
|
+
one text node; cross-inline ligatures and zero-width fragments are unsupported.
|
|
304
|
+
Complex scripts, bidirectional text, custom font features/variants, stretched
|
|
305
|
+
faces, font-size adjustment, automatic hyphenation and soft hyphens are unsupported.
|
|
306
|
+
Exact text appearance can vary between shaping engines.
|
|
307
|
+
|
|
308
|
+
Arbitrary CSS stacking and overlapping paint are unsupported. Pause animations
|
|
309
|
+
and keep source styles, geometry and registered fonts stable during export.
|
|
310
|
+
Changes to the source fail with `SOURCE_CHANGED`.
|
|
311
|
+
|
|
312
|
+
### Image format limits
|
|
313
|
+
|
|
314
|
+
- **PNG:** static images only; APNG is rejected.
|
|
315
|
+
- **JPEG:** 8-bit baseline, extended and progressive grayscale, RGB or CMYK.
|
|
316
|
+
- **TIFF:** classic stripped TIFF in either byte order; common bilevel/grayscale,
|
|
317
|
+
palette and 8-bit RGB/RGBA profiles. Compression: uncompressed, CCITT RLE/Group
|
|
318
|
+
3/Group 4, LZW, PackBits and Deflate. Limited to **40 million pixels per frame**
|
|
319
|
+
and **1,000 main frames**.
|
|
320
|
+
|
|
321
|
+
BigTIFF, tiled TIFF, SubIFDs/RAW layouts, planar-separated samples, 16-bit/float
|
|
322
|
+
samples and JPEG-compressed TIFF fail with `UNSUPPORTED_IMAGE`. The TIFF decoder
|
|
323
|
+
loads only when needed. Structural checks reject malformed input, but cannot
|
|
324
|
+
identify every damaged JPEG scan or CCITT bitstream.
|
|
326
325
|
|
|
327
326
|
## Development
|
|
328
327
|
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
Use Node.js 22.18+ (22.x), 24.11+ (24.x) or 26+. `.node-version` records the
|
|
329
|
+
verified runtime.
|
|
331
330
|
|
|
332
331
|
```sh
|
|
333
332
|
npm ci
|
|
334
333
|
npm run build
|
|
334
|
+
npm run check
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
the
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
The playground deliberately uses one explicitly stated font family;
|
|
352
|
-
the reusable API supports multiple families. Private documents and local system
|
|
353
|
-
font programs belong only in ignored `artifacts/`; regression tests use synthetic
|
|
354
|
-
documents and do not require them.
|
|
355
|
-
|
|
356
|
-
Run `npm run check` before submitting a change. It checks types, lint, unit tests,
|
|
357
|
-
browser tests, builds, formatting and package contents. Browser tests require an installed Google
|
|
358
|
-
Chrome and use synthetic fixtures, Playwright and PDF.js; those tools never run in
|
|
359
|
-
conversion code. Generated PDFs, screenshots and reports stay outside versioned
|
|
360
|
-
source files.
|
|
361
|
-
|
|
362
|
-
Typography regressions compare tight browser/PDF text crops for Noto Sans regular,
|
|
363
|
-
bold and italic plus Noto Serif, including Czech accents, ligatures, kerning and
|
|
364
|
-
wrapped mixed-font lines on multiple pages. The [benchmark guide](https://github.com/MichalekJan93/renderPDF.js/blob/main/BENCHMARKS.md)
|
|
365
|
-
describes repeatable exports of small, medium, large and hybrid documents.
|
|
366
|
-
Run `npm run benchmark` to record timings separately from correctness checks.
|
|
367
|
-
GitHub Actions runs the checks and preserves rendered comparisons and timing reports.
|
|
368
|
-
|
|
369
|
-
### Local package
|
|
370
|
-
|
|
371
|
-
`npm pack --pack-destination artifacts` builds the library and checks its contents
|
|
372
|
-
before creating a local archive. Create the `artifacts/` directory first if it is
|
|
373
|
-
absent. The package includes the ESM entry, TypeScript declarations, package metadata,
|
|
374
|
-
README, MIT license and README logo. Demos, test fonts, source files and local
|
|
375
|
-
documents are excluded by an explicit `files` allowlist.
|
|
376
|
-
|
|
377
|
-
`npm run check:package` checks the current build through npm's dry-run inventory;
|
|
378
|
-
run `npm run build:library` first when no build exists. The check rejects missing
|
|
379
|
-
entry points, empty files and unexpected contents. Packaging does not publish the
|
|
380
|
-
library. Maintainers publish a reviewed archive with
|
|
381
|
-
`npm publish ./artifacts/renderpdf-js-<version>.tgz --access public` after updating
|
|
382
|
-
the package version and verifying the packed consumer.
|
|
337
|
+
`npm run check` runs formatting, lint, types, unit tests, browser tests, build and
|
|
338
|
+
package checks. Google Chrome must be installed for browser tests. Vite+ provides
|
|
339
|
+
the development toolchain; Playwright and PDF.js are used for testing.
|
|
340
|
+
|
|
341
|
+
Run `npm run dev` to serve synthetic test fixtures. The independent
|
|
342
|
+
[playground](https://github.com/MichalekJan93/renderPDF.js/tree/main/playground)
|
|
343
|
+
tests the packaged library. See the
|
|
344
|
+
[benchmark guide](https://github.com/MichalekJan93/renderPDF.js/blob/main/BENCHMARKS.md)
|
|
345
|
+
for `npm run benchmark` and timing reports.
|
|
346
|
+
|
|
347
|
+
To create a local package, create `artifacts/` if needed, then run
|
|
348
|
+
`npm pack --pack-destination artifacts`. Only the library build, declarations,
|
|
349
|
+
package metadata, README, license and logo are packaged. Keep private documents
|
|
350
|
+
and generated files out of Git.
|
|
383
351
|
|
|
384
352
|
## License
|
|
385
353
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
354
|
+
[MIT](https://github.com/MichalekJan93/renderPDF.js/blob/main/LICENSE).
|
|
355
|
+
Third-party dependencies retain their own licenses: `utif2` uses MIT and `pako`
|
|
356
|
+
retains MIT/Zlib notices. Noto Sans and Noto Serif test fonts retain the
|
|
357
|
+
[SIL Open Font License](https://github.com/MichalekJan93/renderPDF.js/blob/main/public/fonts/README.md)
|
|
358
|
+
and are not included in the npm package.
|