@2018kguo/recto 0.1.0 → 0.5.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.
Files changed (44) hide show
  1. package/dist/assets.d.ts +11 -6
  2. package/dist/assets.js +123 -42
  3. package/dist/document.d.ts +144 -2
  4. package/dist/document.js +163 -14
  5. package/dist/engine.d.ts +24 -9
  6. package/dist/engine.js +236 -49
  7. package/dist/errors.d.ts +4 -2
  8. package/dist/errors.js +4 -1
  9. package/dist/index.d.ts +12 -1
  10. package/dist/index.js +13 -0
  11. package/dist/pool-worker.d.ts +15 -0
  12. package/dist/pool-worker.js +65 -0
  13. package/dist/pool.d.ts +42 -0
  14. package/dist/pool.js +295 -0
  15. package/dist/ranged.d.ts +44 -9
  16. package/dist/ranged.js +571 -86
  17. package/package.json +2 -1
  18. package/wasm/FoxitDingbats.pfb +0 -0
  19. package/wasm/FoxitFixed.pfb +0 -0
  20. package/wasm/FoxitFixedBold.pfb +0 -0
  21. package/wasm/FoxitFixedBoldItalic.pfb +0 -0
  22. package/wasm/FoxitFixedItalic.pfb +0 -0
  23. package/wasm/FoxitSans.pfb +0 -0
  24. package/wasm/FoxitSansBold.pfb +0 -0
  25. package/wasm/FoxitSansBoldItalic.pfb +0 -0
  26. package/wasm/FoxitSansItalic.pfb +0 -0
  27. package/wasm/FoxitSerif.pfb +0 -0
  28. package/wasm/FoxitSerifBold.pfb +0 -0
  29. package/wasm/FoxitSerifBoldItalic.pfb +0 -0
  30. package/wasm/FoxitSerifItalic.pfb +0 -0
  31. package/wasm/FoxitSymbol.pfb +0 -0
  32. package/wasm/THIRD_PARTY_NOTICES +2 -158
  33. package/wasm/cmaps.deflate +0 -0
  34. package/wasm/pins.d.mts +1 -1
  35. package/wasm/pins.mjs +19 -3
  36. package/wasm/recto-more.wasm +0 -0
  37. package/wasm/recto-rare.wasm +0 -0
  38. package/wasm/recto-render.wasm +0 -0
  39. package/wasm/recto.gen.d.mts +2 -1
  40. package/wasm/recto.gen.mjs +213 -19
  41. package/wasm/recto.types.d.mts +83 -4
  42. package/wasm/recto.wasm +0 -0
  43. package/wasm/cmaps.brotli +0 -0
  44. package/wasm/fonts.bin +0 -0
package/dist/assets.d.ts CHANGED
@@ -1,14 +1,19 @@
1
1
  import { PINS } from "../wasm/pins.mjs";
2
2
  export type AssetName = keyof typeof PINS;
3
+ export type WasmName = "recto.wasm" | "recto-render.wasm" | "recto-more.wasm" | "recto-rare.wasm";
4
+ /** The standard fonts, in the order the wasm asks for them (`font_index` in crates/recto/src/assets.rs). */
5
+ export declare const STANDARD_FONTS: readonly ["FoxitFixed.pfb", "FoxitFixedBold.pfb", "FoxitFixedBoldItalic.pfb", "FoxitFixedItalic.pfb", "FoxitSans.pfb", "FoxitSansBold.pfb", "FoxitSansBoldItalic.pfb", "FoxitSansItalic.pfb", "FoxitSerif.pfb", "FoxitSerifBold.pfb", "FoxitSerifBoldItalic.pfb", "FoxitSerifItalic.pfb", "FoxitSymbol.pfb", "FoxitDingbats.pfb"];
3
6
  /** Whether an asset can be read at the moment the engine asks for it. */
4
7
  export declare function canReadSync(): boolean;
5
8
  /** `assetBase` as a directory URL, or null for the package's own files. */
6
9
  export declare function assetBaseUrl(base: string | URL | undefined): URL | null;
