@reportwright/pdf 0.1.0-beta.2 → 0.1.0-beta.4

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
@@ -249,6 +249,26 @@ const { bytes } = await doc.saveIncremental(); // the origin
249
249
  [`examples/merge-split.mjs`](./examples/merge-split.mjs). `save()` rewrites the file (and strips active content);
250
250
  `saveIncremental()` appends (and refuses files with active content): see [Security model](#security-model).
251
251
 
252
+ ### Bulk: many files in worker threads
253
+
254
+ ```js
255
+ import { bulk } from '@reportwright/pdf';
256
+
257
+ const files = fs.readdirSync('in').map((f) => fs.readFileSync(`in/${f}`));
258
+ const texts = await bulk(files, 'extractText', { workers: 4 }); // [{ ok: true, value } | { ok: false, error }]
259
+ const locked = await bulk(files, 'encrypt', { encrypt: { userPassword: 'u', ownerPassword: 'o' } });
260
+ const pages = await bulk(files, 'split'); // value: one Uint8Array per page
261
+ const joined = await bulk([[a, b], [c, d]], 'merge'); // each input: the files to merge
262
+ ```
263
+
264
+ Tasks are names (`extractText`, `merge`, `split`, `encrypt`; `BULK_TASKS`), never functions, so only bytes and plain
265
+ options cross to a worker. Results come back in input order; a file that fails (malformed, wrong password, over the
266
+ budget) gives `{ ok: false, error: { name, code, message } }` and the batch goes on. Each worker's heap is capped
267
+ (`memoryMb`, default 512): a file that exhausts it kills only that worker, its item fails with `E_WORKER`, and a new
268
+ worker takes the next. `workers` defaults to `os.availableParallelism() - 1`. Node only: in browsers (no
269
+ `node:worker_threads`), and with `workers: 1`, the same tasks run one at a time in the calling thread, without the
270
+ heap cap. [`examples/bulk.mjs`](./examples/bulk.mjs).
271
+
252
272
  ## Examples
253
273
 
254
274
  | file | what it shows | run |
@@ -263,6 +283,7 @@ const { bytes } = await doc.saveIncremental(); // the origin
263
283
  | [`invoice-facturx.mjs`](./examples/invoice-facturx.mjs) | self-contained Factur-X PDF/A-3a invoice | `node examples/invoice-facturx.mjs font.ttf out.pdf` |
264
284
  | [`factur-x.mjs`](./examples/factur-x.mjs) | Factur-X from your own CII XML | `node examples/factur-x.mjs invoice.xml font.ttf out.pdf` |
265
285
  | [`merge-split.mjs`](./examples/merge-split.mjs) | merge, split, extractText, saveIncremental | `node examples/merge-split.mjs outDir` |
286
+ | [`bulk.mjs`](./examples/bulk.mjs) | extractText and encrypt over many files in worker threads | `node examples/bulk.mjs outDir` |
266
287
 
267
288
  Run from `node_modules/@reportwright/pdf` (or the package folder of the repository).
268
289
 
@@ -301,7 +322,7 @@ PDF/A levels and the reader. The most used:
301
322
  | `createPdf(sink, o)` / `toBytes(build, o)` | `title`, `author`, `creationDate`, `compress`, `metadata`, `pdfa`, `tagged`, `encrypt`, `sign`, `form.flatten`, `signal`, `timeoutMs`, `id` ([table](#createpdfsink-options)) |
302
323
  | `pdf.embedFont(bytes, o)` | `subset`, `shaper`, `textMapping` ([Fonts](#fonts), [Shaping](#shaping)) |
303
324
  | `page.text` / `page.textBox` | `x`, `y`, `font`, `size`, `color`, `width`, `align`, `maxLines`, `fallback`, `direction`, `kerning` ([Text options](#pages)) |
304
- | `pdf.embedImage` / `page.image` | `colorSpace`, `maxDecodedBytes` / `width`, `height`, `fit`, `opacity`, `alt` ([Images](#images)) |
325
+ | `pdf.embedImage` / `page.image` | `colorSpace`, `maxDecodedBytes`, `compression` / `width`, `height`, `fit`, `opacity`, `alt` ([Images](#images)) |
305
326
  | `page.fill` / `stroke` | `fill`, `stroke`, `width`, `dash`, `cap`, `join`, `opacity`, `blendMode`, `rule` ([Vector graphics](#vector-graphics)) |
306
327
  | `encrypt` | `userPassword`, `ownerPassword`, `algorithm`, `permissions`, `encryptMetadata` ([Encryption](#encryption-1)) |
307
328
  | `pdf.sign(field, o)` | `signer`, `reason`, `location`, `name`, `certify`, `timestamp`, `subFilter`, `reserve` ([Signatures](#signatures)) |
@@ -552,10 +573,12 @@ const logo = await pdf.embedImage(fs.readFileSync('logo.png')); // alpha
552
573
  for (const y of [400, 500, 600]) page.image(logo, { x: 40, y, height: 24, opacity: 0.6 }); // written once
553
574
  ```
554
575
 
555
- - `await pdf.embedImage(bytes, { colorSpace, maxDecodedBytes })` — a JPEG or PNG, written to the file at once as an image XObject;
576
+ - `await pdf.embedImage(bytes, { colorSpace, maxDecodedBytes, compression })` — a JPEG or PNG, written to the file at once as an image XObject;
556
577
  returns a handle with `width` and `height` (pixels, after the EXIF orientation). `colorSpace`: an ICC colour space
557
578
  (`pdf.iccColorSpace`) with the image's number of components, instead of the device space. `maxDecodedBytes`: the
558
- decoded size allowed for a PNG that needs decoding (default 64 MB, 67,108,864 bytes).
579
+ decoded size allowed for a PNG that needs decoding (default 64 MB, 67,108,864 bytes). `compression`: the zlib level
580
+ for a PNG that is re-compressed (alpha or interlacing): `'default'` is 6, or 3 above 2 megapixels (about half the
581
+ time for 2–3% more bytes on photos); `'fast'` is 1; or a level 1–9. JPEGs and passed-through PNGs are not affected.
559
582
  - **JPEG**: baseline and progressive, gray, RGB and CMYK, embedded unchanged (DCTDecode, no re-encoding). Adobe's
560
583
  inverted CMYK (an APP14 "Adobe" segment) is drawn with `/Decode [1 0 1 0 1 0 1 0]`. The EXIF orientation (1–8)
561
584
  is honoured. 12-bit, lossless, hierarchical and arithmetic-coded JPEGs are refused.
@@ -575,7 +598,7 @@ for (const y of [400, 500, 600]) page.image(logo, { x: 40, y, height: 24, opacit
575
598
  PDFs: `alt` makes the image a `Figure` with that alternate text; without `alt` it is an artifact (decoration).
576
599
  - Images are deduplicated automatically: `embedImage` called again with the same bytes (and the same options) in the
577
600
  same document returns the same handle and writes nothing new (keyed by a SHA-256 of the bytes; Node's `node:crypto`,
578
- else WebCrypto, else a fast hash confirmed byte for byte). A different option (`colorSpace`, `maxDecodedBytes`)
601
+ else WebCrypto, else a fast hash confirmed byte for byte). A different option (`colorSpace`, `maxDecodedBytes`, `compression`)
579
602
  embeds it again.
580
603
  - An image drawn any number of times, on any pages, is one XObject in the file. Images are always XObjects, never
581
604
  inline images: an inline image would be repeated in every content stream and cannot have a soft mask.