@web-ts-toolkit/pdf-reader 0.42.2 → 0.47.1

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.
Files changed (4) hide show
  1. package/README.md +172 -38
  2. package/index.d.mts +163 -12
  3. package/index.mjs +642 -153
  4. package/package.json +1 -1
package/index.d.mts CHANGED
@@ -14,7 +14,12 @@ type PdfTypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | U
14
14
  * PDF.js `getDocument(...)` source accepted by the package root.
15
15
  *
16
16
  * Prefer trusted in-memory bytes when possible. PDF.js may transfer typed-array
17
- * ownership to its worker during load.
17
+ * ownership to its worker during load, detaching the caller's buffer: keep no
18
+ * further use of the buffer after `load()` starts and retry a detached-input
19
+ * failure with fresh bytes. In-memory `data` references stay borrowed (never
20
+ * copied by the package), so only the byte size is re-validated after source
21
+ * policy approval; concurrent byte-content mutation is caller-visible and
22
+ * unsupported.
18
23
  */
19
24
  type PdfSource = string | URL | ArrayBuffer | PdfTypedArray | number[] | PdfDocumentInitParameters;
20
25
  type TransformMatrix = readonly [number, number, number, number, number, number];
@@ -29,10 +34,20 @@ type PageImageOutputMode = 'data-url' | 'blob';
29
34
  * rejected and a later `load()` call will start a fresh attempt.
30
35
  */
31
36
  type PdfReaderState = 'new' | 'loading' | 'loaded' | 'iterating' | 'failed' | 'destroyed';
32
- /** PDF.js text output without requiring consumers to import PDF.js internals. */
37
+ /** PDF.js text output; use `pdfTextToString` to assemble plain text without importing PDF.js internals. */
33
38
  type PdfTextContent = Awaited<ReturnType<PDFPageProxy['getTextContent']>>;