7
- /** The wasm's bytes, for compiling. Asynchronous where it can be. */
8
- export declare function loadWasm(base: URL | null): Promise<Uint8Array<ArrayBuffer>>;
9
- /** Fetches assets ahead of time, for a thread that can't read them when they're needed. */
10
+ /** A hash to use where there is no native one to call synchronously. A live instance's is quicker than this file's. */
11
+ export type SyncHash = (bytes: Uint8Array) => string | null;
12
+ /** The wasm, verified and then compiled. */
13
+ export declare function compileWasm(name: WasmName, base: URL | null): Promise<WebAssembly.Module>;
14
+ /** Fetches and verifies assets ahead of time; a thread that can't read them when needed has to. */
10
15
  export declare function preloadAssets(base: URL | null, names: AssetName[]): Promise<void>;
11
16
  /** An asset, read synchronously. Throws a PdfError rather than returning nothing. */
12
- export declare function loadAssetSync(name: AssetName, base: URL | null): Uint8Array;
13
- /** Entry `index` of fonts.bin: u32 count, u32 offsets[count + 1], bytes (little-endian). */
14
- export declare function fontFromBundle(bundle: Uint8Array, index: number): Uint8Array;
17
+ export declare function loadAssetSync(name: AssetName, base: URL | null, hash?: SyncHash): Uint8Array;
18
+ /** A file, read now and hashed before it's returned, and not kept: a part is compiled once. */
19
+ export declare function readCheckedSync(name: AssetName, base: URL | null, hash?: SyncHash): Uint8Array;
package/dist/assets.js CHANGED
@@ -8,21 +8,72 @@
8
8
  // are written out as `new URL("literal", import.meta.url)` because that is the form
9
9
  // bundlers recognise and rewrite, emitting the file.
10
10
  //
11
- // Each file is pinned by SHA-256: the engine reads its tables as trusted, and a dev server's
12
- // HTML fallback page must not read as a font. pins.mjs is written by the build.
11
+ // Each file is pinned by SHA-256 and hashed here, every byte, before it's used: the engine reads
12
+ // its tables as trusted, and a dev server's HTML fallback page must not read as a font. Nothing is
13
+ // left to `fetch`'s `integrity`, which a wrapper around `fetch` can drop, and which buffers the
14
+ // body anyway. pins.mjs is written by the build.
13
15
  import { PINS } from "../wasm/pins.mjs";
14
16
  import { PdfError } from "./errors.js";
15
17
  import { sha256Hex } from "./sha256.js";
