@reportwright/pdf 0.1.0-beta.10 → 0.1.0-beta.11
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 +196 -65
- package/dist/fflate.js +2 -2341
- package/dist/fflate.js.map +6 -0
- package/dist/index.js +104 -18112
- package/dist/index.js.map +6 -0
- package/dist/write.js +108 -0
- package/dist/write.js.map +6 -0
- package/examples/p12-signer.mjs +30 -0
- package/index.d.ts +139 -4
- package/package.json +70 -10
- package/write.d.ts +89 -0
package/README.md
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **Safe by default**: no JavaScript or launch actions can be written; active content is stripped on read; every
|
|
32
|
-
input is budgeted.
|
|
1
|
+
<h1 align="center">@reportwright/pdf</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<b>Create, comply, sign, light edit.</b><br>
|
|
5
|
+
A streaming PDF library for Node and browsers with <b>zero runtime dependencies</b>.
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://www.npmjs.com/package/@reportwright/pdf"><img alt="npm" src="https://img.shields.io/npm/v/@reportwright/pdf?color=1f4e79"></a>
|
|
10
|
+
<a href="https://github.com/MrArun005/reportwright-pdf/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/MrArun005/reportwright-pdf/actions/workflows/ci.yml/badge.svg"></a>
|
|
11
|
+
<a href="./LICENSE"><img alt="license: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
12
|
+
<a href="./package.json"><img alt="runtime dependencies: 0" src="https://img.shields.io/badge/runtime%20dependencies-0-brightgreen"></a>
|
|
13
|
+
<a href="./package.json"><img alt="node >= 20.16" src="https://img.shields.io/badge/node-%3E%3D20.16-339933"></a>
|
|
14
|
+
<a href="./conformance"><img alt="PDF/A + PDF/UA: veraPDF clean" src="https://img.shields.io/badge/PDF%2FA%201b%E2%80%933a%20%2B%20UA--1-veraPDF%20clean-6f42c1"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://mrarun005.github.io/reportwright-demo">Live demo</a> ·
|
|
19
|
+
<a href="https://mrarun005.github.io/reportwright-demo/play.html">Playground</a> ·
|
|
20
|
+
<a href="#benchmarks">Benchmarks</a> ·
|
|
21
|
+
<a href="#security-model">Security model</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
Pages are written to your sink as they end, so memory stays flat however many pages you write: 20,000 pages hold
|
|
25
|
+
about the heap that 1,000 do. It is the writer inside ReportWright, extracted, plus a hardened reader for merging,
|
|
26
|
+
splitting, filling and signing existing files.
|
|
27
|
+
|
|
28
|
+
It does **not** render PDFs (use [pdf.js](https://mozilla.github.io/pdf.js/) or
|
|
29
|
+
[PDFium](https://pdfium.googlesource.com/pdfium/)), and for heavy rewriting, repair or raw speed MuPDF, qpdf and
|
|
30
|
+
pdfcpu are faster (see [Benchmarks](#benchmarks)).
|
|
33
31
|
|
|
34
32
|
## Contents
|
|
35
33
|
|
|
36
|
-
[Install](#install) · [Quick start](#quick-start-30-seconds) · [
|
|
37
|
-
[
|
|
38
|
-
[Known limits](#known-limits) · [Changes](#changes)
|
|
34
|
+
[Install](#install) · [Quick start](#quick-start-30-seconds) · [Features](#features) · [Screenshots](#screenshots) ·
|
|
35
|
+
[Guide](#guide) · [Bundle size](#bundle-size) · [Examples](#examples) · [Benchmarks](#benchmarks) · [Options reference](#options-reference) ·
|
|
36
|
+
[Security model](#security-model) · [Known limits](#known-limits) · [Releasing](#releasing) · [Changes](#changes)
|
|
39
37
|
|
|
40
38
|
## Install
|
|
41
39
|
|
|
@@ -43,8 +41,8 @@ than them.
|
|
|
43
41
|
npm i @reportwright/pdf
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
ESM only
|
|
47
|
-
included (`index.d.ts`). Optional, for complex scripts only: `npm i harfbuzzjs`.
|
|
44
|
+
ESM only. Node 20.16 or later, and modern browsers (the same `dist/index.js`; crypto comes from WebCrypto). Types are
|
|
45
|
+
included (`index.d.ts`). Optional, for complex scripts (Indic, Arabic) only: `npm i harfbuzzjs`.
|
|
48
46
|
|
|
49
47
|
## Quick start (30 seconds)
|
|
50
48
|
|
|
@@ -63,6 +61,40 @@ console.log(await pdf.end()); // { pages: 1,
|
|
|
63
61
|
|
|
64
62
|
In memory instead of a stream: `const bytes = await toBytes(async (pdf) => { … }, options)`.
|
|
65
63
|
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
| Area | What you get |
|
|
67
|
+
|---|---|
|
|
68
|
+
| **Write** | Text in the 14 standard fonts or embedded TrueType/OpenType (subset, kerned, any Unicode); text boxes with wrapping and alignment; JPEG/PNG images (alpha as a soft mask); Canvas-style vector paths, gradients, patterns, layers |
|
|
69
|
+
| **Complex scripts** | Devanagari, Bengali, Tamil, Telugu, Kannada, Arabic… through a HarfBuzz shaper you plug in (`harfbuzzjs`), text still extractable |
|
|
70
|
+
| **Streaming** | Each page is written and freed when it ends; flat memory for 20,000+ pages; `timeoutMs` and `AbortSignal` |
|
|
71
|
+
| **Navigate** | Links, named destinations, bookmarks, page labels, annotations |
|
|
72
|
+
| **Forms** | Every AcroForm field type with appearance streams; fill and flatten existing forms |
|
|
73
|
+
| **Protect** | AES-256 / AES-128 encryption with permissions |
|
|
74
|
+
| **Sign** | PAdES B-B / B-T signatures while streaming; incremental countersignatures that keep earlier ones valid |
|
|
75
|
+
| **Comply** | PDF/A-1b, 2b/2u/2a, 3b/3u/3a and PDF/UA-1 (veraPDF clean); Factur-X / ZUGFeRD / XRechnung invoices |
|
|
76
|
+
| **Read and edit** | Load (also damaged or encrypted files), merge, split, reorder, extract text, fill, stamp, `saveIncremental` |
|
|
77
|
+
| **Bulk** | One task over many files in worker threads, each with a heap cap and a per-file timeout; one bad file never stops the batch |
|
|
78
|
+
| **Safe by default** | No JavaScript or launch actions can be written; active content is stripped on read; every input is budgeted |
|
|
79
|
+
| **Small** | Zero runtime dependencies; one ESM file plus a lazily loaded `fflate.js`; TypeScript types included |
|
|
80
|
+
|
|
81
|
+
## Screenshots
|
|
82
|
+
|
|
83
|
+
Page 1 of the [examples](#examples), rendered by poppler (`pdftoppm`) on GitHub Actions straight from their output.
|
|
84
|
+
|
|
85
|
+
<table>
|
|
86
|
+
<tr>
|
|
87
|
+
<td align="center"><img src="https://raw.githubusercontent.com/MrArun005/reportwright-pdf/main/docs/screenshots/write.png" width="240" alt="write.mjs: a report with a logo, a justified paragraph, a table drawn with paths and a link"><br><code>write.mjs</code><br>text, image, table, link</td>
|
|
88
|
+
<td align="center"><img src="https://raw.githubusercontent.com/MrArun005/reportwright-pdf/main/docs/screenshots/invoice-facturx.png" width="240" alt="invoice-facturx.mjs: a Factur-X PDF/A-3a invoice in Inter"><br><code>invoice-facturx.mjs</code><br>Factur-X, PDF/A-3a</td>
|
|
89
|
+
<td align="center"><img src="https://raw.githubusercontent.com/MrArun005/reportwright-pdf/main/docs/screenshots/indic.png" width="240" alt="indic.mjs: Devanagari text shaped by HarfBuzz mixed with English"><br><code>indic.mjs</code><br>Devanagari via HarfBuzz</td>
|
|
90
|
+
</tr>
|
|
91
|
+
<tr>
|
|
92
|
+
<td align="center"><img src="https://raw.githubusercontent.com/MrArun005/reportwright-pdf/main/docs/screenshots/fill-form.png" width="240" alt="fill-form.mjs: a leave request form, filled in"><br><code>fill-form.mjs</code><br>a form, filled</td>
|
|
93
|
+
<td align="center"><img src="https://raw.githubusercontent.com/MrArun005/reportwright-pdf/main/docs/screenshots/accessible.png" width="240" alt="accessible.mjs: a tagged PDF/UA-1 and PDF/A-2a policy page"><br><code>accessible.mjs</code><br>PDF/UA-1 + PDF/A-2a, tagged</td>
|
|
94
|
+
<td></td>
|
|
95
|
+
</tr>
|
|
96
|
+
</table>
|
|
97
|
+
|
|
66
98
|
## Guide
|
|
67
99
|
|
|
68
100
|
Every snippet below is cut from a file in [`examples/`](./examples), each of which was run and produces a PDF. The
|
|
@@ -297,6 +329,21 @@ from the best of 3 calls in one process (warm), and the first call's ms (cold);
|
|
|
297
329
|
| large | encrypt | 1 | 149 | 155 | 213 | 212 | 181 |
|
|
298
330
|
| large | encrypt | 4 | 93 | 217 | 218 | 210 | 416 |
|
|
299
331
|
|
|
332
|
+
## Bundle size
|
|
333
|
+
|
|
334
|
+
Gzip (`gzip -9`) of the built files in `dist/`. `.` is `dist/index.js` (writer and reader); `./write` is `dist/write.js`
|
|
335
|
+
(writer only: `createPdf`, fonts, images, graphics, forms, standards, encryption, signing). fflate is a separate file
|
|
336
|
+
(`dist/fflate.js`) loaded on first use by either entry.
|
|
337
|
+
|
|
338
|
+
| entry | before (unminified) | after (minified) |
|
|
339
|
+
| --- | --- | --- |
|
|
340
|
+
| `.` (`dist/index.js`) | 166.8 KiB (170,811 B) | 133.4 KiB (136,583 B) |
|
|
341
|
+
| `./write` (`dist/write.js`) | not exported | 85.0 KiB (87,080 B) |
|
|
342
|
+
| `fflate` (`dist/fflate.js`) | 15.5 KiB (15,842 B) | 12.6 KiB (12,953 B) |
|
|
343
|
+
|
|
344
|
+
Import `@reportwright/pdf/write` when the code only writes PDFs: it leaves out the reader (`loadPdf`, `mergePdfs`) and
|
|
345
|
+
`bulk`. The two entries are separate bundles, so an app that imports both pays for the writer twice.
|
|
346
|
+
|
|
300
347
|
## Examples
|
|
301
348
|
|
|
302
349
|
| file | what it shows | run |
|
|
@@ -315,30 +362,41 @@ from the best of 3 calls in one process (warm), and the first call's ms (cold);
|
|
|
315
362
|
|
|
316
363
|
Run from `node_modules/@reportwright/pdf` (or the package folder of the repository).
|
|
317
364
|
|
|
318
|
-
##
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
|
330
|
-
|
|
331
|
-
|
|
|
332
|
-
|
|
|
333
|
-
|
|
|
334
|
-
|
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
365
|
+
## Benchmarks
|
|
366
|
+
|
|
367
|
+
From the deep benchmark of the ReportWright project: every library in its own process, median of 5 runs after a
|
|
368
|
+
warm-up, contestants interleaved on the same GitHub `ubuntu-latest` runner (2 vCPU), every output checked (qpdf,
|
|
369
|
+
MuPDF, pdf.js or the job's own text check) before it is ranked. Times are wall time from process start to exit, so they include each runtime's
|
|
370
|
+
start-up. Full tables, method and raw numbers:
|
|
371
|
+
**[bench/deep/RESULTS.md](https://github.com/MrArun005/reportwright/blob/bench-deep/bench/deep/RESULTS.md)**
|
|
372
|
+
(the `@reportwright/pdf` measured there is the 0.1.0-beta.7 code).
|
|
373
|
+
|
|
374
|
+
**Where it wins**
|
|
375
|
+
|
|
376
|
+
| Task | @reportwright/pdf | Next best |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| PDF/A-2b invoice (veraPDF-checked) | 255 ms · 75 MB | pdfkit 543 ms · 103 MB (the only other writer with PDF/A in the test) |
|
|
379
|
+
| Fill and flatten a form | **100 ms** | PyMuPDF / pypdf 139 ms, pdf-lib 296 ms, pdfbox 669 ms |
|
|
380
|
+
| Sign (PAdES/CMS) | **~100 ms** | pdfbox 705 ms, openpdf 760 ms, iText 1,113 ms |
|
|
381
|
+
| Incremental metadata change | 100-125 ms | PyMuPDF / pypdf 139 ms, pdfbox 463 ms |
|
|
382
|
+
| 1,000 pages of text, against other JS writers | 3.1 s · 135 MB | pdfkit 4.5 s, jsPDF 5.8 s, pdf-lib 215 s |
|
|
383
|
+
| Reading fuzzed PDFs (200 mutations) | 182 read, 18 clean errors, 0 crashes or hangs | pdfbox 176 read; most readers refuse far more |
|
|
384
|
+
|
|
385
|
+
**Where it loses** (honestly)
|
|
386
|
+
|
|
387
|
+
| Task | @reportwright/pdf | Fastest |
|
|
388
|
+
|---|---|---|
|
|
389
|
+
| Plain text pages (10 / 100 / 1,000) | 0.30 / 0.65 / 3.1 s | fpdf (PHP) 0.05 / 0.11 / 0.57 s: **5-6× faster** |
|
|
390
|
+
| Grouped report, 100 to 100,000 rows | 2.2-5.2× slower | fpdf |
|
|
391
|
+
| One invoice in a warm process | 11 ms | PDFsharp (.NET) 1.7× faster |
|
|
392
|
+
| Images (6 MP PNG + 24 MP JPEG) | 1.2 s | printpdf (Rust) 3.3× faster |
|
|
393
|
+
| Unicode (Hindi, Arabic, CJK) | 3.1× slower | fpdf |
|
|
394
|
+
| Text of a 1,200-page PDF | 1.0 s | pdftotext 0.65 s (1.5× faster) |
|
|
395
|
+
| Merge 100 files / split 100 pages / AES-256 encrypt | 204 / 217 / 147 ms | pdfcpu (Go) 3-4.3× faster |
|
|
396
|
+
|
|
397
|
+
In short: it is not the fastest PDF writer or reader. It is a fast JavaScript one with flat memory, and it is the
|
|
398
|
+
quickest of those measured at the jobs it is built for (compliance, forms, signing, incremental edits), where most
|
|
399
|
+
libraries have no support at all.
|
|
342
400
|
|
|
343
401
|
## Options reference
|
|
344
402
|
|
|
@@ -394,11 +452,15 @@ writer to be ready. `pdf.end()` ends a Node stream and closes a `WritableStream`
|
|
|
394
452
|
- `await pdf.embedFont(bytes, { subset, shaper, textMapping })` — a TrueType (`glyf`) or OpenType/CFF (`.otf`) font. Any Unicode
|
|
395
453
|
text (Identity-H with a ToUnicode map, so text can be copied and searched). TrueType embeds as `FontFile2`
|
|
396
454
|
(CIDFontType2); OpenType/CFF as `FontFile3 /Subtype /OpenType` (CIDFontType0; `pdffonts` says "CID Type 0C (OT)").
|
|
397
|
-
`subset`: `true` (default) keeps only the glyphs used, both kinds built in (
|
|
455
|
+
`subset`: `true` (default) keeps only the glyphs used, both kinds built in (TrueType: the glyphs used, their
|
|
456
|
+
components and `.notdef` are renumbered 0…n and only `glyf loca head hhea hmtx maxp` and the hinting tables
|
|
457
|
+
`cvt fpgm prep` are kept; the content still draws by the original glyph ID, through a `/CIDToGIDMap` stream, so
|
|
458
|
+
ToUnicode and widths are unchanged. CFF: unused glyphs' charstrings become
|
|
398
459
|
`endchar`, subroutines are kept, glyph IDs stay; a font using the deprecated `seac` accents embeds whole; a
|
|
399
460
|
CID-keyed CFF gets an identity charset so every reader maps its glyphs alike); `false` embeds the whole font; or a
|
|
400
461
|
function `(font, codePoints, { glyphs }) => Promise<Uint8Array>` (e.g. a HarfBuzz subsetter, which also
|
|
401
|
-
desubroutinises CFF) that must keep glyph IDs
|
|
462
|
+
desubroutinises CFF) that must keep glyph IDs (its font is embedded as returned, `/CIDToGIDMap /Identity`, as
|
|
463
|
+
`core` callers that write their own fonts expect). `shaper`: see Shaping.
|
|
402
464
|
A variable font (an `fvar` table) is drawn at its default instance, which may be its thinnest weight (Noto Sans JP's
|
|
403
465
|
is Thin), with a warning naming the axes' values: for another weight, embed a static instance of it (fonttools
|
|
404
466
|
`varLib.instancer`, or `hb-subset --instance`).
|
|
@@ -797,6 +859,11 @@ f.signature('sig', { page, x: 40, y: 360, width: 200, height: 40, tooltip: 'Sign
|
|
|
797
859
|
- An embedded font in a fillable field is subset to the characters drawn; for fields people will type into, embed it
|
|
798
860
|
with `subset: false` so a viewer has every glyph.
|
|
799
861
|
|
|
862
|
+
A caller that draws its own appearances (pdf.core users) passes `appearance: { normal }` (a checkbox: `{ on, off }`)
|
|
863
|
+
of content-stream operators, written exactly, and `da` (`'/F1 12 Tf 0 g'`, a font it registered with
|
|
864
|
+
`core.resource('Font', …)`): with both, no `font` is needed. Each field's `widgetRef` is its widget's object number,
|
|
865
|
+
and `structParent` puts a `/StructParent` on it for a structure tree of the caller's own.
|
|
866
|
+
|
|
800
867
|
### Encryption
|
|
801
868
|
|
|
802
869
|
```js
|
|
@@ -858,6 +925,11 @@ await pdf.end(); // the signer runs here
|
|
|
858
925
|
AcroForm gets `/SigFlags 3`. **One signature** a document (more need incremental updates).
|
|
859
926
|
- Works with encryption (the signature's `/Contents` is not encrypted, as the spec requires) and with PDF/A-2b.
|
|
860
927
|
|
|
928
|
+
An invisible signature: `pdf.sign({ name: 'Signature1' }, { signer })` (no page, no size) is a zero-`/Rect` field in
|
|
929
|
+
the AcroForm only, so it works after every page is written (a document built through `pdf.core`); with `page` (still
|
|
930
|
+
open) it goes in that page's `/Annots`. A PKCS#12 (.p12/.pfx) file signs through `cmsSigner`:
|
|
931
|
+
`examples/p12-signer.mjs` (node-forge, which you install; not a dependency) gives `p12Signer(bytes, password)`.
|
|
932
|
+
|
|
861
933
|
### Tagged PDF
|
|
862
934
|
|
|
863
935
|
`page.tag(type, fn, options)` options: `alt` (`/Alt`), `actualText` (`/ActualText`: the text the element stands for),
|
|
@@ -961,6 +1033,34 @@ default configuration `/D` (`/Name`, `/Order` listing every layer, `/ON`, `/OFF`
|
|
|
961
1033
|
- `await pdf.end()` — ends open pages, writes fonts, outline, metadata and the cross-reference stream; resolves to
|
|
962
1034
|
`{ pages, bytes, warnings }`.
|
|
963
1035
|
|
|
1036
|
+
### Extension API (core)
|
|
1037
|
+
|
|
1038
|
+
`pdf.core` (typed `PdfCore` in index.d.ts) is the layer every feature module is written on, for a caller that writes
|
|
1039
|
+
its own content streams and objects beside the Pdf methods (the ReportWright engine does). Not covered by semver in
|
|
1040
|
+
0.x; ARCHITECTURE.md ("Extension points") has the details.
|
|
1041
|
+
|
|
1042
|
+
```js
|
|
1043
|
+
const pdf = createPdf(sink);
|
|
1044
|
+
const { core } = pdf;
|
|
1045
|
+
const n = core.reserve(); // an object number, written later
|
|
1046
|
+
await core.write(n, '<< /Type /ExtGState /ca 0.5 >>'); // small objects go into object streams
|
|
1047
|
+
const gs = core.resource('ExtGState', `${n} 0 R`); // a name in every page's shared Resources
|
|
1048
|
+
core.catalog('PageLabels', '<< /Nums [0 << /S /r >>] >>');
|
|
1049
|
+
core.onFinish(async () => {
|
|
1050
|
+
/* runs at pdf.end(), after the last page, before the fonts and the catalog */
|
|
1051
|
+
});
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
- `reserve()`, `write(n, dict, stream?)`, `writeStream(n, dict, raw)`, `writeBig(n, head, parts, tail)`,
|
|
1055
|
+
`object(dict, stream?)` — objects; a stream's bytes are already filtered (`/Length` is added).
|
|
1056
|
+
- `resource(kind, value, name?)`, `resourcesRef` — the shared Resources dictionary.
|
|
1057
|
+
- `imageResource(image)` → `{ name, ref, alpha, orientation }` — an image from `pdf.embedImage` for your own content
|
|
1058
|
+
stream: `q w 0 0 h x y cm <orientation> cm /<name> Do Q` (with `alpha`, give the page a transparency `/Group`).
|
|
1059
|
+
- `catalog(key, value)`, `onFinish(fn)`, `info`, `xmp` — the end of the document; `info` set to a dictionary's text
|
|
1060
|
+
replaces the Info made from the options.
|
|
1061
|
+
- `pageRef(i)`, `pageHeight(i)`, `pageCount`, `warn(msg)`, `serial(fn)`, `tags` — pages, warnings, ordering, tagging.
|
|
1062
|
+
- In an encrypted file, every string an object holds (literal or hex) is encrypted as it is written.
|
|
1063
|
+
|
|
964
1064
|
### Reading and modifying
|
|
965
1065
|
|
|
966
1066
|
```js
|
|
@@ -1128,11 +1228,42 @@ widgets may cover at most 40% of a page in all, and a widget with an opaque back
|
|
|
1128
1228
|
|
|
1129
1229
|
## Known limits
|
|
1130
1230
|
|
|
1131
|
-
|
|
1132
|
-
bidi algorithm, hyphenation, kerning of the standard fonts, WOFF fonts;
|
|
1231
|
+
- **Images**: image masks (stencils); embedded ICC profiles of PNG/JPEG files are ignored.
|
|
1232
|
+
- **Text**: no Unicode bidi algorithm, no hyphenation, no kerning of the standard fonts, no WOFF fonts; text
|
|
1233
|
+
extraction is reading order, not layout-perfect. With `textMapping: 'actualText'`, readers that ignore ActualText
|
|
1234
|
+
or close it early (pdf.js; a tester saw an older MuPDF drop a line's last Bengali cluster) lose the spanned
|
|
1235
|
+
clusters: the writer closes every span after its cluster's last glyph, and MuPDF 1.28.2 (PyMuPDF) reads Bengali,
|
|
1236
|
+
Devanagari and the other test scripts whole.
|
|
1237
|
+
- **Annotations**: no popup annotations.
|
|
1238
|
+
- **Crypto**: RC4 is refused (read only); public-key (certificate) encryption is neither written nor read; signatures
|
|
1239
|
+
are listed with their coverage, but their cryptography is not verified.
|
|
1240
|
+
- **Standards**: no PDF/A-1a or PDF/X; no attachment annotations in PDF/A-3 (use `pdf.attach`); a Factur-X invoice's
|
|
1241
|
+
content is not validated.
|
|
1242
|
+
- **Editing**: no page edits in an incremental update (use `save()`).
|
|
1243
|
+
- **Browsers**: reading encrypted files needs Node (WebCrypto has no synchronous AES).
|
|
1244
|
+
|
|
1245
|
+
See NOTES.md for the plan, and ARCHITECTURE.md for extending the library.
|
|
1246
|
+
|
|
1247
|
+
## Releasing
|
|
1248
|
+
|
|
1249
|
+
Releases are published to npm by GitHub Actions from a GitHub Release (tag `v<version>`). The one-time `NPM_TOKEN`
|
|
1250
|
+
setup and the steps are in [RELEASING.md](./RELEASING.md).
|
|
1133
1251
|
|
|
1134
1252
|
## Changes
|
|
1135
1253
|
|
|
1254
|
+
Unreleased: the built-in TrueType subset renumbers its glyphs and drops the tables a PDF does not read (`name`,
|
|
1255
|
+
`OS/2`, `gasp`; OpenType/CFF also drops `name` and `OS/2`): a 19-character DejaVu Sans line is 6.5 KB, was 20.2 KB.
|
|
1256
|
+
Renders and extracted text are unchanged. A subsetter function, and `core`, still keep glyph IDs.
|
|
1257
|
+
|
|
1258
|
+
0.1.0-beta.11: `pdf.core`, the low-level API under `createPdf`, is documented (`PdfCore` in the types), with
|
|
1259
|
+
`core.imageResource`; encryption now covers every literal and hex string in direct objects (it leaked some and threw
|
|
1260
|
+
on others); `readFont` and `checkSubset` read a font's metrics and check a subset before embedding; form fields take
|
|
1261
|
+
a caller's `appearance`, `da`, `structParent` and `widgetRef`; a document whose pages were written through `core` can
|
|
1262
|
+
be signed (an invisible field), with a PKCS#12 example (`examples/p12-signer.mjs`); `svgPathOps` turns SVG path data
|
|
1263
|
+
into path operators; the build is minified, and `@reportwright/pdf/write` exports the writer without the reader; the
|
|
1264
|
+
`page`, `width` and `height` of `SignatureOptions` are optional.
|
|
1265
|
+
It includes everything from beta.9 and beta.10.
|
|
1266
|
+
|
|
1136
1267
|
0.1.0-beta.10: a WritableStream's writer lock is released when the export ends, fails or is stopped (`timeoutMs`,
|
|
1137
1268
|
`signal`), so the caller can reuse or cancel the stream; the abort reason is still the original error.
|
|
1138
1269
|
|