34
39
  interface PdfReaderLimits {
35
- /** Maximum known in-memory source bytes accepted before PDF.js starts loading. Unset by default. */
40
+ /**
41
+ * Maximum known in-memory source bytes accepted before PDF.js starts loading. Unset by default.
42
+ *
43
+ * The limit counts PDF.js byte conversion: binary-string code units (one
44
+ * byte each), number-array entries (one byte each), and buffer/view
45
+ * `byteLength` (sliced views count only their slice). It is checked before
46
+ * source-policy approval and rechecked after approval, so growth during
47
+ * approval still fails before loading. Remote downloads have no
48
+ * synchronously knowable length and stay application-owned via
49
+ * fetch/range transport controls.
50
+ */
36
51
  maxSourceBytes?: number;
37
52
  /** Maximum number of document pages accepted after loading. Defaults to 1,000. */
38
53
  maxDocumentPages?: number;
@@ -55,17 +70,32 @@ interface PdfReaderLogger {
55
70
  warn(message: string, error?: unknown): void;
56
71
  }
57
72
  interface PdfReaderSourceInfo {
58
- /** Immutable shallow snapshot that will be passed to PDF.js if policy allows it. */
73
+ /**
74
+ * Immutable shallow snapshot that will be passed to PDF.js if policy allows it.
75
+ *
76
+ * Stabilized request fields (`url` as a string, `httpHeaders` as an owned
77
+ * frozen record) are read once before policy approval, so later caller
78
+ * mutation or getter re-evaluation cannot change the effective request.
79
+ * Opaque objects (`data` bytes, `worker`, factories, range transports) are
80
+ * deliberately borrowed by reference and pass through unchanged; the
81
+ * snapshot freezes only the outer params object, not recursive contents.
82
+ */
59
83
  rawSource: Readonly<PdfDocumentInitParameters>;
60
84
  /** Broad source shape so callers can distinguish direct bytes from URL-based inputs. */
61
85
  kind: 'bytes' | 'url' | 'document-init-parameters';
62
86
  /** Normalized URL string when one is present on the source. */
63
87
  url?: string;
64
- /** Known byte length for direct byte sources and `DocumentInitParameters.data` when synchronously measurable. */
88
+ /** Known byte length for direct byte sources and `DocumentInitParameters.data` when synchronously measurable: binary-string length, array length, or buffer/view `byteLength`. Remote URL sources report `undefined`; their response-byte limits remain application-owned. */
65
89
  byteLength?: number;
66
90
  /** True when the source carries any custom HTTP headers. */
67
91
  hasHttpHeaders: boolean;
68
- /** Shallow copy of recognized HTTP headers when the source uses a plain object. */
92
+ /**
93
+ * Owned frozen copy of the effective HTTP headers enumerated exactly as
94
+ * PDF.js consumes them (`for...in`, inherited included, `undefined`
95
+ * skipped, values string-coerced). Non-plain containers (e.g. `Headers`)
96
+ * report `hasHttpHeaders: true` with no value entries rather than a
97
+ * false-equivalent record; gate on `hasHttpHeaders` to block them.
98
+ */
69
99
  httpHeaders?: Readonly<Record<string, string>>;
70
100
  /** Whether PDF.js would send credentials for URL-based loading. */
71
101
  withCredentials: boolean;
@@ -81,21 +111,44 @@ interface PdfReaderOptions {
81
111
  sourcePolicy?: PdfReaderSourcePolicy;
82
112
  }
83
113
  interface LoadOptions {
84
- /** Cancels only the current load caller, not other callers sharing the same underlying PDF.js task. */
114
+ /**
115
+ * Cancels only the current load caller, not other callers sharing the same underlying PDF.js task.
116
+ * Structurally validated across realms: boolean `aborted`, callable
117
+ * `addEventListener`/`removeEventListener`. Omitted/`undefined` means no
118
+ * signal; `null` and malformed values throw `INVALID_OPTION` before loading,
119
+ * including when the document is already loaded.
120
+ */
85
121
  signal?: AbortSignal;
86
- /** Caller-local deadline in milliseconds for waiting on `load()`. */
122
+ /** Caller-local deadline in milliseconds for waiting on `load()`. Must be a positive finite number not exceeding 2,147,483,647 (the maximum browser/Node timer delay); larger values are rejected with `INVALID_OPTION`. */
87
123
  deadlineMs?: number;
88
124
  }
89
125
  interface ConvertOptions {
126
+ /**
127
+ * 1-based page selection: one page number or an inclusive `[start, end]`
128
+ * tuple. Reversed tuples are normalized; defaults to all pages.
129
+ */
90
130
  pageRange?: PageRange;
131
+ /** Render scale factor or page-size callback. Defaults to `1.5`. */
91
132
  viewportScale?: ViewportScale;
133
+ /** Page render format. Defaults to `'image/png'`. */
92
134
  imageFormat?: PageImageMimeType;
135
+ /** JPEG quality from `0` through `1`, applied only to `'image/jpeg'`. Defaults to `0.92`; PNG output ignores it. */
93
136
  jpegQuality?: number;
137
+ /** Render the full page under `page.pageImage`. Defaults to `true`. */
94
138
  includePageImage?: boolean;
139
+ /** Page-image payload shape. Defaults to `'data-url'`. */
95
140
  pageImageOutput?: PageImageOutputMode;
141
+ /** Include PDF.js `TextContent` under `page.text`. Defaults to `true`. */
96
142
  includeText?: boolean;
143
+ /** Best-effort embedded raster extraction under `page.images`. Defaults to `false`. */
97
144
  includeEmbeddedImages?: boolean;
98
- /** Stops between expensive steps and cancels an active page render. */
145
+ /**
146
+ * Stops between expensive steps, including after canvas/viewport callbacks, and settles page-stage waits promptly. Active renders are cancelled. Cancellation/failure of an already-created render permanently closes the reader and starts public teardown; the initiating call keeps ABORTED/native error, while later operations reject DESTROYED. Retry requires a fresh reader and fresh bytes. Pre-render/non-render cancellation and encoding cancellation after successful rendering permit same-reader reuse; other started PDF.js work stays observed, and cleanup waits for cached-page ownership to become idle (or destruction). A suspended pages() consumer observes cancellation on resume.
147
+ * Structurally validated across realms: boolean `aborted`, callable
148
+ * `addEventListener`/`removeEventListener`. Omitted/`undefined` means no
149
+ * signal; `null` and malformed values reject with `INVALID_OPTION` before
150
+ * page acquisition and release the operation lock for valid reuse.
151
+ */
99
152
  signal?: AbortSignal;
100
153
  }
101
154
  interface DataUrlPageImage {
@@ -114,14 +167,30 @@ type PageImageResult = DataUrlPageImage | BlobPageImage;
114
167
  interface ExtractedImage {
115
168
  /** Best-effort PNG data URL copied from a PDF.js image object. */
116
169
  dataUrl: string;
170
+ /** Left edge in PDF user-space units, before viewport scaling. */
117
171
  x: number;
172
+ /**
173
+ * Upper bound in PDF user-space units, before viewport scaling.
174
+ *
175
+ * `y` is the top of the transformed unit square (`max` of the mapped
176
+ * corners), not the baseline origin.
177
+ */
118
178
  y: number;
179
+ /** Width in PDF user-space units, before viewport scaling. */
119
180
  width: number;
181
+ /** Height in PDF user-space units, before viewport scaling. */
120
182
  height: number;
183
+ /**
184
+ * Decoded PDF.js image source size in bytes (`dataLen` when reported,
185
+ * else raw `data.byteLength`). Not the encoded PNG data-URL length.
186
+ */
121
187
  size: number;
122
188
  mimeType: 'image/png';
189
+ /** Unscaled page width in PDF user-space units (`viewport.width / viewport.scale`). */
123
190
  pageWidth: number;
191
+ /** Unscaled page height in PDF user-space units (`viewport.height / viewport.scale`). */
124
192
  pageHeight: number;
193
+ /** Full six-value graphics transform active when the image was painted. */
125
194
  transform: TransformMatrix;
126
195
  }
127
196
  interface PageResult {
@@ -168,17 +237,95 @@ declare class PDFReader {
168
237
  * use supported PDF.js read methods, but must not call `destroy()` on the
169
238
  * proxy while the reader owns lifecycle teardown. Call `reader.destroy()` to
170
239
  * release document and worker resources.
240
+ * After explicit destruction or automatic closure of an interrupted render,
241
+ * throws `DESTROYED` synchronously before validating options or starting work.
242
+ *
243
+ * Signals are structurally validated across realms: boolean `aborted` and
244
+ * callable `addEventListener`/`removeEventListener`. Omitted/`undefined`
245
+ * means no signal; `null` and malformed values throw `INVALID_OPTION`
246
+ * synchronously, even when already loaded, before policy/PDF.js work.
171
247
  */
172
248
  load(signal?: AbortSignal): Promise<PDFDocumentProxy>;
173
249
  load(options?: LoadOptions): Promise<PDFDocumentProxy>;
174
- /** Streams page results so callers do not need to retain the entire document conversion in memory. */
250
+ /**
251
+ * Streams page results so callers do not need to retain the entire document conversion in memory.
252
+ *
253
+ * Page-stage waits (`getPage`, text, operator list, render, encoding) settle
254
+ * promptly with `ABORTED`/`DESTROYED` via the shared cancellation/wait
255
+ * contract. Active renders are cancelled; racing other PDF.js work does not
256
+ * stop it. Already-started promises remain observed even if a synchronous
257
+ * callback aborts/destroys before wait registration. Late `getPage()` values
258
+ * are never processed; late text/operator values are dropped. Page cleanup
259
+ * is deferred until package-owned acquisitions, processing, and upstream
260
+ * stage promises for that cached page are idle, including safe retries.
261
+ * Canvas factories and viewport callbacks are followed by lifecycle checks
262
+ * before subsequent allocation/rendering. Caller settlement releases the
263
+ * operation lock, but does not imply upstream work has finished.
264
+ * Cancellation/failure of an already-created render permanently closes the
265
+ * reader and starts public PDF.js teardown. The initiating call retains
266
+ * `ABORTED` or the native error; later operations reject `DESTROYED`. Retry
267
+ * with a fresh reader and fresh bytes (PDF.js may have transferred the input).
268
+ * Pre-render and non-render failures, and encoding cancellation/failure after
269
+ * successful rendering, do not automatically close the reader.
270
+ * A suspended generator only observes abort/destroy on the next
271
+ * `next()`/`return()`; destruction cannot force suspended consumer code to run.
272
+ * Malformed signals (including `null`) reject with `INVALID_OPTION` before
273
+ * page acquisition. Omitted/`undefined` signals are accepted.
274
+ */
175
275
  pages(options?: ConvertOptions): AsyncGenerator<PageResult>;
176
276
  /** Collects `pages()` into an array. Prefer `pages()` for large documents. */
177
277
  convert(options?: ConvertOptions): Promise<PageResult[]>;
178
- /** Terminates the loading task and document resources. The reader cannot be reused afterwards. */
278
+ /**
279
+ * Terminates the loading task and document resources. The reader cannot be reused afterwards.
280
+ *
281
+ * A caller-created PDF.js `PDFWorker` passed on the source stays
282
+ * caller-owned: neither `destroy()` nor PDF.js task teardown destroys it.
283
+ * Destroy it explicitly in your own `finally` block, even if this method
284
+ * rejects.
285
+ *
286
+ * In-flight page-stage waiters settle promptly with `DESTROYED`, but their
287
+ * uncancellable getPage/text/operator work stays observed in the background.
288
+ * Active renders are cancelled. This method waits for PDF.js loading/document
289
+ * destruction, not for orphaned `getPage`/text/operator/image-object
290
+ * continuations, which never start later processing. It clears retained
291
+ * page ownership; late acquisitions receive best-effort cleanup. Await
292
+ * active operations to observe their temporary canvas release. It cannot
293
+ * force a suspended `pages()` consumer to resume; that consumer observes
294
+ * destruction on next resume.
295
+ * Render cancellation/failure also starts this teardown automatically without
296
+ * awaiting it or replacing the initiating error. This method always returns
297
+ * the same teardown promise, including its rejection; call it to observe that
298
+ * outcome. Automatic teardown rejection is observed even if you never do.
299
+ */
179
300
  destroy(): Promise<void>;
180
301
  }
181
302
 
303
+ /**
304
+ * Assemble already-extracted PDF.js text in supplied item order, without mutation.
305
+ *
306
+ * Preserves each string verbatim, including whitespace and Unicode. Appends one
307
+ * `\n` after each text item with `hasEOL: true`, even an empty or final item;
308
+ * existing newlines are not deduplicated and trailing whitespace is not trimmed.
309
+ * Marked-content entries are skipped. XFA-style string items without `hasEOL`
310
+ * contribute their string only. Empty or marker-only content returns `''`.
311
+ *
312
+ * No spaces are guessed between fragments. This is not OCR, layout/reading-order
313
+ * reconstruction, or streaming extraction; PDF.js has already materialized the
314
+ * content (and may have normalized its text). The helper allocates a new string
315
+ * and applies no resource limits of its own. Display untrusted text via
316
+ * `textContent`, not HTML.
317
+ *
318
+ * @param content - Raw page text, such as `PageResult.text` when present.
319
+ * @returns Concatenated text with explicit PDF.js end-of-line markers honored.
320
+ * @example
321
+ * ```ts
322
+ * import { pdfTextToString } from '@web-ts-toolkit/pdf-reader';
323
+ *
324
+ * if (page.text) output.textContent = pdfTextToString(page.text);
325
+ * ```
326
+ */
327
+ declare function pdfTextToString(content: PdfTextContent): string;
328
+
182
329
  interface PdfUrlSourceOptions {
183
330
  /** Basic authentication headers forwarded to PDF.js URL loading. */
184
331
  httpHeaders?: Readonly<Record<string, string>>;
@@ -224,7 +371,11 @@ declare function pdfUrlSource(url: string | URL, options?: PdfUrlSourceOptions):
224
371
  *
225
372
  * When passing an existing DOM `Worker`, the caller retains ownership of
226
373
  * terminating it.
374
+ *
375
+ * A caller-created PDF.js `PDFWorker` passed on a `PdfSource` is likewise
376
+ * never destroyed by the reader or by PDF.js task teardown: destroy it
377
+ * explicitly in a `finally` block, even when `reader.destroy()` rejects.
227
378
  */
228
379
  declare function configurePdfWorker(worker: string | Worker): void;
229
380
 
230
- export { type BlobPageImage, type ConvertOptions, type DataUrlPageImage, type ExtractedImage, type LoadOptions, type LoadedPdfDocument, type LoadedPdfPage, PDFReader, type PageImageMimeType, type PageImageOutputMode, type PageImageResult, type PageRange, type PageResult, type PdfDocumentInitParameters, PdfReaderError, type PdfReaderErrorCode, type PdfReaderLimits, type PdfReaderLogger, type PdfReaderOptions, type PdfReaderSourceInfo, type PdfReaderSourcePolicy, type PdfReaderState, type PdfSource, type PdfTextContent, type PdfTypedArray, type PdfUrlSource, type PdfUrlSourceOptions, type TransformMatrix, type ViewportScale, configurePdfWorker, pdfUrlSource };
381
+ export { type BlobPageImage, type ConvertOptions, type DataUrlPageImage, type ExtractedImage, type LoadOptions, type LoadedPdfDocument, type LoadedPdfPage, PDFReader, type PageImageMimeType, type PageImageOutputMode, type PageImageResult, type PageRange, type PageResult, type PdfDocumentInitParameters, PdfReaderError, type PdfReaderErrorCode, type PdfReaderLimits, type PdfReaderLogger, type PdfReaderOptions, type PdfReaderSourceInfo, type PdfReaderSourcePolicy, type PdfReaderState, type PdfSource, type PdfTextContent, type PdfTypedArray, type PdfUrlSource, type PdfUrlSourceOptions, type TransformMatrix, type ViewportScale, configurePdfWorker, pdfTextToString, pdfUrlSource };