18
+ /** The standard fonts, in the order the wasm asks for them (`font_index` in crates/recto/src/assets.rs). */
19
+ export const STANDARD_FONTS = [
20
+ "FoxitFixed.pfb",
21
+ "FoxitFixedBold.pfb",
22
+ "FoxitFixedBoldItalic.pfb",
23
+ "FoxitFixedItalic.pfb",
24
+ "FoxitSans.pfb",
25
+ "FoxitSansBold.pfb",
26
+ "FoxitSansBoldItalic.pfb",
27
+ "FoxitSansItalic.pfb",
28
+ "FoxitSerif.pfb",
29
+ "FoxitSerifBold.pfb",
30
+ "FoxitSerifBoldItalic.pfb",
31
+ "FoxitSerifItalic.pfb",
32
+ "FoxitSymbol.pfb",
33
+ "FoxitDingbats.pfb",
34
+ ];
16
35
  function packagedUrl(name) {
17
36
  switch (name) {
18
37
  case "recto.wasm":
19
38
  return new URL("../wasm/recto.wasm", import.meta.url);
20
- case "fonts.bin":
21
- return new URL("../wasm/fonts.bin", import.meta.url);
39
+ case "recto-render.wasm":
40
+ return new URL("../wasm/recto-render.wasm", import.meta.url);
41
+ case "recto-more.wasm":
42
+ return new URL("../wasm/recto-more.wasm", import.meta.url);
43
+ case "recto-rare.wasm":
44
+ return new URL("../wasm/recto-rare.wasm", import.meta.url);
45
+ case "FoxitFixed.pfb":
46
+ return new URL("../wasm/FoxitFixed.pfb", import.meta.url);
47
+ case "FoxitFixedBold.pfb":
48
+ return new URL("../wasm/FoxitFixedBold.pfb", import.meta.url);
49
+ case "FoxitFixedBoldItalic.pfb":
50
+ return new URL("../wasm/FoxitFixedBoldItalic.pfb", import.meta.url);
51
+ case "FoxitFixedItalic.pfb":
52
+ return new URL("../wasm/FoxitFixedItalic.pfb", import.meta.url);
53
+ case "FoxitSans.pfb":
54
+ return new URL("../wasm/FoxitSans.pfb", import.meta.url);
55
+ case "FoxitSansBold.pfb":
56
+ return new URL("../wasm/FoxitSansBold.pfb", import.meta.url);
57
+ case "FoxitSansBoldItalic.pfb":
58
+ return new URL("../wasm/FoxitSansBoldItalic.pfb", import.meta.url);
59
+ case "FoxitSansItalic.pfb":
60
+ return new URL("../wasm/FoxitSansItalic.pfb", import.meta.url);
61
+ case "FoxitSerif.pfb":
62
+ return new URL("../wasm/FoxitSerif.pfb", import.meta.url);
63
+ case "FoxitSerifBold.pfb":
64
+ return new URL("../wasm/FoxitSerifBold.pfb", import.meta.url);
65
+ case "FoxitSerifBoldItalic.pfb":
66
+ return new URL("../wasm/FoxitSerifBoldItalic.pfb", import.meta.url);
67
+ case "FoxitSerifItalic.pfb":
68
+ return new URL("../wasm/FoxitSerifItalic.pfb", import.meta.url);
69
+ case "FoxitSymbol.pfb":
70
+ return new URL("../wasm/FoxitSymbol.pfb", import.meta.url);
71
+ case "FoxitDingbats.pfb":
72
+ return new URL("../wasm/FoxitDingbats.pfb", import.meta.url);
22
73
  case "cjk-fallback.ttf":
23
74
  return new URL("../wasm/cjk-fallback.ttf", import.meta.url);
24
- case "cmaps.brotli":
25
- return new URL("../wasm/cmaps.brotli", import.meta.url);
75
+ case "cmaps.deflate":
76
+ return new URL("../wasm/cmaps.deflate", import.meta.url);
26
77
  }
27
78
  }
28
79
  /** A stalled server must not block the thread for ever; the largest asset is 5 MB. */
@@ -71,11 +122,16 @@ function loadFailure(name, url, reason, cause) {
71
122
  return new PdfError("missing-asset", `recto could not load ${name}${url ? ` from ${url.href}` : ""}: ${reason}. It ships in the package's wasm/ ` +
72
123
  "directory. If a bundler or dev server moved the package without its files, copy them " +
73
124
  "somewhere it serves and pass that directory as `assetBase` (with Vite's dev server, add " +
74
- 'optimizeDeps: { exclude: ["@2018kguo/recto"] } instead).', cause);
125
+ 'optimizeDeps: { exclude: ["@2018kguo/recto"] } instead; a server bundle should keep the package external).', cause);
75
126
  }
76
- function checked(name, url, bytes) {
77
- if (sha256Hex(bytes) !== PINS[name]) {
78
- throw loadFailure(name, url, `the ${bytes.byteLength} bytes there are not this build's file (a server's fallback page, or a different version of the package)`);
127
+ function notThisBuild(name, url, bytes) {
128
+ return loadFailure(name, url, `the ${bytes.byteLength} bytes there are not this build's file (a server's fallback page, or a different version of the package)`);
129
+ }
130
+ function checked(name, url, bytes, hash) {
131
+ const crypto = globalThis.process?.getBuiltinModule?.("node:crypto");
132
+ const hex = crypto ? crypto.createHash("sha256").update(bytes).digest("hex") : (hash?.(bytes) ?? sha256Hex(bytes));
133
+ if (hex !== PINS[name]) {
134
+ throw notThisBuild(name, url, bytes);
79
135
  }
80
136
  return bytes;
81
137
  }
@@ -93,32 +149,52 @@ function readSync(url) {
93
149
  }
94
150
  return new Uint8Array(xhr.response);
95
151
  }
96
- /** The wasm's bytes, for compiling. Asynchronous where it can be. */
97
- export async function loadWasm(base) {
152
+ /** `crypto.subtle` is only there in a secure context; a plain-http page hashes here. */
153
+ async function hashOf(bytes) {
154
+ const subtle = globalThis.crypto?.subtle;
155
+ if (!subtle) {
156
+ return sha256Hex(bytes);
157
+ }
158
+ const digest = new Uint8Array(await subtle.digest("SHA-256", bytes));
159
+ return Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
160
+ }
161
+ /** The only way bytes get here from a network without blocking: whole, and hashed before they're used. */
162
+ async function fetchBytes(name, url) {
163
+ const response = await fetch(url);
164
+ if (!response.ok) {
165
+ throw new Error(`HTTP ${response.status}`);
166
+ }
167
+ const bytes = new Uint8Array(await response.arrayBuffer());
168
+ if ((await hashOf(bytes)) !== PINS[name]) {
169
+ throw notThisBuild(name, url, bytes);
170
+ }
171
+ return bytes;
172
+ }
173
+ /** The wasm, verified and then compiled. */
174
+ export async function compileWasm(name, base) {
98
175
  let url = null;
99
176
  let bytes;
100
177
  try {
101
178
  // Inside the try: under a blob: worker `import.meta.url` is no base for a URL.
102
- url = assetUrl("recto.wasm", base);
103
- if (nodeFs()) {
104
- bytes = readFromDisk(url);
105
- }
106
- else {
107
- const response = await fetch(url);
108
- if (!response.ok) {
109
- throw new Error(`HTTP ${response.status}`);
110
- }
111
- bytes = new Uint8Array(await response.arrayBuffer());
112
- }
179
+ url = assetUrl(name, base);
180
+ bytes = (nodeFs() ? checked(name, url, readFromDisk(url)) : await fetchBytes(name, url));
113
181
  }
114
182
  catch (error) {
115
- throw loadFailure("recto.wasm", url, error instanceof Error ? error.message : String(error), error);
183
+ if (error instanceof PdfError) {
184
+ throw error;
185
+ }
186
+ throw loadFailure(name, url, error instanceof Error ? error.message : String(error), error);
116
187
  }
117
- return checked("recto.wasm", url, bytes);
188
+ return await compiling(name, WebAssembly.compile(bytes));
189
+ }
190
+ function compiling(name, module) {
191
+ return module.catch((error) => {
192
+ throw new PdfError("missing-asset", `recto could not compile ${name}: ${String(error)}. A Content-Security-Policy must allow 'wasm-unsafe-eval'.`, error);
193
+ });
118
194
  }
119
195
  /** Verified assets by URL: hash and read each once per thread. */
120
196
  const loaded = new Map();
121
- /** Fetches assets ahead of time, for a thread that can't read them when they're needed. */
197
+ /** Fetches and verifies assets ahead of time; a thread that can't read them when needed has to. */
122
198
  export async function preloadAssets(base, names) {
123
199
  await Promise.all(names.map(async (name) => {
124
200
  let url = null;
@@ -128,28 +204,40 @@ export async function preloadAssets(base, names) {
128
204
  if (loaded.has(url.href)) {
129
205
  return;
130
206
  }
131
- const response = await fetch(url);
132
- if (!response.ok) {
133
- throw new Error(`HTTP ${response.status}`);
134
- }
135
- bytes = new Uint8Array(await response.arrayBuffer());
207
+ bytes = nodeFs() ? checked(name, url, readFromDisk(url)) : await fetchBytes(name, url);
136
208
  }
137
209
  catch (error) {
210
+ if (error instanceof PdfError) {
211
+ throw error;
212
+ }
138
213
  throw loadFailure(name, url, error instanceof Error ? error.message : String(error), error);
139
214
  }
140
- loaded.set(url.href, checked(name, url, bytes));
215
+ loaded.set(url.href, bytes);
141
216
  }));
142
217
  }
143
218
  /** An asset, read synchronously. Throws a PdfError rather than returning nothing. */
144
- export function loadAssetSync(name, base) {
219
+ export function loadAssetSync(name, base, hash) {
145
220
  let url = null;
146
- let bytes;
147
221
  try {
148
222
  url = assetUrl(name, base);
149
223
  const cached = loaded.get(url.href);
150
224
  if (cached) {
151
225
  return cached;
152
226
  }
227
+ }
228
+ catch (error) {
229
+ throw loadFailure(name, url, error instanceof Error ? error.message : String(error), error);
230
+ }
231
+ const bytes = readCheckedSync(name, base, hash);
232
+ loaded.set(url.href, bytes);
233
+ return bytes;
234
+ }
235
+ /** A file, read now and hashed before it's returned, and not kept: a part is compiled once. */
236
+ export function readCheckedSync(name, base, hash) {
237
+ let url = null;
238
+ let bytes;
239
+ try {
240
+ url = assetUrl(name, base);
153
241
  if (!canReadSync()) {
154
242
  throw new Error("this thread can only use assets fetched up front, and it was left out of preloadAssets");
155
243
  }
@@ -158,12 +246,5 @@ export function loadAssetSync(name, base) {
158
246
  catch (error) {
159
247
  throw loadFailure(name, url, error instanceof Error ? error.message : String(error), error);
160
248
  }
161
- loaded.set(url.href, checked(name, url, bytes));
162
- return bytes;
163
- }
164
- /** Entry `index` of fonts.bin: u32 count, u32 offsets[count + 1], bytes (little-endian). */
165
- export function fontFromBundle(bundle, index) {
166
- const view = new DataView(bundle.buffer, bundle.byteOffset, bundle.byteLength);
167
- const base = 4 + (view.getUint32(0, true) + 1) * 4;
168
- return bundle.subarray(base + view.getUint32(4 + index * 4, true), base + view.getUint32(8 + index * 4, true));
249
+ return checked(name, url, bytes, hash);
169
250
  }
@@ -170,6 +170,29 @@ export type PdfRenderOptions = {
170
170
  width: number;
171
171
  /** Default 4096. May be Infinity, to fit the width alone. A render is capped at 16,384 px a side and 64 Mpx. */
172
172
  maxHeight?: number;
173
+ /**
174
+ * Draws only this part of the page: x, y, width, height in points in the viewer frame, as text and
175
+ * annotation boxes are (top-left origin, `/Rotate` and the crop box applied). The bitmap holds the
176
+ * pixels of the whole page's bitmap at the scale `width` and `maxHeight` give that this covers,
177
+ * grown out to whole pixels and cut to the page, and says where they are. With a region the limits
178
+ * are on the region's bitmap, not the page's, and `width` may be up to 262,144.
179
+ */
180
+ region?: PdfBox;
181
+ };
182
+ /** A render of a region: where its pixels are in the whole page's bitmap at that scale. */
183
+ export type PdfRegionBitmap = PdfBitmap & {
184
+ /** The bitmap's top-left pixel, in the whole page's bitmap. */
185
+ x: number;
186
+ y: number;
187
+ /** The whole page's bitmap's size at this scale: what `render` without a region and its limits would give. */
188
+ pageWidth: number;
189
+ pageHeight: number;
190
+ };
191
+ export type PdfRenderAsyncOptions = PdfRenderOptions & {
192
+ /** Stops the render at the next pause: `aborted`, with the document still usable. */
193
+ signal?: AbortSignal;
194
+ /** About how long it runs before giving the thread back. Default 8. */
195
+ sliceMs?: number;
173
196
  };
174
197
  export type PdfBitmap = {
175
198
  width: number;
@@ -182,6 +205,67 @@ export type PdfBitmap = {
182
205
  */
183
206
  truncated: boolean;
184
207
  };
208
+ /** Where a link in this file goes: a page, and the place on it in that page's viewer frame. */
209
+ export type PdfDestination = {
210
+ /** The page's index, or -1 if it goes nowhere in this file. */
211
+ page: number;
212
+ /**
213
+ * How to show the page, as the file names it: "XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH",
214
+ * "FitBV"; null if it names none.
215
+ */
216
+ view: string | null;
217
+ /**
218
+ * The point to bring to the window's top-left, in points in the target page's viewer frame
219
+ * (top-left origin, its `/Rotate` and crop box applied). null for a coordinate the destination leaves
220
+ * as it was; under a quarter turn, the page's `left` is the viewer's `y`.
221
+ */
222
+ x: number | null;
223
+ y: number | null;
224
+ /** "XYZ"'s zoom, 1 for 100%; null to leave it as it was. */
225
+ zoom: number | null;
226
+ /** "FitR"'s rectangle, in the target page's viewer frame; null for any other view. */
227
+ box: PdfBox | null;
228
+ };
229
+ /** What a link does. Another file or a program is reported, never followed. */
230
+ export type PdfLinkTarget = {
231
+ kind: "uri";
232
+ uri: string;
233
+ }
234
+ /** A `GoTo` action or the link's `/Dest`, named destinations resolved. */
235
+ | {
236
+ kind: "page";
237
+ destination: PdfDestination;
238
+ }
239
+ /** `GoToR`: a destination in another file, by name or by page number there. */
240
+ | {
241
+ kind: "remote";
242
+ file: string;
243
+ name: string | null;
244
+ page: number | null;
245
+ }
246
+ /** `Launch`: a file or program. */
247
+ | {
248
+ kind: "launch";
249
+ file: string;
250
+ }
251
+ /** Any other action by its `/S` ("JavaScript", "SubmitForm", ...); a `Named` action's `/N` ("NextPage", ...) is `name`. */
252
+ | {
253
+ kind: "action";
254
+ action: string;
255
+ name: string | null;
256
+ }
257
+ /** No action and no destination. */
258
+ | {
259
+ kind: "none";
260
+ };
261
+ /** A `/Link` annotation and where it goes. */
262
+ export type PdfLink = {
263
+ /** Its index in what `annotations` returns for the page. */
264
+ annotation: number;
265
+ /** Its `/Rect` in the viewer frame, as its annotation's `box`. */
266
+ box: PdfBox;
267
+ target: PdfLinkTarget;
268
+ };
185
269
  export declare const ANNOT_SUBTYPE: {
186
270
  readonly text: 1;
187
271
  readonly link: 2;
@@ -204,6 +288,18 @@ export interface PdfDocument {
204
288
  readonly pageCount: number;
205
289
  /** The document's bookmarks; empty if it has none. */
206
290
  outline(): PdfOutlineItem[];
291
+ /** What each page is called, one string per page ("iv", "A-5"), or null if the file doesn't say. */
292
+ pageLabels(): string[] | null;
293
+ /**
294
+ * Which of the document's trees were cut short: `pages` means `pageCount` may be short, by a limit or
295
+ * because a damaged tree left no pages (an honestly empty document says false). `pageLabels` and `outline`
296
+ * mean those passed a limit, and may be wrong or incomplete. A page's own limits are `truncated` on its results.
297
+ */
298
+ structureTruncated(): {
299
+ pages: boolean;
300
+ pageLabels: boolean;
301
+ outline: boolean;
302
+ };
207
303
  pageInfo(index: number): PdfPageInfo;
208
304
  text(index: number): PdfPageText;
209
305
  /** `text`, `objects` and `annotations` from one interpretation of the page. */
@@ -219,17 +315,46 @@ export interface PdfDocument {
219
315
  };
220
316
  objects(index: number): PdfPageObjects;
221
317
  annotations(index: number): PdfAnnotation[];
318
+ /**
319
+ * The page's links, in the order `annotations` lists them, and where each goes. Loads drawing's
320
+ * part of the wasm, as `objects` does.
321
+ */
322
+ links(index: number): PdfLink[];
323
+ render(index: number, options: PdfRenderOptions & {
324
+ region: PdfBox;
325
+ }): PdfRegionBitmap;
222
326
  render(index: number, options: PdfRenderOptions): PdfBitmap;
327
+ /**
328
+ * The same pixels as `render`, giving the thread back between slices, and stoppable with a
329
+ * signal. The last step, rasterizing, is one piece: about a third of a heavy page's time.
330
+ */
331
+ renderAsync(index: number, options: PdfRenderAsyncOptions & {
332
+ region: PdfBox;
333
+ }): Promise<PdfRegionBitmap>;
334
+ renderAsync(index: number, options: PdfRenderAsyncOptions): Promise<PdfBitmap>;
223
335
  close(): void;
224
336
  [Symbol.dispose](): void;
225
337
  }
226
338
  export type Instance = {
227
339
  glue: ReturnType<typeof createGlue>;
340
+ /** Loads a part of the wasm, hashed first, or throws `missing-asset` having changed nothing. */
341
+ loadPart(name: "recto-render.wasm" | "recto-more.wasm" | "recto-rare.wasm"): void;
228
342
  memory: WebAssembly.Memory;
343
+ /** Its memory when it was made: past this, something used it, and dropping it gives memory back. */
344
+ startBytes: number;
229
345
  generation: number;
230
346
  openDocuments: number;
231
- /** Set once the instance trapped or an asset failed; nothing may call it again. */
347
+ /** The engine's count of documents open, on any of its instances. */
348
+ documents: {
349
+ open: number;
350
+ };
351
+ /** Set once the instance trapped; nothing may call it again. */
232
352
  dead: boolean;
353
+ /**
354
+ * Set once a part or asset failed to load inside a call. Nothing more runs on it, but a document's
355
+ * file is still whole there, so it reopens on another instance at its next call.
356
+ */
357
+ abandoned: boolean;
233
358
  /** An asset's load failure, rethrown by the call that needed the asset. */
234
359
  failure: PdfError | undefined;
235
360
  /** Called when a document on this instance closes. */
@@ -240,12 +365,22 @@ export declare function engineFailure(instance: Instance, error: unknown): unkno
240
365
  export declare class RectoDocument implements PdfDocument {
241
366
  #private;
242
367
  readonly pageCount: number;
243
- constructor(instance: Instance, doc: Glue.Document, fetchFailure?: () => PdfError | null);
368
+ constructor(instance: Instance, doc: Glue.Document, reopen: (doc: Glue.Document, instance: Instance) => {
369
+ instance: Instance;
370
+ doc: Glue.Document;
371
+ }, fetchFailure?: () => PdfError | null);
244
372
  pageInfo(index: number): PdfPageInfo;
245
373
  text(index: number): PdfPageText;
246
374
  objects(index: number): PdfPageObjects;
375
+ pageLabels(): string[] | null;
376
+ structureTruncated(): {
377
+ pages: boolean;
378
+ pageLabels: boolean;
379
+ outline: boolean;
380
+ };
247
381
  outline(): PdfOutlineItem[];
248
382
  annotations(index: number): PdfAnnotation[];
383
+ links(index: number): PdfLink[];
249
384
  scan(index: number): {
250
385
  text: PdfPageText;
251
386
  objects: PdfPageObjects;
@@ -255,7 +390,14 @@ export declare class RectoDocument implements PdfDocument {
255
390
  text: PdfPageText;
256
391
  annotations: PdfAnnotation[];
257
392
  };
393
+ render(index: number, options: PdfRenderOptions & {
394
+ region: PdfBox;
395
+ }): PdfRegionBitmap;
258
396
  render(index: number, options: PdfRenderOptions): PdfBitmap;
397
+ renderAsync(index: number, options: PdfRenderAsyncOptions & {
398
+ region: PdfBox;
399
+ }): Promise<PdfRegionBitmap>;
400
+ renderAsync(index: number, options: PdfRenderAsyncOptions): Promise<PdfBitmap>;
259
401
  close(): void;
260
402
  [Symbol.dispose](): void;
261
403
  }