@embedpdf/plugin-render 3.0.0-next.13 → 3.0.0-next.15
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/dist/contract/host.cjs +18 -0
- package/dist/contract/host.cjs.map +1 -0
- package/dist/contract/host.d.cts +3 -0
- package/dist/contract/host.d.ts +3 -0
- package/dist/contract/host.js +16 -0
- package/dist/contract/host.js.map +1 -0
- package/dist/contract-CGcu-84H.cjs +124 -0
- package/dist/contract-CGcu-84H.cjs.map +1 -0
- package/dist/contract-Dy5TiQDy.js +95 -0
- package/dist/contract-Dy5TiQDy.js.map +1 -0
- package/dist/contract-uc5nOice.d.cts +279 -0
- package/dist/contract-uc5nOice.d.ts +279 -0
- package/dist/contract.cjs +3 -1
- package/dist/contract.d.cts +2 -2
- package/dist/contract.d.ts +2 -2
- package/dist/contract.js +2 -2
- package/dist/host-contract-CTOQn0Xz.d.cts +127 -0
- package/dist/host-contract-CsjZjWjb.d.ts +127 -0
- package/dist/index.cjs +869 -493
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -12
- package/dist/index.d.ts +29 -12
- package/dist/index.js +866 -492
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
- package/dist/contract-Bvlo2Ukp.cjs +0 -11
- package/dist/contract-Bvlo2Ukp.cjs.map +0 -1
- package/dist/contract-BvwW7rrm.js +0 -7
- package/dist/contract-BvwW7rrm.js.map +0 -1
- package/dist/contract-CWPMd1OW.d.cts +0 -332
- package/dist/contract-CWPMd1OW.d.ts +0 -332
package/dist/index.cjs
CHANGED
|
@@ -1,72 +1,255 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_contract = require("./contract-
|
|
2
|
+
const require_contract = require("./contract-CGcu-84H.cjs");
|
|
3
|
+
const require_contract_host = require("./contract/host.cjs");
|
|
3
4
|
let _embedpdf_core = require("@embedpdf/core");
|
|
4
5
|
let _embedpdf_core_geometry = require("@embedpdf/core-geometry");
|
|
5
|
-
//#region src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
//#region src/appearance-store.ts
|
|
7
|
+
/**
|
|
8
|
+
* The engine's pictures of a page's annotations or form widgets, shared
|
|
9
|
+
* between the views and plugins that draw them. One ask is one key (the
|
|
10
|
+
* page, the family, the scale and the owner's items at their versions):
|
|
11
|
+
* asks with the same key share one fetch.
|
|
12
|
+
*
|
|
13
|
+
* Consumer aborts are refcounted, as in the raster store: a view that leaves
|
|
14
|
+
* detaches without stopping the fetch for the others; the fetch stops only
|
|
15
|
+
* when the last one leaves before it resolved.
|
|
16
|
+
*
|
|
17
|
+
* Kept for undo:
|
|
18
|
+
* - the last {@link SETS_KEPT} sets of each page and family: an undo that
|
|
19
|
+
* brings a page back to an earlier set asks the engine for nothing;
|
|
20
|
+
* - each item's last pictures ({@link RecentPictures}), a while after it left
|
|
21
|
+
* its page: an undo that brings it back shows them at once.
|
|
22
|
+
*/
|
|
23
|
+
var AppearanceStore = class {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
26
|
+
this.kept = /* @__PURE__ */ new Map();
|
|
27
|
+
}
|
|
28
|
+
/** `group` names the page and family; `key` the whole ask. */
|
|
29
|
+
acquire(group, key, fetch, signal) {
|
|
30
|
+
const existing = this.entries.get(key);
|
|
31
|
+
if (existing) {
|
|
32
|
+
if (existing.resolved) this.keep(group, key);
|
|
33
|
+
return this.attach(key, existing, signal);
|
|
34
|
+
}
|
|
35
|
+
const entry = {
|
|
36
|
+
group,
|
|
37
|
+
promise: void 0,
|
|
38
|
+
refs: 0,
|
|
39
|
+
abort: new AbortController(),
|
|
40
|
+
resolved: false
|
|
41
|
+
};
|
|
42
|
+
entry.promise = fetch(entry.abort.signal).then((pictures) => {
|
|
43
|
+
entry.resolved = true;
|
|
44
|
+
this.keep(group, key);
|
|
45
|
+
return pictures;
|
|
46
|
+
}, (error) => {
|
|
47
|
+
this.drop(key, entry);
|
|
48
|
+
throw error;
|
|
49
|
+
});
|
|
50
|
+
entry.promise.catch(() => {});
|
|
51
|
+
this.entries.set(key, entry);
|
|
52
|
+
return this.attach(key, entry, signal);
|
|
53
|
+
}
|
|
54
|
+
/** Keep `key` as the group's newest set, letting go of the oldest beyond the few kept. */
|
|
55
|
+
keep(group, key) {
|
|
56
|
+
const kept = (this.kept.get(group) ?? []).filter((each) => each !== key);
|
|
57
|
+
kept.push(key);
|
|
58
|
+
const gone = kept.splice(0, Math.max(0, kept.length - 4));
|
|
59
|
+
this.kept.set(group, kept);
|
|
60
|
+
for (const old of gone) {
|
|
61
|
+
const entry = this.entries.get(old);
|
|
62
|
+
if (entry && entry.refs <= 0) this.entries.delete(old);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
isKept(group, key) {
|
|
66
|
+
return this.kept.get(group)?.includes(key) ?? false;
|
|
67
|
+
}
|
|
68
|
+
attach(key, entry, signal) {
|
|
69
|
+
if (signal?.aborted) return Promise.reject(abortReason$1(signal));
|
|
70
|
+
entry.refs += 1;
|
|
71
|
+
if (!signal) {
|
|
72
|
+
const release = () => this.release(key, entry);
|
|
73
|
+
entry.promise.then(release, release);
|
|
74
|
+
return entry.promise;
|
|
75
|
+
}
|
|
76
|
+
let settled = false;
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const onAbort = () => {
|
|
79
|
+
if (settled) return;
|
|
80
|
+
settled = true;
|
|
81
|
+
entry.refs -= 1;
|
|
82
|
+
if (entry.refs <= 0 && !entry.resolved) {
|
|
83
|
+
entry.abort.abort(abortReason$1(signal));
|
|
84
|
+
this.drop(key, entry);
|
|
85
|
+
}
|
|
86
|
+
reject(abortReason$1(signal));
|
|
87
|
+
};
|
|
88
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
89
|
+
entry.promise.then((pictures) => {
|
|
90
|
+
if (settled) return;
|
|
91
|
+
settled = true;
|
|
92
|
+
this.release(key, entry);
|
|
93
|
+
signal.removeEventListener("abort", onAbort);
|
|
94
|
+
resolve(pictures);
|
|
95
|
+
}, (error) => {
|
|
96
|
+
if (settled) return;
|
|
97
|
+
settled = true;
|
|
98
|
+
this.release(key, entry);
|
|
99
|
+
signal.removeEventListener("abort", onAbort);
|
|
100
|
+
reject(error);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/** A consumer let go: a set no longer kept goes with the last one. */
|
|
105
|
+
release(key, entry) {
|
|
106
|
+
entry.refs -= 1;
|
|
107
|
+
if (entry.refs <= 0 && entry.resolved && !this.isKept(entry.group, key)) this.drop(key, entry);
|
|
108
|
+
}
|
|
109
|
+
drop(key, entry) {
|
|
110
|
+
if (this.entries.get(key) === entry) this.entries.delete(key);
|
|
111
|
+
}
|
|
11
112
|
};
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
113
|
+
/**
|
|
114
|
+
* The last pictures of each item (all its states), the newest {@link ITEMS_KEPT}
|
|
115
|
+
* items per family, kept after the item left its page.
|
|
116
|
+
*/
|
|
117
|
+
var RecentPictures = class {
|
|
118
|
+
constructor() {
|
|
119
|
+
this.families = /* @__PURE__ */ new Map();
|
|
120
|
+
}
|
|
121
|
+
/** A fetch's pictures, grouped by item: each replaces what its item had. */
|
|
122
|
+
remember(family, pictures, keyOf) {
|
|
123
|
+
const items = this.families.get(family) ?? /* @__PURE__ */ new Map();
|
|
124
|
+
const byItem = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const picture of pictures) {
|
|
126
|
+
const key = keyOf(picture);
|
|
127
|
+
byItem.set(key, [...byItem.get(key) ?? [], picture]);
|
|
128
|
+
}
|
|
129
|
+
for (const [key, itemPictures] of byItem) {
|
|
130
|
+
items.delete(key);
|
|
131
|
+
items.set(key, itemPictures);
|
|
132
|
+
}
|
|
133
|
+
while (items.size > 200) items.delete(items.keys().next().value);
|
|
134
|
+
this.families.set(family, items);
|
|
135
|
+
}
|
|
136
|
+
of(family, key) {
|
|
137
|
+
return this.families.get(family)?.get(key) ?? [];
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
function abortReason$1(signal) {
|
|
141
|
+
return signal.reason ?? new DOMException("aborted", "AbortError");
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/invalidation.ts
|
|
145
|
+
const placedPages = (widgets) => widgets.flatMap((widget) => widget.page ? [widget.page.objectNumber] : []);
|
|
146
|
+
const changeOf = (pages, scope) => pages.length ? {
|
|
147
|
+
pages: [...new Set(pages)],
|
|
148
|
+
scope
|
|
149
|
+
} : null;
|
|
150
|
+
/**
|
|
151
|
+
* The pages a form fact repaints. Every form fact names the widgets whose
|
|
152
|
+
* look changed or that went (`meta.changedWidgets`), each with its page; the
|
|
153
|
+
* widget rows it carries, and a deleted widget's page, are counted too. An
|
|
154
|
+
* unplaced widget (`page: null`) has no pixels to repaint.
|
|
155
|
+
*/
|
|
156
|
+
function formPagesOf(event) {
|
|
157
|
+
const pages = placedPages(event.meta.changedWidgets);
|
|
158
|
+
if ("widgets" in event) pages.push(...placedPages(event.widgets));
|
|
159
|
+
if ("page" in event && event.page) pages.push(event.page.objectNumber);
|
|
160
|
+
return pages;
|
|
161
|
+
}
|
|
162
|
+
function pixelChangeOf(event, allPageObjectNumbers) {
|
|
163
|
+
switch (event.type) {
|
|
164
|
+
case "annotations.created":
|
|
165
|
+
case "annotations.updated":
|
|
166
|
+
case "annotations.deleted":
|
|
167
|
+
case "annotations.restored":
|
|
168
|
+
case "annotations.reordered": return changeOf([event.page.objectNumber], "annotations");
|
|
169
|
+
case "forms.valueSet":
|
|
170
|
+
case "forms.created":
|
|
171
|
+
case "forms.updated":
|
|
172
|
+
case "forms.deleted":
|
|
173
|
+
case "forms.restored":
|
|
174
|
+
case "forms.widgetAdded":
|
|
175
|
+
case "forms.widgetRemoved":
|
|
176
|
+
case "forms.widgetDeleted":
|
|
177
|
+
case "forms.widgetRestored":
|
|
178
|
+
case "forms.widgetUpdated":
|
|
179
|
+
case "forms.widgetsReordered": return changeOf(formPagesOf(event), "fields");
|
|
180
|
+
case "forms.repaired": return changeOf(allPageObjectNumbers(), "fields");
|
|
181
|
+
case "signatures.completed": return changeOf(event.signature.widget ? placedPages([event.signature.widget]) : [], "fields");
|
|
182
|
+
case "redaction.applied":
|
|
183
|
+
case "pages.flattened": return changeOf(event.results.filter((result) => result.status === "applied").map((result) => result.page.objectNumber), "content");
|
|
184
|
+
case "annotations.flattened": return changeOf(event.results.some((result) => result.status === "applied") ? [event.page.objectNumber] : [], "content");
|
|
185
|
+
case "stream.desynced": return changeOf(allPageObjectNumbers(), "content");
|
|
186
|
+
default: return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/model.ts
|
|
191
|
+
const initialRenderState = () => ({
|
|
192
|
+
contentEpochs: {},
|
|
193
|
+
annotationEpochs: {},
|
|
194
|
+
fieldEpochs: {}
|
|
195
|
+
});
|
|
196
|
+
const bump = (epochs, pageObjectNumbers) => {
|
|
197
|
+
const next = { ...epochs };
|
|
198
|
+
for (const pageObjectNumber of pageObjectNumbers) next[pageObjectNumber] = (next[pageObjectNumber] ?? 0) + 1;
|
|
199
|
+
return next;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Bump the pages' versions in the ledger the scope names. A content fact
|
|
203
|
+
* bumps only the content ledger; every picture counts content (see
|
|
204
|
+
* {@link renderEpochOf}), which is how content invalidation reaches them all.
|
|
205
|
+
*/
|
|
206
|
+
function invalidatePages(state, pageObjectNumbers, scope) {
|
|
207
|
+
if (pageObjectNumbers.length === 0) return state;
|
|
208
|
+
switch (scope) {
|
|
209
|
+
case "content": return {
|
|
210
|
+
...state,
|
|
211
|
+
contentEpochs: bump(state.contentEpochs, pageObjectNumbers)
|
|
212
|
+
};
|
|
213
|
+
case "annotations": return {
|
|
214
|
+
...state,
|
|
215
|
+
annotationEpochs: bump(state.annotationEpochs, pageObjectNumbers)
|
|
216
|
+
};
|
|
217
|
+
case "fields": return {
|
|
218
|
+
...state,
|
|
219
|
+
fieldEpochs: bump(state.fieldEpochs, pageObjectNumbers)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** The version of one picture of a page: its content, plus the annotations and form fields it draws. */
|
|
224
|
+
function renderEpochOf(state, pageObjectNumber, layers) {
|
|
225
|
+
return (state.contentEpochs[pageObjectNumber] ?? 0) + (layers.includeAnnotations ? state.annotationEpochs[pageObjectNumber] ?? 0 : 0) + (layers.includeFormFields ? state.fieldEpochs[pageObjectNumber] ?? 0 : 0);
|
|
46
226
|
}
|
|
227
|
+
/** What a picture draws, as one key part: `a1f0` draws annotations and no form fields. */
|
|
228
|
+
const layersKeyOf = (layers) => `a${layers.includeAnnotations ? 1 : 0}f${layers.includeFormFields ? 1 : 0}`;
|
|
47
229
|
//#endregion
|
|
48
230
|
//#region src/raster-store.ts
|
|
49
231
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
232
|
+
* The client-side counterpart of the server's read-through cache: per-key
|
|
233
|
+
* singleflight + cost-budgeted LRU over encoded page images.
|
|
52
234
|
*
|
|
53
|
-
* The key is the raster's canonical identity (
|
|
54
|
-
* annotations flag
|
|
235
|
+
* The key is the raster's canonical identity (for page rasters: width,
|
|
236
|
+
* annotations flag, epoch and format, built by `rasterKey` in the
|
|
237
|
+
* controller; for tiles: `TileManager`'s tile key), so the render points do
|
|
55
238
|
* the caching work: every ask at one point is the same key, and
|
|
56
239
|
* re-crossings (zoom 1280 → 2560 → back) resolve instantly from the LRU
|
|
57
240
|
* instead of re-rendering. Epoch bumps mint new keys; stale-epoch entries
|
|
58
241
|
* age out through the LRU (they are never re-requested).
|
|
59
242
|
*
|
|
60
|
-
* The budget is
|
|
243
|
+
* The budget is cost, approximately bytes: an entry costs its encoded byte
|
|
61
244
|
* length when the handle carries bytes (local), its pixel count as a
|
|
62
245
|
* decoded-order estimate otherwise (URL-backed cloud handles), and 1 when
|
|
63
246
|
* neither is known. Entry counts lie — a 512² webp tile and a BMP full page
|
|
64
247
|
* differ by 100× — and long deep-zoom sessions cycle hundreds of tile keys,
|
|
65
248
|
* so the budget is what actually bounds memory.
|
|
66
249
|
*
|
|
67
|
-
* Consumer aborts are
|
|
250
|
+
* Consumer aborts are refcounted: a caller abandoning a fetch (camera moved,
|
|
68
251
|
* layer unmounted) detaches without killing it for other consumers; the
|
|
69
|
-
* underlying engine call aborts only when the
|
|
252
|
+
* underlying engine call aborts only when the last in-flight consumer leaves.
|
|
70
253
|
* Resolved entries are kept (evictable by LRU) — an abort after resolution
|
|
71
254
|
* is a no-op by design.
|
|
72
255
|
*/
|
|
@@ -96,9 +279,9 @@ var RasterStore = class {
|
|
|
96
279
|
this.total += entry.cost;
|
|
97
280
|
this.evict();
|
|
98
281
|
return handle;
|
|
99
|
-
}, (
|
|
282
|
+
}, (error) => {
|
|
100
283
|
this.drop(key, entry);
|
|
101
|
-
throw
|
|
284
|
+
throw error;
|
|
102
285
|
});
|
|
103
286
|
entry.promise.catch(() => {});
|
|
104
287
|
this.entries.set(key, entry);
|
|
@@ -147,12 +330,12 @@ var RasterStore = class {
|
|
|
147
330
|
entry.refs -= 1;
|
|
148
331
|
signal.removeEventListener("abort", onAbort);
|
|
149
332
|
resolve(handle);
|
|
150
|
-
}, (
|
|
333
|
+
}, (error) => {
|
|
151
334
|
if (settled) return;
|
|
152
335
|
settled = true;
|
|
153
336
|
entry.refs -= 1;
|
|
154
337
|
signal.removeEventListener("abort", onAbort);
|
|
155
|
-
reject(
|
|
338
|
+
reject(error);
|
|
156
339
|
});
|
|
157
340
|
});
|
|
158
341
|
}
|
|
@@ -164,7 +347,7 @@ var RasterStore = class {
|
|
|
164
347
|
}
|
|
165
348
|
evict() {
|
|
166
349
|
if (this.total <= this.maxCost) return;
|
|
167
|
-
const candidates = [...this.entries.entries()].filter(([,
|
|
350
|
+
const candidates = [...this.entries.entries()].filter(([, entry]) => entry.refs <= 0 && entry.handle !== void 0).sort((left, right) => left[1].used - right[1].used);
|
|
168
351
|
for (const [key, entry] of candidates) {
|
|
169
352
|
if (this.total <= this.maxCost) return;
|
|
170
353
|
this.drop(key, entry);
|
|
@@ -183,11 +366,11 @@ function abortReason(signal) {
|
|
|
183
366
|
}
|
|
184
367
|
//#endregion
|
|
185
368
|
//#region src/strategy.ts
|
|
186
|
-
const ascending = (values) => [...values].sort((
|
|
369
|
+
const ascending = (values) => [...values].sort((left, right) => left - right);
|
|
187
370
|
/** Rung list capped by the budget — always keeps at least the smallest rung. */
|
|
188
371
|
const capWidths = (widths, maxWidth) => {
|
|
189
372
|
const sorted = ascending(widths);
|
|
190
|
-
const kept = sorted.filter((
|
|
373
|
+
const kept = sorted.filter((width) => width <= maxWidth);
|
|
191
374
|
return kept.length ? kept : sorted.slice(0, 1);
|
|
192
375
|
};
|
|
193
376
|
function resolveStrategy(policy, options) {
|
|
@@ -211,15 +394,16 @@ function resolveStrategy(policy, options) {
|
|
|
211
394
|
};
|
|
212
395
|
}
|
|
213
396
|
/**
|
|
214
|
-
* The
|
|
215
|
-
* `
|
|
397
|
+
* The one base-sizing function. Its callers: the controller's
|
|
398
|
+
* `conformViewport` (which `renderSource` and `getSourceKey` go through)
|
|
399
|
+
* and tile engagement in the tile manager. Exact mode
|
|
216
400
|
* returns the (settled) demand itself up to the budget; ladder mode snaps
|
|
217
|
-
*
|
|
401
|
+
* up and caps at the top rung.
|
|
218
402
|
*/
|
|
219
403
|
function baseAskWidth(strategy, demandWidth) {
|
|
220
404
|
const wanted = Math.max(1, Math.round(demandWidth));
|
|
221
405
|
if (strategy.widths === null) return Math.min(wanted, strategy.maxWidth);
|
|
222
|
-
return strategy.widths.find((
|
|
406
|
+
return strategy.widths.find((width) => width >= wanted) ?? strategy.widths[strategy.widths.length - 1];
|
|
223
407
|
}
|
|
224
408
|
//#endregion
|
|
225
409
|
//#region src/tiles.ts
|
|
@@ -239,22 +423,22 @@ const tileSpanPt = (grid) => grid.tileSize / grid.scale;
|
|
|
239
423
|
*/
|
|
240
424
|
function tilesInRect(grid, page, rect) {
|
|
241
425
|
const span = tileSpanPt(grid);
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
for (let iy =
|
|
426
|
+
const firstColumn = Math.max(0, Math.floor(rect.x / span));
|
|
427
|
+
const firstRow = Math.max(0, Math.floor(rect.y / span));
|
|
428
|
+
const lastColumn = Math.min(grid.cols - 1, Math.ceil((rect.x + rect.width) / span) - 1);
|
|
429
|
+
const lastRow = Math.min(grid.rows - 1, Math.ceil((rect.y + rect.height) / span) - 1);
|
|
430
|
+
const coords = [];
|
|
431
|
+
for (let iy = firstRow; iy <= lastRow; iy++) for (let ix = firstColumn; ix <= lastColumn; ix++) coords.push({
|
|
248
432
|
ix,
|
|
249
433
|
iy
|
|
250
434
|
});
|
|
251
|
-
return
|
|
435
|
+
return coords;
|
|
252
436
|
}
|
|
253
437
|
/** Paint rect: y-down page points, clamped to the page edge. */
|
|
254
|
-
function tilePaintRect(grid, page,
|
|
438
|
+
function tilePaintRect(grid, page, coord) {
|
|
255
439
|
const span = tileSpanPt(grid);
|
|
256
|
-
const x =
|
|
257
|
-
const y =
|
|
440
|
+
const x = coord.ix * span;
|
|
441
|
+
const y = coord.iy * span;
|
|
258
442
|
return {
|
|
259
443
|
x,
|
|
260
444
|
y,
|
|
@@ -262,43 +446,33 @@ function tilePaintRect(grid, page, c) {
|
|
|
262
446
|
height: Math.min(span, page.height - y)
|
|
263
447
|
};
|
|
264
448
|
}
|
|
265
|
-
/** Engine target rect (y-UP PDF user space, top > bottom) for an arbitrary
|
|
266
|
-
* y-down page-point rect. */
|
|
267
|
-
function toEngineRect(page, rect) {
|
|
268
|
-
return {
|
|
269
|
-
left: rect.x,
|
|
270
|
-
right: rect.x + rect.width,
|
|
271
|
-
top: page.height - rect.y,
|
|
272
|
-
bottom: page.height - (rect.y + rect.height)
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
449
|
/**
|
|
276
|
-
* Expand a paint rect by `
|
|
277
|
-
* the tile
|
|
450
|
+
* Expand a paint rect by `amountPt` page points per side, clamped to the page —
|
|
451
|
+
* the tile bleed. Neighboring tiles rendered with bleed overlap by twice
|
|
278
452
|
* this amount, and the overlapping strips contain identical content (same
|
|
279
453
|
* page region, same scale), so every img edge composites over its
|
|
280
454
|
* neighbor's duplicated pixels instead of the backdrop: no AA hairlines,
|
|
281
455
|
* no bilinear edge smear, at any CSS stretch.
|
|
282
456
|
*/
|
|
283
|
-
function bleedRect(rect,
|
|
284
|
-
const x = Math.max(0, rect.x -
|
|
285
|
-
const y = Math.max(0, rect.y -
|
|
457
|
+
function bleedRect(rect, amountPt, page) {
|
|
458
|
+
const x = Math.max(0, rect.x - amountPt);
|
|
459
|
+
const y = Math.max(0, rect.y - amountPt);
|
|
286
460
|
return {
|
|
287
461
|
x,
|
|
288
462
|
y,
|
|
289
|
-
width: Math.min(page.width, rect.x + rect.width +
|
|
290
|
-
height: Math.min(page.height, rect.y + rect.height +
|
|
463
|
+
width: Math.min(page.width, rect.x + rect.width + amountPt) - x,
|
|
464
|
+
height: Math.min(page.height, rect.y + rect.height + amountPt) - y
|
|
291
465
|
};
|
|
292
466
|
}
|
|
293
467
|
/**
|
|
294
|
-
* Does a set of
|
|
468
|
+
* Does a set of painted want-level tiles cover `region` (y-down points)?
|
|
295
469
|
* The retention release check: a retained source may leave the paint list
|
|
296
470
|
* once its visible footprint answers true here. Pure index arithmetic on
|
|
297
471
|
* the aligned grid — the whole reason the pyramid is aligned.
|
|
298
472
|
*/
|
|
299
473
|
function regionCovered(grid, page, region, isPainted) {
|
|
300
474
|
if (region.width <= 0 || region.height <= 0) return true;
|
|
301
|
-
for (const
|
|
475
|
+
for (const coord of tilesInRect(grid, page, region)) if (!isPainted(coord)) return false;
|
|
302
476
|
return true;
|
|
303
477
|
}
|
|
304
478
|
/**
|
|
@@ -308,26 +482,26 @@ function regionCovered(grid, page, region, isPainted) {
|
|
|
308
482
|
* velocity is just data.
|
|
309
483
|
*/
|
|
310
484
|
function inflateRect(rect, margin, velocity) {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
let left =
|
|
314
|
-
let right =
|
|
315
|
-
let up =
|
|
316
|
-
let down =
|
|
485
|
+
const marginX = rect.width * margin;
|
|
486
|
+
const marginY = rect.height * margin;
|
|
487
|
+
let left = marginX;
|
|
488
|
+
let right = marginX;
|
|
489
|
+
let up = marginY;
|
|
490
|
+
let down = marginY;
|
|
317
491
|
if (velocity) {
|
|
318
492
|
if (velocity.dx > 0) {
|
|
319
|
-
right =
|
|
320
|
-
left =
|
|
493
|
+
right = marginX * 2;
|
|
494
|
+
left = marginX / 2;
|
|
321
495
|
} else if (velocity.dx < 0) {
|
|
322
|
-
left =
|
|
323
|
-
right =
|
|
496
|
+
left = marginX * 2;
|
|
497
|
+
right = marginX / 2;
|
|
324
498
|
}
|
|
325
499
|
if (velocity.dy > 0) {
|
|
326
|
-
down =
|
|
327
|
-
up =
|
|
500
|
+
down = marginY * 2;
|
|
501
|
+
up = marginY / 2;
|
|
328
502
|
} else if (velocity.dy < 0) {
|
|
329
|
-
up =
|
|
330
|
-
down =
|
|
503
|
+
up = marginY * 2;
|
|
504
|
+
down = marginY / 2;
|
|
331
505
|
}
|
|
332
506
|
}
|
|
333
507
|
return {
|
|
@@ -337,74 +511,70 @@ function inflateRect(rect, margin, velocity) {
|
|
|
337
511
|
height: rect.height + up + down
|
|
338
512
|
};
|
|
339
513
|
}
|
|
340
|
-
/** Snap
|
|
514
|
+
/** Snap up through a pyramid's sorted scales; cap at the top. */
|
|
341
515
|
function snapToPyramid(scales, needed) {
|
|
342
|
-
const sorted = [...scales].sort((
|
|
343
|
-
return sorted.find((
|
|
516
|
+
const sorted = [...scales].sort((left, right) => left - right);
|
|
517
|
+
return sorted.find((scale) => scale >= needed) ?? sorted[sorted.length - 1];
|
|
344
518
|
}
|
|
345
519
|
//#endregion
|
|
346
520
|
//#region src/tile-manager.ts
|
|
521
|
+
/** One state entry per view of a page (see {@link TileManager}'s `pages`). */
|
|
522
|
+
const stateKey = (view, pageObjectNumber) => `${view}\u0000${pageObjectNumber}`;
|
|
523
|
+
/** Backpressure: raw rasters in flight at once (render + encode transit). */
|
|
524
|
+
const MAX_IN_FLIGHT = 8;
|
|
525
|
+
/** Stage-less (no visibleRect) demand is capped to this many whole-page tiles. */
|
|
526
|
+
const STAGELESS_TILE_CAP = 64;
|
|
347
527
|
/**
|
|
348
|
-
* The tile retention state machine. One instance per
|
|
349
|
-
*
|
|
350
|
-
* the
|
|
528
|
+
* The tile retention state machine. One instance per plugin instance (per
|
|
529
|
+
* document), all levels and pages in one place, over the same RasterStore
|
|
530
|
+
* the base renders use.
|
|
351
531
|
*
|
|
352
|
-
* Invariant it enforces: every screen region paints the sharpest
|
|
532
|
+
* Invariant it enforces: every screen region paints the sharpest painted
|
|
353
533
|
* pixels available; quality per region only goes up until the want set
|
|
354
534
|
* resolves. Mechanics:
|
|
355
|
-
* - want vs paint: `plan()`
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
* current level
|
|
359
|
-
* -
|
|
535
|
+
* - want vs paint: `plan()` queues the want set (visible tiles first,
|
|
536
|
+
* center-out, then the prefetch ring once every visible tile resolved)
|
|
537
|
+
* and returns a paint list drawn only from resolved entries: the
|
|
538
|
+
* current level and retained older generations.
|
|
539
|
+
* - one line for every view and page: a transit slot goes to an on-screen
|
|
540
|
+
* tile of any page before a tile of any prefetch ring, so a ring never
|
|
541
|
+
* takes the slots an on-screen tile needs. The engine ranks the tiles it
|
|
542
|
+
* is given by where each one is on screen.
|
|
543
|
+
* - release-on-occlusion: when a want-level tile reports painted (after the
|
|
360
544
|
* layer's first presentation opportunity), retained sources whose
|
|
361
|
-
* visible footprint is
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
* from cache.
|
|
545
|
+
* visible footprint is covered by painted want tiles leave the paint
|
|
546
|
+
* list. Their bytes stay in the RasterStore (demotion, not eviction), so
|
|
547
|
+
* zooming back re-promotes from cache.
|
|
365
548
|
* - epoch exception: an invalidation bump means retained pixels are
|
|
366
|
-
*
|
|
549
|
+
* wrong, so everything of the old epoch drops immediately.
|
|
367
550
|
*
|
|
368
|
-
* Levels come from the resolved
|
|
369
|
-
* under a lattice (or the opt-in client ladder), the
|
|
370
|
-
* under exact mode
|
|
551
|
+
* Levels come from the resolved strategy (policy ∧ options): a pyramid
|
|
552
|
+
* under a lattice (or the opt-in client ladder), the exact settled scale
|
|
553
|
+
* under exact mode, where the level identity is the demand's device width
|
|
371
554
|
* across the page, so keys stay integer and stable. The retention/coverage
|
|
372
555
|
* math is generic over any mix of retained scales.
|
|
373
556
|
*
|
|
374
|
-
* `plan()`
|
|
375
|
-
* is idempotent (the store
|
|
376
|
-
*
|
|
557
|
+
* `plan()` never wakes readers itself: its callers do, once, when the
|
|
558
|
+
* returned plan changed. Fetch kickoff is idempotent (the store
|
|
559
|
+
* singleflights); resolution handlers call `onAdvance`, which re-plans the
|
|
560
|
+
* page and wakes subscribed layers, and hand the freed slot on.
|
|
377
561
|
*/
|
|
378
|
-
/** One state entry per VIEW-of-page (see the class doc). */
|
|
379
|
-
const stateKey = (view, pon) => `${view}\u0000${pon}`;
|
|
380
|
-
/** Backpressure: raw rasters in flight at once (render + encode transit). */
|
|
381
|
-
const MAX_IN_FLIGHT = 8;
|
|
382
|
-
/** Stage-less (no visibleRect) demand is capped to this many whole-page tiles. */
|
|
383
|
-
const STAGELESS_TILE_CAP = 64;
|
|
384
562
|
var TileManager = class {
|
|
385
563
|
constructor(deps) {
|
|
386
564
|
this.deps = deps;
|
|
387
565
|
this.pages = /* @__PURE__ */ new Map();
|
|
388
|
-
this.
|
|
566
|
+
this.strategy = (0, _embedpdf_core.memo)(() => [this.deps.getPolicy(), this.deps.getOptions()], (policy, options) => resolveStrategy(policy, options));
|
|
389
567
|
this.inFlight = 0;
|
|
390
568
|
this.warnedStageless = false;
|
|
391
569
|
}
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
if (
|
|
395
|
-
policy,
|
|
396
|
-
strategy: resolveStrategy(policy, this.deps.options)
|
|
397
|
-
};
|
|
398
|
-
return this.strategyMemo.strategy;
|
|
399
|
-
}
|
|
400
|
-
plan(view, pon, demand, includeAnnotations) {
|
|
401
|
-
const { options } = this.deps;
|
|
402
|
-
if (!options.tiles.enabled) return EMPTY_TILE_PLAN;
|
|
570
|
+
plan(view, pageObjectNumber, demand, layers) {
|
|
571
|
+
const options = this.deps.getOptions();
|
|
572
|
+
if (!options.tiles.enabled) return require_contract.EMPTY_TILE_PLAN;
|
|
403
573
|
const strategy = this.strategy();
|
|
404
|
-
const page = this.deps.getPageSize(
|
|
405
|
-
if (!page) return EMPTY_TILE_PLAN;
|
|
406
|
-
const epoch = this.deps.getEpoch(
|
|
407
|
-
const state = this.pageState(
|
|
574
|
+
const page = this.deps.getPageSize(pageObjectNumber);
|
|
575
|
+
if (!page) return require_contract.EMPTY_TILE_PLAN;
|
|
576
|
+
const epoch = this.deps.getEpoch(pageObjectNumber, layers);
|
|
577
|
+
const state = this.pageState(pageObjectNumber, view);
|
|
408
578
|
if (state.epoch !== epoch) {
|
|
409
579
|
this.abortAll(state);
|
|
410
580
|
state.entries.clear();
|
|
@@ -418,15 +588,16 @@ var TileManager = class {
|
|
|
418
588
|
const deficit = demand.desiredDeviceWidth / supplied;
|
|
419
589
|
if (deficit <= strategy.engageAt) {
|
|
420
590
|
if (state.entries.size || state.wantScale !== null) {
|
|
421
|
-
this.deps.debug?.(`disengage
|
|
591
|
+
this.deps.debug?.(`disengage page=${pageObjectNumber} (deficit ${deficit.toFixed(2)})`);
|
|
422
592
|
this.abortAll(state);
|
|
423
593
|
state.entries.clear();
|
|
424
594
|
state.failedKeys.clear();
|
|
425
595
|
state.wantScale = null;
|
|
426
596
|
state.wantWidth = null;
|
|
427
597
|
state.planCache = null;
|
|
598
|
+
this.pump();
|
|
428
599
|
}
|
|
429
|
-
return EMPTY_TILE_PLAN;
|
|
600
|
+
return require_contract.EMPTY_TILE_PLAN;
|
|
430
601
|
}
|
|
431
602
|
let wantWidth;
|
|
432
603
|
let wantScale;
|
|
@@ -445,7 +616,7 @@ var TileManager = class {
|
|
|
445
616
|
console.warn(`[render] stage-less tile demand of ${Math.round(demand.desiredDeviceWidth)} device px would tile the whole page — clamped to ${maxStagelessWidth}. Supply a visibleRect (host the page in a Stage) for sharp deep zoom.`);
|
|
446
617
|
}
|
|
447
618
|
if (strategy.pyramid) {
|
|
448
|
-
const fitting = strategy.pyramid.filter((
|
|
619
|
+
const fitting = strategy.pyramid.filter((scale) => Math.round(scale * page.width) <= maxStagelessWidth);
|
|
449
620
|
wantScale = fitting.length ? fitting[fitting.length - 1] : strategy.pyramid[0];
|
|
450
621
|
wantWidth = Math.round(wantScale * page.width);
|
|
451
622
|
} else {
|
|
@@ -462,12 +633,12 @@ var TileManager = class {
|
|
|
462
633
|
height: page.height
|
|
463
634
|
};
|
|
464
635
|
state.lastVisible = visible;
|
|
465
|
-
const
|
|
636
|
+
const visibleCoords = tilesInRect(grid, page, visible);
|
|
466
637
|
const ring = inflateRect(visible, options.tiles.prefetchMargin, options.tiles.velocityBias ? demand.velocity : void 0);
|
|
467
|
-
const
|
|
468
|
-
const
|
|
469
|
-
this.schedule(
|
|
470
|
-
if (this.releaseCovered(
|
|
638
|
+
const visibleKeys = new Set(visibleCoords.map((coord) => coordKey(coord)));
|
|
639
|
+
const prefetchCoords = tilesInRect(grid, page, ring).filter((coord) => !visibleKeys.has(coordKey(coord)));
|
|
640
|
+
this.schedule(pageObjectNumber, state, page, grid, wantScale, wantWidth, layers, epoch, visibleCoords, prefetchCoords, visible);
|
|
641
|
+
if (this.releaseCovered(pageObjectNumber, state, page, layers)) {
|
|
471
642
|
state.version += 1;
|
|
472
643
|
state.planCache = null;
|
|
473
644
|
}
|
|
@@ -476,11 +647,11 @@ var TileManager = class {
|
|
|
476
647
|
const paint = [];
|
|
477
648
|
const fetching = [];
|
|
478
649
|
const stale = [];
|
|
479
|
-
const bleedPx = this.deps.
|
|
650
|
+
const bleedPx = this.deps.getOptions().tiles.bleedPx;
|
|
480
651
|
for (const entry of state.entries.values()) {
|
|
481
|
-
const
|
|
652
|
+
const visiblePart = (0, _embedpdf_core_geometry.intersectRects)(entry.rect, visible);
|
|
482
653
|
if (entry.resolved) {
|
|
483
|
-
if (
|
|
654
|
+
if (visiblePart.width > 0 && visiblePart.height > 0) {
|
|
484
655
|
const handle = this.deps.store.peek(entry.key);
|
|
485
656
|
if (!handle) {
|
|
486
657
|
stale.push(entry.key);
|
|
@@ -498,9 +669,10 @@ var TileManager = class {
|
|
|
498
669
|
}
|
|
499
670
|
for (const key of stale) state.entries.delete(key);
|
|
500
671
|
const rank = /* @__PURE__ */ new Map();
|
|
501
|
-
|
|
502
|
-
for (const
|
|
503
|
-
|
|
672
|
+
const scales = [...new Set(paint.map((source) => source.scale))];
|
|
673
|
+
for (const scale of scales.sort((left, right) => left - right)) rank.set(scale, rank.size);
|
|
674
|
+
for (const source of paint) source.z = rank.get(source.scale);
|
|
675
|
+
paint.sort((left, right) => left.z - right.z || left.key.localeCompare(right.key));
|
|
504
676
|
const plan = {
|
|
505
677
|
engaged: true,
|
|
506
678
|
paint,
|
|
@@ -515,41 +687,46 @@ var TileManager = class {
|
|
|
515
687
|
return plan;
|
|
516
688
|
}
|
|
517
689
|
/** The layer's painted report: this key's pixels had a presentation opportunity. */
|
|
518
|
-
sourcePainted(view,
|
|
519
|
-
const state = this.pages.get(stateKey(view,
|
|
690
|
+
sourcePainted(view, pageObjectNumber, key) {
|
|
691
|
+
const state = this.pages.get(stateKey(view, pageObjectNumber));
|
|
520
692
|
const entry = state?.entries.get(key);
|
|
521
693
|
if (!state || !entry || entry.painted) return;
|
|
522
694
|
entry.painted = true;
|
|
523
|
-
const page = this.deps.getPageSize(
|
|
524
|
-
if (page) this.releaseCovered(
|
|
695
|
+
const page = this.deps.getPageSize(pageObjectNumber);
|
|
696
|
+
if (page) this.releaseCovered(pageObjectNumber, state, page, layersOfKey(key));
|
|
525
697
|
state.version += 1;
|
|
526
698
|
state.planCache = null;
|
|
527
|
-
this.deps.onAdvance(
|
|
699
|
+
this.deps.onAdvance(pageObjectNumber);
|
|
528
700
|
}
|
|
529
701
|
/**
|
|
530
702
|
* The inverse report: this key's <img> left the DOM (pan-away, plan drop),
|
|
531
|
-
* so its pixels are
|
|
703
|
+
* so its pixels are not currently compositable. Without this, a tile that
|
|
532
704
|
* unmounts and later remounts is still counted as painted while its new
|
|
533
705
|
* <img> re-decodes — and an adjacent fresh `sourcePainted` could release
|
|
534
706
|
* retained coarse coverage over a region that momentarily has no sharp
|
|
535
|
-
* pixels. Painted is a statement about the
|
|
707
|
+
* pixels. Painted is a statement about the screen, so it follows the DOM.
|
|
536
708
|
*/
|
|
537
|
-
sourceUnpainted(view,
|
|
538
|
-
const entry = this.pages.get(stateKey(view,
|
|
709
|
+
sourceUnpainted(view, pageObjectNumber, key) {
|
|
710
|
+
const entry = this.pages.get(stateKey(view, pageObjectNumber))?.entries.get(key);
|
|
539
711
|
if (entry) entry.painted = false;
|
|
540
712
|
}
|
|
541
|
-
/** A lens
|
|
542
|
-
*
|
|
543
|
-
|
|
544
|
-
|
|
713
|
+
/** A lens stopped painting tiles for the page (it left the view, or its
|
|
714
|
+
* layer paints none): stop fetching, drop bookkeeping. Resolved bytes stay
|
|
715
|
+
* in the RasterStore for a re-mount. */
|
|
716
|
+
releasePage(view, pageObjectNumber) {
|
|
717
|
+
const state = this.pages.get(stateKey(view, pageObjectNumber));
|
|
545
718
|
if (!state) return;
|
|
546
719
|
this.abortAll(state);
|
|
547
|
-
this.pages.delete(stateKey(view,
|
|
720
|
+
this.pages.delete(stateKey(view, pageObjectNumber));
|
|
721
|
+
this.pump();
|
|
548
722
|
}
|
|
549
|
-
pageState(
|
|
550
|
-
let state = this.pages.get(stateKey(view,
|
|
723
|
+
pageState(pageObjectNumber, view) {
|
|
724
|
+
let state = this.pages.get(stateKey(view, pageObjectNumber));
|
|
551
725
|
if (!state) {
|
|
552
726
|
state = {
|
|
727
|
+
view,
|
|
728
|
+
pageObjectNumber,
|
|
729
|
+
queue: null,
|
|
553
730
|
epoch: -1,
|
|
554
731
|
wantScale: null,
|
|
555
732
|
wantWidth: null,
|
|
@@ -557,22 +734,23 @@ var TileManager = class {
|
|
|
557
734
|
failedKeys: /* @__PURE__ */ new Set(),
|
|
558
735
|
version: 0,
|
|
559
736
|
planCache: null,
|
|
560
|
-
|
|
737
|
+
cancelSettle: null,
|
|
561
738
|
pendingLevel: null,
|
|
562
739
|
lastVisible: null
|
|
563
740
|
};
|
|
564
|
-
this.pages.set(stateKey(view,
|
|
741
|
+
this.pages.set(stateKey(view, pageObjectNumber), state);
|
|
565
742
|
}
|
|
566
743
|
return state;
|
|
567
744
|
}
|
|
568
|
-
schedule(
|
|
569
|
-
const
|
|
745
|
+
schedule(pageObjectNumber, state, page, grid, wantScale, wantWidth, layers, epoch, visibleCoords, prefetchCoords, visible) {
|
|
746
|
+
const options = this.deps.getOptions();
|
|
570
747
|
const firstEngage = state.wantWidth === null;
|
|
571
748
|
const levelChanged = state.wantWidth !== null && state.wantWidth !== wantWidth;
|
|
572
749
|
if (levelChanged) state.failedKeys.clear();
|
|
573
750
|
state.wantScale = wantScale;
|
|
574
751
|
state.wantWidth = wantWidth;
|
|
575
|
-
const
|
|
752
|
+
const keyOf = (coord) => this.tileKey(pageObjectNumber, wantWidth, coord, layers, epoch);
|
|
753
|
+
const wanted = new Set([...visibleCoords, ...prefetchCoords].map(keyOf));
|
|
576
754
|
for (const [key, entry] of state.entries) {
|
|
577
755
|
if (wanted.has(key)) continue;
|
|
578
756
|
if (!entry.resolved) {
|
|
@@ -581,110 +759,154 @@ var TileManager = class {
|
|
|
581
759
|
state.entries.delete(key);
|
|
582
760
|
} else if (entry.scale === wantScale) state.entries.delete(key);
|
|
583
761
|
}
|
|
584
|
-
const
|
|
585
|
-
const
|
|
762
|
+
const centerX = visible.x + visible.width / 2;
|
|
763
|
+
const centerY = visible.y + visible.height / 2;
|
|
586
764
|
const span = grid.tileSize / grid.scale;
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
});
|
|
765
|
+
const distanceToCenter = (coord) => {
|
|
766
|
+
const dx = (coord.ix + .5) * span - centerX;
|
|
767
|
+
const dy = (coord.iy + .5) * span - centerY;
|
|
768
|
+
return dx * dx + dy * dy;
|
|
769
|
+
};
|
|
770
|
+
const orderedVisible = [...visibleCoords].sort((left, right) => distanceToCenter(left) - distanceToCenter(right));
|
|
594
771
|
const kickoff = () => {
|
|
595
|
-
|
|
772
|
+
state.queue = {
|
|
773
|
+
coords: orderedVisible.every((coord) => {
|
|
774
|
+
const key = keyOf(coord);
|
|
775
|
+
return state.failedKeys.has(key) || state.entries.get(key)?.resolved === true;
|
|
776
|
+
}) ? [...orderedVisible, ...prefetchCoords] : orderedVisible,
|
|
777
|
+
ringFrom: orderedVisible.length,
|
|
778
|
+
next: 0,
|
|
779
|
+
page,
|
|
780
|
+
grid,
|
|
781
|
+
wantWidth,
|
|
782
|
+
layers,
|
|
783
|
+
epoch
|
|
784
|
+
};
|
|
785
|
+
this.pump();
|
|
596
786
|
};
|
|
597
787
|
if (levelChanged && options.tiles.settleMs > 0) {
|
|
598
|
-
this.deps.debug?.(`arm settle
|
|
788
|
+
this.deps.debug?.(`arm settle page=${pageObjectNumber} level=${wantWidth}`);
|
|
789
|
+
state.queue = null;
|
|
599
790
|
state.pendingLevel = wantWidth;
|
|
600
|
-
|
|
601
|
-
state.
|
|
602
|
-
state.
|
|
603
|
-
this.deps.debug?.(`settle fired
|
|
791
|
+
state.cancelSettle?.();
|
|
792
|
+
state.cancelSettle = this.deps.after(options.tiles.settleMs, () => {
|
|
793
|
+
state.cancelSettle = null;
|
|
794
|
+
this.deps.debug?.(`settle fired page=${pageObjectNumber} level=${wantWidth} current=${state.pendingLevel === wantWidth}`);
|
|
604
795
|
if (state.pendingLevel === wantWidth) kickoff();
|
|
605
|
-
}
|
|
796
|
+
});
|
|
606
797
|
return;
|
|
607
798
|
}
|
|
608
|
-
if (state.
|
|
609
|
-
|
|
610
|
-
state.
|
|
799
|
+
if (state.cancelSettle !== null && !levelChanged && !firstEngage) {
|
|
800
|
+
state.cancelSettle();
|
|
801
|
+
state.cancelSettle = null;
|
|
611
802
|
state.pendingLevel = null;
|
|
612
803
|
}
|
|
613
804
|
kickoff();
|
|
614
805
|
}
|
|
615
|
-
/**
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
806
|
+
/**
|
|
807
|
+
* Starts queued tiles while transit slots are free: the first on-screen
|
|
808
|
+
* tile of any view and page, in the order their queues were set, before
|
|
809
|
+
* any tile of a prefetch ring. Within one view of a page, its queue's order
|
|
810
|
+
* holds. Backpressure bounds raw rasters in transit (render + encode); each
|
|
811
|
+
* resolution hands its slot on through here, so no other pump is needed.
|
|
812
|
+
*/
|
|
813
|
+
pump() {
|
|
814
|
+
const started = /* @__PURE__ */ new Map();
|
|
815
|
+
while (this.inFlight < MAX_IN_FLIGHT) {
|
|
816
|
+
let next = null;
|
|
817
|
+
for (const state of this.pages.values()) {
|
|
818
|
+
const queued = this.nextToStart(state);
|
|
819
|
+
if (!queued || next && queued.prefetch) continue;
|
|
820
|
+
next = {
|
|
821
|
+
state,
|
|
822
|
+
coord: queued.coord
|
|
823
|
+
};
|
|
824
|
+
if (!queued.prefetch) break;
|
|
627
825
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
826
|
+
if (!next) break;
|
|
827
|
+
this.start(next.state, next.coord);
|
|
828
|
+
started.set(next.state, (started.get(next.state) ?? 0) + 1);
|
|
829
|
+
}
|
|
830
|
+
for (const [state, count] of started) this.deps.debug?.(`fetch page=${state.pageObjectNumber} level=${state.queue?.wantWidth} +${count} tiles`);
|
|
831
|
+
}
|
|
832
|
+
/** The first tile of the state's queue not started yet, nor failed at this level. */
|
|
833
|
+
nextToStart(state) {
|
|
834
|
+
const queue = state.queue;
|
|
835
|
+
if (!queue) return null;
|
|
836
|
+
for (; queue.next < queue.coords.length; queue.next += 1) {
|
|
837
|
+
const coord = queue.coords[queue.next];
|
|
838
|
+
const key = this.tileKey(state.pageObjectNumber, queue.wantWidth, coord, queue.layers, queue.epoch);
|
|
839
|
+
if (state.failedKeys.has(key) || state.entries.has(key)) continue;
|
|
840
|
+
return {
|
|
637
841
|
coord,
|
|
638
|
-
|
|
639
|
-
resolved: false,
|
|
640
|
-
painted: false,
|
|
641
|
-
charged: true,
|
|
642
|
-
abort
|
|
842
|
+
prefetch: queue.next >= queue.ringFrom
|
|
643
843
|
};
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
844
|
+
}
|
|
845
|
+
return null;
|
|
846
|
+
}
|
|
847
|
+
/** Starts one queued tile's fetch, in a transit slot. */
|
|
848
|
+
start(state, coord) {
|
|
849
|
+
const { page, grid, wantWidth, layers, epoch } = state.queue;
|
|
850
|
+
const pageObjectNumber = state.pageObjectNumber;
|
|
851
|
+
const key = this.tileKey(pageObjectNumber, wantWidth, coord, layers, epoch);
|
|
852
|
+
const bleedPt = this.deps.getOptions().tiles.bleedPx / grid.scale;
|
|
853
|
+
this.inFlight += 1;
|
|
854
|
+
const abort = new AbortController();
|
|
855
|
+
const logical = tilePaintRect(grid, page, coord);
|
|
856
|
+
const entry = {
|
|
857
|
+
key,
|
|
858
|
+
scale: grid.scale,
|
|
859
|
+
coord,
|
|
860
|
+
rect: logical,
|
|
861
|
+
resolved: false,
|
|
862
|
+
painted: false,
|
|
863
|
+
charged: true,
|
|
864
|
+
abort
|
|
865
|
+
};
|
|
866
|
+
state.entries.set(key, entry);
|
|
867
|
+
state.planCache = null;
|
|
868
|
+
this.deps.store.acquire(key, (signal) => this.deps.fetchTile(state.view, pageObjectNumber, bleedPt > 0 ? bleedRect(logical, bleedPt, page) : logical, grid.scale, layers, signal), abort.signal).then(() => {
|
|
869
|
+
this.releaseSlot(entry);
|
|
870
|
+
if (state.entries.get(key) === entry) {
|
|
648
871
|
entry.resolved = true;
|
|
649
872
|
state.version += 1;
|
|
650
873
|
state.planCache = null;
|
|
651
|
-
this.deps.onAdvance(
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
|
|
874
|
+
this.deps.onAdvance(pageObjectNumber);
|
|
875
|
+
}
|
|
876
|
+
this.pump();
|
|
877
|
+
}, (error) => {
|
|
878
|
+
this.releaseSlot(entry);
|
|
879
|
+
if (state.entries.get(key) === entry) {
|
|
655
880
|
state.entries.delete(key);
|
|
656
881
|
if (!abort.signal.aborted) {
|
|
657
882
|
state.failedKeys.add(key);
|
|
658
|
-
this.deps.debug?.(`tile failed ${key}: ${String(
|
|
883
|
+
this.deps.debug?.(`tile failed ${key}: ${String(error)}`);
|
|
659
884
|
state.version += 1;
|
|
660
885
|
state.planCache = null;
|
|
661
|
-
this.deps.onAdvance(
|
|
886
|
+
this.deps.onAdvance(pageObjectNumber);
|
|
662
887
|
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
return allReady;
|
|
888
|
+
}
|
|
889
|
+
this.pump();
|
|
890
|
+
});
|
|
667
891
|
}
|
|
668
892
|
/**
|
|
669
|
-
* The release rule, evaluated as a
|
|
670
|
-
* set: retained sources (any non-want level) whose
|
|
671
|
-
* covered by
|
|
893
|
+
* The release rule, evaluated as a pure function of the current painted
|
|
894
|
+
* set: retained sources (any non-want level) whose visible footprint is
|
|
895
|
+
* covered by painted want-level tiles leave the paint list. Runs on every
|
|
672
896
|
* `plan()` and on every painted report — never only "against the tile
|
|
673
|
-
* that just painted": release must not depend on paint
|
|
897
|
+
* that just painted": release must not depend on paint order (center-out
|
|
674
898
|
* scheduling means the report that completes a retained tile's coverage
|
|
675
899
|
* routinely lands far away from it), and re-evaluating from current state
|
|
676
900
|
* also self-heals any painted-flag transition the reports raced past.
|
|
677
|
-
* Coverage is index arithmetic over the
|
|
901
|
+
* Coverage is index arithmetic over the want grid — generic over whatever
|
|
678
902
|
* mix of retained scales exists (exact levels included).
|
|
679
903
|
*/
|
|
680
|
-
releaseCovered(
|
|
904
|
+
releaseCovered(pageObjectNumber, state, page, layers) {
|
|
681
905
|
const { wantScale, wantWidth } = state;
|
|
682
906
|
if (wantScale === null || wantWidth === null) return false;
|
|
683
907
|
const grid = tileGrid(page, wantScale, this.strategy().tileSize);
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
return state.entries.get(key)?.painted === true;
|
|
687
|
-
};
|
|
908
|
+
const wantKeyOf = (coord) => this.tileKey(pageObjectNumber, wantWidth, coord, layers, state.epoch);
|
|
909
|
+
const paintedAt = (coord) => state.entries.get(wantKeyOf(coord))?.painted === true;
|
|
688
910
|
let released = false;
|
|
689
911
|
for (const [key, entry] of state.entries) {
|
|
690
912
|
if (entry.scale === wantScale || !entry.resolved) continue;
|
|
@@ -692,13 +914,12 @@ var TileManager = class {
|
|
|
692
914
|
if (regionCovered(grid, page, region, paintedAt)) {
|
|
693
915
|
state.entries.delete(key);
|
|
694
916
|
released = true;
|
|
695
|
-
} else if (this.deps.debug) {
|
|
696
|
-
const missing = tilesInRect(grid, page, region).filter((
|
|
697
|
-
const detail = missing.slice(0, 3).map((
|
|
698
|
-
const
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
return `${c.ix},${c.iy}=${st}`;
|
|
917
|
+
} else if (this.deps.debug && this.deps.getOptions().debug) {
|
|
918
|
+
const missing = tilesInRect(grid, page, region).filter((coord) => !paintedAt(coord));
|
|
919
|
+
const detail = missing.slice(0, 3).map((coord) => {
|
|
920
|
+
const missingEntry = state.entries.get(wantKeyOf(coord));
|
|
921
|
+
const status = !missingEntry ? "NO-ENTRY" : !missingEntry.resolved ? "PENDING" : "RESOLVED-unpainted";
|
|
922
|
+
return `${coord.ix},${coord.iy}=${status}`;
|
|
702
923
|
});
|
|
703
924
|
this.deps.debug(`retained ${key} blocked by: ${detail.join(" ")} (${missing.length} missing)`);
|
|
704
925
|
}
|
|
@@ -706,23 +927,22 @@ var TileManager = class {
|
|
|
706
927
|
return released;
|
|
707
928
|
}
|
|
708
929
|
/** Test/diagnostic introspection: bookkeeping size for one page. */
|
|
709
|
-
stats(view,
|
|
930
|
+
stats(view, pageObjectNumber) {
|
|
710
931
|
return {
|
|
711
|
-
entries: this.pages.get(stateKey(view,
|
|
932
|
+
entries: this.pages.get(stateKey(view, pageObjectNumber))?.entries.size ?? 0,
|
|
712
933
|
inFlight: this.inFlight
|
|
713
934
|
};
|
|
714
935
|
}
|
|
715
|
-
/** Idempotent transit-slot release — the
|
|
936
|
+
/** Idempotent transit-slot release — the one place inFlight decrements. */
|
|
716
937
|
releaseSlot(entry) {
|
|
717
938
|
if (!entry.charged) return;
|
|
718
939
|
entry.charged = false;
|
|
719
940
|
this.inFlight -= 1;
|
|
720
941
|
}
|
|
721
942
|
abortAll(state) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
943
|
+
state.queue = null;
|
|
944
|
+
state.cancelSettle?.();
|
|
945
|
+
state.cancelSettle = null;
|
|
726
946
|
for (const entry of state.entries.values()) if (!entry.resolved) {
|
|
727
947
|
entry.abort?.abort();
|
|
728
948
|
this.releaseSlot(entry);
|
|
@@ -730,117 +950,306 @@ var TileManager = class {
|
|
|
730
950
|
}
|
|
731
951
|
/** Level identity is the integer device width across the page — integer
|
|
732
952
|
* and stable for pyramid rungs and exact levels alike. The key also
|
|
733
|
-
* carries everything that determines the fetched
|
|
953
|
+
* carries everything that determines the fetched bitmap for a coord
|
|
734
954
|
* (tile size, bleed, encode format): a raster's identity must include
|
|
735
955
|
* its geometry, or a cached handle from one configuration could be
|
|
736
956
|
* stretched into another's rect. Format genuinely varies at runtime —
|
|
737
957
|
* the cloud policy arrives async after open and can change the resolved
|
|
738
958
|
* format under a live store. */
|
|
739
|
-
tileKey(
|
|
740
|
-
const { tiles } = this.deps.
|
|
959
|
+
tileKey(pageObjectNumber, levelWidth, coord, layers, epoch) {
|
|
960
|
+
const { tiles } = this.deps.getOptions();
|
|
741
961
|
const format = this.strategy().format;
|
|
742
962
|
const geometry = `g${tiles.size}.${tiles.bleedPx}${format ? `.${format}` : ""}`;
|
|
743
|
-
return `t:${
|
|
963
|
+
return `t:${pageObjectNumber}|w${levelWidth}|${coord.ix},${coord.iy}|${layersKeyOf(layers)}|e${epoch}|${geometry}`;
|
|
744
964
|
}
|
|
745
965
|
};
|
|
746
|
-
const coordKey = (
|
|
747
|
-
const rectKey = (
|
|
748
|
-
|
|
966
|
+
const coordKey = (coord) => `${coord.ix},${coord.iy}`;
|
|
967
|
+
const rectKey = (rect) => `${Math.round(rect.x)},${Math.round(rect.y)},${Math.round(rect.width)},${Math.round(rect.height)}`;
|
|
968
|
+
/** What a tile key's picture draws, as {@link layersKeyOf} wrote it into the key. */
|
|
969
|
+
const layersOfKey = (key) => {
|
|
970
|
+
const drawn = /\|a([01])f([01])\|/.exec(key);
|
|
971
|
+
return {
|
|
972
|
+
includeAnnotations: drawn?.[1] === "1",
|
|
973
|
+
includeFormFields: drawn?.[2] === "1"
|
|
974
|
+
};
|
|
975
|
+
};
|
|
749
976
|
//#endregion
|
|
750
|
-
//#region src/
|
|
977
|
+
//#region src/controller.ts
|
|
978
|
+
const RENDER_SCOPE = "doc.render";
|
|
979
|
+
const FORMS_SCOPE = "doc.forms.read";
|
|
980
|
+
const ANNOTATIONS_SCOPE = "doc.annotate.read";
|
|
981
|
+
/** Every answer `getLayerRights()` gives, one object each, so the same rights are the same object. */
|
|
982
|
+
const LAYER_RIGHTS = [false, true].map((annotations) => [false, true].map((formFields) => Object.freeze({
|
|
983
|
+
annotations,
|
|
984
|
+
formFields
|
|
985
|
+
})));
|
|
986
|
+
/** An abort reason the engine task accepts (signals carry `unknown`). */
|
|
987
|
+
const reasonOf = (signal) => typeof signal.reason === "string" ? signal.reason : void 0;
|
|
988
|
+
const DEFAULT_BATCH_CONCURRENCY = 4;
|
|
751
989
|
/**
|
|
752
|
-
* The render
|
|
753
|
-
*
|
|
754
|
-
* serves). The engine never snaps
|
|
755
|
-
*
|
|
756
|
-
* collapsing same-key asks in the raster store, exposing stable source
|
|
757
|
-
*
|
|
990
|
+
* The render controller: the one place strategy (config: what the viewer
|
|
991
|
+
* spends) composes with policy (the engine fact: what the deployment
|
|
992
|
+
* serves). The engine never snaps and layers never see either; everything
|
|
993
|
+
* between happens here: conforming demand to the resolved render points,
|
|
994
|
+
* collapsing same-key asks in the raster store, exposing stable source keys,
|
|
995
|
+
* and turning a view's demand into a retention-safe tile plan.
|
|
996
|
+
*
|
|
997
|
+
* Two doors for rasters: `renderPage` (public, exact size) and
|
|
998
|
+
* `renderSource` (host, conformed). Both share the raster store, so a
|
|
999
|
+
* developer's thumbnail and the rail's base plane never render twice.
|
|
1000
|
+
*
|
|
1001
|
+
* Every render it asks for is the picture of a page itself, so it asks the
|
|
1002
|
+
* engine with `priority: 'high'`, naming the view it's for when it knows it:
|
|
1003
|
+
* the engine runs it before anything else about the same place (annotation
|
|
1004
|
+
* appearances, form fields, text geometry, links).
|
|
1005
|
+
*
|
|
1006
|
+
* Two doors for staleness: confirmed document events (see `connect`) and
|
|
1007
|
+
* the `invalidate` verb. Both go through `publishInvalidation`, the one
|
|
1008
|
+
* place the ledger bumps and `onInvalidated` fires.
|
|
1009
|
+
*
|
|
1010
|
+
* The tile manager and the per-view plans live outside state (they hold
|
|
1011
|
+
* live handles and abort controllers); a re-plan wakes readers with
|
|
1012
|
+
* `ctx.notify()`.
|
|
758
1013
|
*/
|
|
759
|
-
function
|
|
760
|
-
const
|
|
761
|
-
const
|
|
1014
|
+
function createRenderController(ctx) {
|
|
1015
|
+
const settings = ctx.settings();
|
|
1016
|
+
const resolvedOptions = (0, _embedpdf_core.memo)(() => [settings.get()], (current) => require_contract.resolveRenderOptions(current));
|
|
762
1017
|
const store = new RasterStore();
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
1018
|
+
const appearances = new AppearanceStore();
|
|
1019
|
+
const recentAppearances = new RecentPictures();
|
|
1020
|
+
const invalidated = ctx.events.source();
|
|
1021
|
+
const renderCompleted = ctx.events.source();
|
|
1022
|
+
const renderFailed = ctx.events.source();
|
|
1023
|
+
const canRender = () => ctx.allows(RENDER_SCOPE);
|
|
1024
|
+
const rights = () => LAYER_RIGHTS[ctx.allows("doc.annotate.read") ? 1 : 0][ctx.allows("doc.forms.read") ? 1 : 0];
|
|
1025
|
+
const layersOf = (options) => {
|
|
1026
|
+
const may = rights();
|
|
1027
|
+
return (0, _embedpdf_core.resolvePageLayers)({
|
|
1028
|
+
...options.includeAnnotations !== void 0 && (may.annotations || !options.includeAnnotations) ? { includeAnnotations: options.includeAnnotations } : {},
|
|
1029
|
+
...options.includeFormFields !== void 0 && (may.formFields || !options.includeFormFields) ? { includeFormFields: options.includeFormFields } : {}
|
|
1030
|
+
}, may);
|
|
1031
|
+
};
|
|
1032
|
+
const pictureLayers = (options, operation) => {
|
|
1033
|
+
if (options.includeAnnotations === true) ctx.assertAllowed("doc.annotate.read", operation);
|
|
1034
|
+
if (options.includeFormFields === true) ctx.assertAllowed(FORMS_SCOPE, operation);
|
|
1035
|
+
return layersOf(options);
|
|
775
1036
|
};
|
|
776
1037
|
let warnedFormat = false;
|
|
777
|
-
const warnDroppedFormat = (
|
|
778
|
-
if (warnedFormat ||
|
|
1038
|
+
const warnDroppedFormat = (strategy, requested) => {
|
|
1039
|
+
if (warnedFormat || requested === void 0 || strategy.format === requested) return;
|
|
779
1040
|
warnedFormat = true;
|
|
780
|
-
console.warn(`[render] format '${
|
|
1041
|
+
console.warn(`[render] format '${requested}' is not in the deployment's formats — using '${strategy.format}'.`);
|
|
781
1042
|
};
|
|
782
1043
|
let warnedBudget = false;
|
|
783
1044
|
const warnPastBudgetNoTiles = (demandWidth, supplied) => {
|
|
784
|
-
if (warnedBudget ||
|
|
1045
|
+
if (warnedBudget || resolvedOptions().tiles.enabled || demandWidth <= supplied * 2) return;
|
|
785
1046
|
warnedBudget = true;
|
|
786
1047
|
console.warn(`[render] demand is ${(demandWidth / supplied).toFixed(1)}× over the base budget and the tile plane is disabled (tiles: false) — the page rests blurry. Raise fullPage.maxWidth or re-enable tiles.`);
|
|
787
1048
|
};
|
|
788
|
-
const
|
|
789
|
-
const
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
return
|
|
1049
|
+
const renderPolicy = () => ctx.document()?.renderPolicy ?? _embedpdf_core.CONTINUOUS_RENDER_POLICY;
|
|
1050
|
+
const currentStrategy = (0, _embedpdf_core.memo)(() => [renderPolicy(), resolvedOptions()], (policy, options) => {
|
|
1051
|
+
const strategy = resolveStrategy(policy, options);
|
|
1052
|
+
warnDroppedFormat(strategy, options.format);
|
|
1053
|
+
return strategy;
|
|
1054
|
+
});
|
|
1055
|
+
const pageWidthOf = (page) => {
|
|
1056
|
+
const info = ctx.getPage(page);
|
|
1057
|
+
if (!info) ctx.assertPageRef(page);
|
|
1058
|
+
return info.size.width;
|
|
793
1059
|
};
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
const demandWidth = scale * pageWidthOf(
|
|
797
|
-
const width = baseAskWidth(
|
|
1060
|
+
const allPageObjectNumbers = () => (ctx.document()?.pages ?? []).map((info) => info.ref.objectNumber);
|
|
1061
|
+
const conformViewport = (page, scale) => {
|
|
1062
|
+
const demandWidth = scale * pageWidthOf(page);
|
|
1063
|
+
const width = baseAskWidth(currentStrategy(), demandWidth);
|
|
798
1064
|
warnPastBudgetNoTiles(demandWidth, width);
|
|
799
1065
|
return {
|
|
800
1066
|
kind: "width",
|
|
801
1067
|
width
|
|
802
1068
|
};
|
|
803
1069
|
};
|
|
804
|
-
const epochOf = (
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1070
|
+
const epochOf = (pageObjectNumber, layers) => renderEpochOf(ctx.state.get(), pageObjectNumber, layers);
|
|
1071
|
+
const rasterKey = (pageObjectNumber, width, layers, format, quality) => `${pageObjectNumber}|w${width}|${layersKeyOf(layers)}|e${epochOf(pageObjectNumber, layers)}${format ? `|f${format}` : ""}${quality !== void 0 ? `|q${quality}` : ""}`;
|
|
1072
|
+
function acquireRaster(page, key, request, signal) {
|
|
1073
|
+
return store.acquire(key, (storeSignal) => {
|
|
1074
|
+
const task = ctx.doc.with({
|
|
1075
|
+
priority: "high",
|
|
1076
|
+
...request.view !== void 0 ? { view: request.view } : {}
|
|
1077
|
+
}).page(page).render.image({
|
|
1078
|
+
viewport: request.viewport,
|
|
1079
|
+
...request.layers,
|
|
1080
|
+
...request.format !== void 0 ? { format: request.format } : {},
|
|
1081
|
+
...request.quality !== void 0 ? { quality: request.quality } : {}
|
|
1082
|
+
});
|
|
1083
|
+
if (storeSignal.aborted) task.abort(reasonOf(storeSignal));
|
|
1084
|
+
else storeSignal.addEventListener("abort", () => task.abort(reasonOf(storeSignal)), { once: true });
|
|
1085
|
+
return task;
|
|
1086
|
+
}, signal).then((image) => {
|
|
1087
|
+
renderCompleted.emit({
|
|
1088
|
+
page,
|
|
1089
|
+
key
|
|
1090
|
+
});
|
|
1091
|
+
return image;
|
|
1092
|
+
}, (error) => {
|
|
1093
|
+
const mapped = (0, _embedpdf_core.toPluginError)("render", error);
|
|
1094
|
+
if (!(0, _embedpdf_core.isPluginError)(mapped, "operation-cancelled") && !(0, _embedpdf_core.isPluginError)(mapped, "instance-closed")) renderFailed.emit({
|
|
1095
|
+
page,
|
|
1096
|
+
key,
|
|
1097
|
+
error: (0, _embedpdf_core.toPluginErrorInfo)(mapped)
|
|
1098
|
+
});
|
|
1099
|
+
throw mapped;
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
/** The host door: conformed to the render points. Async so refusals reject. */
|
|
1103
|
+
async function renderSource(page, { scale, view, signal, ...options }) {
|
|
1104
|
+
ctx.assertAllowed(RENDER_SCOPE, "render.renderSource");
|
|
1105
|
+
const layers = pictureLayers(options, "render.renderSource");
|
|
1106
|
+
const viewport = conformViewport(page, scale);
|
|
1107
|
+
const strategy = currentStrategy();
|
|
1108
|
+
return acquireRaster(page, rasterKey(page.objectNumber, viewport.kind === "width" ? viewport.width : 0, layers, strategy.format, void 0), {
|
|
1109
|
+
viewport,
|
|
1110
|
+
layers,
|
|
1111
|
+
format: strategy.format,
|
|
1112
|
+
quality: strategy.quality,
|
|
1113
|
+
view
|
|
1114
|
+
}, signal);
|
|
1115
|
+
}
|
|
1116
|
+
/** The public door: the requested size, exactly. Async so refusals reject. */
|
|
1117
|
+
async function renderPage(pageArgument, options = {}) {
|
|
1118
|
+
ctx.assertAllowed(RENDER_SCOPE, "render.renderPage");
|
|
1119
|
+
const { ref: page, size } = ctx.pageOf(pageArgument);
|
|
1120
|
+
const width = Math.max(1, Math.round(options.width ?? (options.scale ?? 1) * size.width));
|
|
1121
|
+
const layers = pictureLayers(options, "render.renderPage");
|
|
1122
|
+
const strategy = currentStrategy();
|
|
1123
|
+
const format = options.format ?? strategy.format;
|
|
1124
|
+
const quality = options.quality ?? strategy.quality;
|
|
1125
|
+
const key = rasterKey(page.objectNumber, width, layers, format, options.quality);
|
|
1126
|
+
return ctx.cancellable(options.signal, acquireRaster(page, key, {
|
|
1127
|
+
viewport: {
|
|
1128
|
+
kind: "width",
|
|
1129
|
+
width
|
|
1130
|
+
},
|
|
1131
|
+
layers,
|
|
1132
|
+
format,
|
|
1133
|
+
quality,
|
|
1134
|
+
view: void 0
|
|
1135
|
+
}, options.signal));
|
|
1136
|
+
}
|
|
1137
|
+
async function renderPages(pages, options = {}) {
|
|
1138
|
+
ctx.assertAllowed(RENDER_SCOPE, "render.renderPages");
|
|
1139
|
+
const concurrency = Math.max(1, options.concurrency ?? DEFAULT_BATCH_CONCURRENCY);
|
|
1140
|
+
const applied = [];
|
|
1141
|
+
const failed = [];
|
|
1142
|
+
const queue = pages.map((page, index) => ({
|
|
1143
|
+
page,
|
|
1144
|
+
index
|
|
1145
|
+
}));
|
|
1146
|
+
const worker = async () => {
|
|
1147
|
+
for (let next = queue.shift(); next; next = queue.shift()) {
|
|
1148
|
+
const { page, index } = next;
|
|
1149
|
+
try {
|
|
1150
|
+
const image = await renderPage(page, options);
|
|
1151
|
+
applied.push({
|
|
1152
|
+
index,
|
|
1153
|
+
render: {
|
|
1154
|
+
page: ctx.pageOf(page).ref,
|
|
1155
|
+
image
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
failed.push({
|
|
1160
|
+
index,
|
|
1161
|
+
ref: page,
|
|
1162
|
+
error: (0, _embedpdf_core.toPluginErrorInfo)((0, _embedpdf_core.toPluginError)("render", error))
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
const batch = Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, worker));
|
|
1168
|
+
await ctx.cancellable(options.signal, batch);
|
|
1169
|
+
const byInput = (left, right) => left.index - right.index;
|
|
1170
|
+
return {
|
|
1171
|
+
applied: applied.sort(byInput).map(({ render }) => render),
|
|
1172
|
+
skipped: [],
|
|
1173
|
+
failed: failed.sort(byInput).map(({ ref, error }) => ({
|
|
1174
|
+
ref,
|
|
1175
|
+
error
|
|
1176
|
+
}))
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
async function renderAppearances(page, { family, scale, items, signal }) {
|
|
1180
|
+
ctx.assertAllowed(family === "widgets" ? FORMS_SCOPE : ANNOTATIONS_SCOPE, "render.renderAppearances");
|
|
1181
|
+
const pageObjectNumber = ctx.pageOf(page).ref.objectNumber;
|
|
1182
|
+
const versions = new Map(items.map((item) => [(0, _embedpdf_core.annotationKey)(item.ref), item.version]));
|
|
1183
|
+
const asked = [...versions].map(([item, version]) => `${item}@${version}`).sort();
|
|
1184
|
+
const group = `${family}|${pageObjectNumber}`;
|
|
1185
|
+
const pictures = appearances.acquire(group, `${group}|s${scale}|${asked.join(",")}`, async (fetchSignal) => {
|
|
1186
|
+
const pageHandle = ctx.doc.page((0, _embedpdf_core.toPageRef)(pageObjectNumber));
|
|
1187
|
+
const options = {
|
|
1188
|
+
viewport: {
|
|
1189
|
+
kind: "scale",
|
|
1190
|
+
scale
|
|
1191
|
+
},
|
|
1192
|
+
modes: ["normal"]
|
|
1193
|
+
};
|
|
1194
|
+
const task = family === "widgets" ? pageHandle.forms.renderAppearances(options) : pageHandle.annotations.renderAppearances(options);
|
|
1195
|
+
if (fetchSignal.aborted) task.abort(reasonOf(fetchSignal));
|
|
1196
|
+
else fetchSignal.addEventListener("abort", () => task.abort(reasonOf(fetchSignal)), { once: true });
|
|
1197
|
+
const drawn = (await task).appearances.flatMap((picture) => {
|
|
1198
|
+
const version = versions.get((0, _embedpdf_core.annotationKey)(picture.ref));
|
|
1199
|
+
return version === void 0 ? [] : [{
|
|
1200
|
+
...picture,
|
|
1201
|
+
version
|
|
1202
|
+
}];
|
|
1203
|
+
});
|
|
1204
|
+
recentAppearances.remember(family, drawn, (picture) => (0, _embedpdf_core.annotationKey)(picture.ref));
|
|
1205
|
+
return drawn;
|
|
1206
|
+
}, signal);
|
|
1207
|
+
return ctx.cancellable(signal, pictures.catch((error) => {
|
|
1208
|
+
throw (0, _embedpdf_core.toPluginError)("render", error);
|
|
1209
|
+
}));
|
|
1210
|
+
}
|
|
1211
|
+
function publishInvalidation(pageObjectNumbers, scope, origin) {
|
|
1212
|
+
if (pageObjectNumbers.length === 0) return;
|
|
1213
|
+
ctx.state.update(invalidatePages, pageObjectNumbers, scope);
|
|
1214
|
+
for (const pageObjectNumber of pageObjectNumbers) replanPage(pageObjectNumber);
|
|
1215
|
+
invalidated.emit({
|
|
1216
|
+
pages: pageObjectNumbers.map((pageObjectNumber) => (0, _embedpdf_core.toPageRef)(pageObjectNumber)),
|
|
1217
|
+
scope,
|
|
1218
|
+
origin
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
function invalidate({ pages, scope = "content" } = {}) {
|
|
1222
|
+
const named = pages?.flatMap((page) => ctx.getPage(page)?.ref.objectNumber ?? []);
|
|
1223
|
+
publishInvalidation(named ?? allPageObjectNumbers(), scope, null);
|
|
1224
|
+
}
|
|
814
1225
|
const pendingAdvance = /* @__PURE__ */ new Set();
|
|
815
1226
|
let advanceScheduled = false;
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
pendingAdvance.add(pon);
|
|
1227
|
+
const wake = (pageObjectNumber) => {
|
|
1228
|
+
pendingAdvance.add(pageObjectNumber);
|
|
819
1229
|
if (advanceScheduled) return;
|
|
820
1230
|
advanceScheduled = true;
|
|
821
|
-
|
|
1231
|
+
ctx.clock.nextFrame(() => {
|
|
822
1232
|
advanceScheduled = false;
|
|
823
|
-
const
|
|
1233
|
+
const advanced = [...pendingAdvance];
|
|
824
1234
|
pendingAdvance.clear();
|
|
825
|
-
for (const
|
|
826
|
-
type: "PAINT_ADVANCED",
|
|
827
|
-
pon: p
|
|
828
|
-
});
|
|
1235
|
+
for (const advancedPage of advanced) replanPage(advancedPage);
|
|
829
1236
|
});
|
|
830
1237
|
};
|
|
831
|
-
const viewTiles = /* @__PURE__ */ new Map();
|
|
832
1238
|
const tiles = new TileManager({
|
|
833
1239
|
store,
|
|
834
|
-
|
|
835
|
-
getPolicy:
|
|
836
|
-
getPageSize: (
|
|
1240
|
+
getOptions: resolvedOptions,
|
|
1241
|
+
getPolicy: renderPolicy,
|
|
1242
|
+
getPageSize: (pageObjectNumber) => ctx.getPage((0, _embedpdf_core.toPageRef)(pageObjectNumber))?.size,
|
|
837
1243
|
getEpoch: epochOf,
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
const task = doc.
|
|
1244
|
+
after: ctx.clock.after,
|
|
1245
|
+
fetchTile: async (view, pageObjectNumber, rect, scale, layers, signal) => {
|
|
1246
|
+
ctx.assertAllowed(RENDER_SCOPE, "render.tile");
|
|
1247
|
+
const page = (0, _embedpdf_core.toPageRef)(pageObjectNumber);
|
|
1248
|
+
const strategy = currentStrategy();
|
|
1249
|
+
const task = ctx.doc.with({
|
|
1250
|
+
priority: "high",
|
|
1251
|
+
view
|
|
1252
|
+
}).page(page).render.image({
|
|
844
1253
|
target: {
|
|
845
1254
|
kind: "rect",
|
|
846
1255
|
rect
|
|
@@ -849,184 +1258,151 @@ function createRenderCapability(ctx, options = {}) {
|
|
|
849
1258
|
kind: "scale",
|
|
850
1259
|
scale
|
|
851
1260
|
},
|
|
852
|
-
|
|
853
|
-
...
|
|
854
|
-
...
|
|
1261
|
+
...layers,
|
|
1262
|
+
...strategy.format !== void 0 ? { format: strategy.format } : {},
|
|
1263
|
+
...strategy.quality !== void 0 ? { quality: strategy.quality } : {}
|
|
855
1264
|
});
|
|
856
|
-
if (signal.aborted) task.abort(signal
|
|
857
|
-
else signal.addEventListener("abort", () => task.abort(signal
|
|
1265
|
+
if (signal.aborted) task.abort(reasonOf(signal));
|
|
1266
|
+
else signal.addEventListener("abort", () => task.abort(reasonOf(signal)), { once: true });
|
|
858
1267
|
return task;
|
|
859
1268
|
},
|
|
860
1269
|
onAdvance: wake,
|
|
861
|
-
|
|
1270
|
+
debug: (message) => {
|
|
1271
|
+
if (resolvedOptions().debug) console.debug(`[render] ${message}`);
|
|
1272
|
+
}
|
|
862
1273
|
});
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
includeAnnotations: annotations,
|
|
877
|
-
...s.format !== void 0 ? { format: s.format } : {},
|
|
878
|
-
...s.quality !== void 0 ? { quality: s.quality } : {}
|
|
879
|
-
});
|
|
880
|
-
if (storeSignal.aborted) task.abort(storeSignal.reason);
|
|
881
|
-
else storeSignal.addEventListener("abort", () => task.abort(storeSignal.reason), { once: true });
|
|
882
|
-
return task;
|
|
883
|
-
}, signal);
|
|
884
|
-
},
|
|
885
|
-
renderSourceKey(pon, { scale, includeAnnotations }) {
|
|
886
|
-
const annotations = includeAnnotations ?? true;
|
|
887
|
-
return baseKey(pon, conformViewport(pon, scale), annotations);
|
|
888
|
-
},
|
|
889
|
-
conformViewport,
|
|
890
|
-
paintSettings() {
|
|
891
|
-
return {
|
|
892
|
-
fadeMs: resolved.tiles.fadeMs,
|
|
893
|
-
tiles: resolved.tiles.enabled
|
|
894
|
-
};
|
|
895
|
-
},
|
|
896
|
-
renderPolicy: policy,
|
|
897
|
-
tilesFor(view) {
|
|
898
|
-
let handle = viewTiles.get(view);
|
|
899
|
-
if (!handle) {
|
|
900
|
-
handle = {
|
|
901
|
-
plan: (pon, demand, opts) => tiles.plan(view, pon, demand, opts?.includeAnnotations ?? true),
|
|
902
|
-
painted: (pon, key) => tiles.sourcePainted(view, pon, key),
|
|
903
|
-
unpainted: (pon, key) => tiles.sourceUnpainted(view, pon, key),
|
|
904
|
-
release: (pon) => tiles.releasePage(view, pon)
|
|
905
|
-
};
|
|
906
|
-
viewTiles.set(view, handle);
|
|
1274
|
+
/** One handle per view id, reference-counted; demands and plans per page. */
|
|
1275
|
+
const views = /* @__PURE__ */ new Map();
|
|
1276
|
+
/** Re-plan a page for every view that wants it (schedules the next want
|
|
1277
|
+
* set), and wake readers once when any plan changed. */
|
|
1278
|
+
function replanPage(pageObjectNumber) {
|
|
1279
|
+
let changed = false;
|
|
1280
|
+
for (const [viewId, view] of views) {
|
|
1281
|
+
const entry = view.pages.get(pageObjectNumber);
|
|
1282
|
+
if (!entry) continue;
|
|
1283
|
+
const plan = tiles.plan(viewId, pageObjectNumber, entry.demand, entry.layers);
|
|
1284
|
+
if (plan !== entry.plan) {
|
|
1285
|
+
entry.plan = plan;
|
|
1286
|
+
changed = true;
|
|
907
1287
|
}
|
|
908
|
-
return handle;
|
|
909
|
-
},
|
|
910
|
-
renderEpoch(pon, includeAnnotations = true) {
|
|
911
|
-
return epochOf(pon, includeAnnotations);
|
|
912
|
-
},
|
|
913
|
-
invalidate({ pons, scope = "content" } = {}) {
|
|
914
|
-
const target = pons ?? (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);
|
|
915
|
-
if (target.length) ctx.dispatch({
|
|
916
|
-
type: "INVALIDATE",
|
|
917
|
-
scope,
|
|
918
|
-
pons: target
|
|
919
|
-
});
|
|
920
1288
|
}
|
|
921
|
-
|
|
922
|
-
}
|
|
923
|
-
//#endregion
|
|
924
|
-
//#region src/effects.ts
|
|
925
|
-
/**
|
|
926
|
-
* Which pages' ANNOTATED raster a confirmed mutation repaints — the whole
|
|
927
|
-
* event→scope map. Everything the engine emits today is appearance-scoped;
|
|
928
|
-
* when it grows content-mutation events (redaction.applied, content.edited),
|
|
929
|
-
* they get a case here dispatching scope 'content'. Over-invalidation is
|
|
930
|
-
* acceptable (a z-order move that changes nothing repaints one thumb);
|
|
931
|
-
* under-invalidation is the bug.
|
|
932
|
-
*/
|
|
933
|
-
function annotatedPons(event, allPons) {
|
|
934
|
-
switch (event.type) {
|
|
935
|
-
case "annotation.created":
|
|
936
|
-
case "annotation.updated":
|
|
937
|
-
case "annotation.deleted":
|
|
938
|
-
case "annotation.moved": return [event.pageObjectNumber];
|
|
939
|
-
case "form.valueChanged":
|
|
940
|
-
case "form.effectsApplied": return event.changedWidgets.map((w) => w.pageObjectNumber);
|
|
941
|
-
case "form.fieldDeleted": return event.removedWidgets.map((w) => w.pageObjectNumber);
|
|
942
|
-
case "form.fieldCreated":
|
|
943
|
-
case "form.fieldUpdated":
|
|
944
|
-
case "form.widgetAttached":
|
|
945
|
-
case "form.widgetDetached": return event.field.widgets.map((w) => w.pageObjectNumber);
|
|
946
|
-
case "form.imported":
|
|
947
|
-
case "form.repaired": return allPons();
|
|
948
|
-
default: return [];
|
|
1289
|
+
if (changed) ctx.notify();
|
|
949
1290
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const unsubscribe = doc.events.subscribe((event) => {
|
|
956
|
-
if (event.type === "redaction.applied") {
|
|
957
|
-
const pons = event.results.filter((r) => r.status === "applied").map((r) => r.pageObjectNumber);
|
|
958
|
-
if (pons.length) ctx.dispatch({
|
|
959
|
-
type: "INVALIDATE",
|
|
960
|
-
scope: "content",
|
|
961
|
-
pons
|
|
962
|
-
});
|
|
963
|
-
return;
|
|
1291
|
+
function createViewDemand(viewId) {
|
|
1292
|
+
const existing = views.get(viewId);
|
|
1293
|
+
if (existing) {
|
|
1294
|
+
existing.refs += 1;
|
|
1295
|
+
return existing.handle;
|
|
964
1296
|
}
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1297
|
+
const pages = /* @__PURE__ */ new Map();
|
|
1298
|
+
const handle = {
|
|
1299
|
+
setDemand: (page, demand, options) => {
|
|
1300
|
+
const pageObjectNumber = page.objectNumber;
|
|
1301
|
+
const layers = layersOf(options ?? {});
|
|
1302
|
+
const previous = pages.get(pageObjectNumber);
|
|
1303
|
+
const plan = tiles.plan(viewId, pageObjectNumber, demand, layers);
|
|
1304
|
+
pages.set(pageObjectNumber, {
|
|
1305
|
+
demand,
|
|
1306
|
+
layers,
|
|
1307
|
+
plan
|
|
1308
|
+
});
|
|
1309
|
+
if (plan !== previous?.plan) ctx.notify();
|
|
1310
|
+
},
|
|
1311
|
+
getPlan: (page) => pages.get(page.objectNumber)?.plan ?? require_contract.EMPTY_TILE_PLAN,
|
|
1312
|
+
markPainted: (page, key) => tiles.sourcePainted(viewId, page.objectNumber, key),
|
|
1313
|
+
markUnpainted: (page, key) => tiles.sourceUnpainted(viewId, page.objectNumber, key),
|
|
1314
|
+
release: (page) => {
|
|
1315
|
+
pages.delete(page.objectNumber);
|
|
1316
|
+
tiles.releasePage(viewId, page.objectNumber);
|
|
1317
|
+
},
|
|
1318
|
+
dispose: () => {
|
|
1319
|
+
const view = views.get(viewId);
|
|
1320
|
+
if (!view || view.refs === 0) return;
|
|
1321
|
+
view.refs -= 1;
|
|
1322
|
+
if (view.refs > 0) return;
|
|
1323
|
+
for (const pageObjectNumber of [...view.pages.keys()]) tiles.releasePage(viewId, pageObjectNumber);
|
|
1324
|
+
view.pages.clear();
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
views.set(viewId, {
|
|
1328
|
+
handle,
|
|
1329
|
+
refs: 1,
|
|
1330
|
+
pages
|
|
970
1331
|
});
|
|
1332
|
+
return handle;
|
|
1333
|
+
}
|
|
1334
|
+
const paintSettings = (0, _embedpdf_core.memo)(() => [resolvedOptions().tiles.enabled], (enabled) => Object.freeze({ tiles: enabled }));
|
|
1335
|
+
ctx.listen(settings.api.onSettingsChanged, () => {
|
|
1336
|
+
const shown = /* @__PURE__ */ new Set();
|
|
1337
|
+
for (const view of views.values()) for (const page of view.pages.keys()) shown.add(page);
|
|
1338
|
+
for (const page of shown) replanPage(page);
|
|
971
1339
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
})
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1340
|
+
return {
|
|
1341
|
+
api: {
|
|
1342
|
+
...settings.api,
|
|
1343
|
+
canRender,
|
|
1344
|
+
renderPage,
|
|
1345
|
+
renderThumbnail: (page, { maxWidth, ...options }) => renderPage(page, {
|
|
1346
|
+
width: maxWidth,
|
|
1347
|
+
...options
|
|
1348
|
+
}),
|
|
1349
|
+
renderPages,
|
|
1350
|
+
getRenderPolicy: renderPolicy,
|
|
1351
|
+
getRenderEpoch: (page, layers = {}) => {
|
|
1352
|
+
const info = ctx.getPage(page);
|
|
1353
|
+
return info ? epochOf(info.ref.objectNumber, layersOf(layers)) : 0;
|
|
1354
|
+
},
|
|
1355
|
+
invalidate,
|
|
1356
|
+
onInvalidated: invalidated.on,
|
|
1357
|
+
renderSource,
|
|
1358
|
+
getSourceKey: (page, { scale, view: _view, ...options }) => {
|
|
1359
|
+
const viewport = conformViewport(page, scale);
|
|
1360
|
+
return rasterKey(page.objectNumber, viewport.kind === "width" ? viewport.width : 0, layersOf(options), currentStrategy().format, void 0);
|
|
1361
|
+
},
|
|
1362
|
+
conformViewport,
|
|
1363
|
+
getPaintSettings: paintSettings,
|
|
1364
|
+
getLayerRights: rights,
|
|
1365
|
+
createViewDemand,
|
|
1366
|
+
renderAppearances,
|
|
1367
|
+
keptAppearances: (family, item) => recentAppearances.of(family, (0, _embedpdf_core.annotationKey)(item)),
|
|
1368
|
+
getAppearanceScale: (renderScale) => (0, _embedpdf_core.snapAppearanceScale)(renderPolicy(), renderScale),
|
|
1369
|
+
onRenderCompleted: renderCompleted.on,
|
|
1370
|
+
onRenderFailed: renderFailed.on
|
|
1371
|
+
},
|
|
1372
|
+
connect() {
|
|
1373
|
+
ctx.listen(ctx.doc.events, (event) => {
|
|
1374
|
+
const change = pixelChangeOf(event, allPageObjectNumbers);
|
|
1375
|
+
if (!change) return;
|
|
1376
|
+
publishInvalidation(change.pages, change.scope, "origin" in event ? event.origin : null);
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1004
1379
|
};
|
|
1005
|
-
}
|
|
1380
|
+
}
|
|
1006
1381
|
//#endregion
|
|
1007
1382
|
//#region src/render.plugin.ts
|
|
1008
1383
|
/**
|
|
1009
|
-
* Document-scoped. The
|
|
1010
|
-
*
|
|
1011
|
-
* policy
|
|
1012
|
-
* same-
|
|
1013
|
-
* demand into a retention-safe paint plan over the
|
|
1014
|
-
* per-page
|
|
1015
|
-
* built-in map and the `invalidate` verb), the policy latch, and the tile
|
|
1016
|
-
* wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch
|
|
1017
|
-
* exactly when those change.
|
|
1384
|
+
* Document-scoped page rasters. The only render-policy consumer in the
|
|
1385
|
+
* client stack: the kernel materializes the engine's advertised render
|
|
1386
|
+
* policy on the document, the controller conforms desired scales to it and
|
|
1387
|
+
* collapses same-key asks in its raster store, and the tile manager turns
|
|
1388
|
+
* host-supplied demand into a retention-safe paint plan over the same store.
|
|
1389
|
+
* State is the per-page raster-version ledger.
|
|
1018
1390
|
*/
|
|
1019
|
-
const renderPlugin = (
|
|
1391
|
+
const renderPlugin = (config) => (0, _embedpdf_core.definePlugin)({
|
|
1020
1392
|
id: "render",
|
|
1021
1393
|
scope: "document",
|
|
1022
|
-
token:
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1394
|
+
token: require_contract_host.RenderToken,
|
|
1395
|
+
state: initialRenderState,
|
|
1396
|
+
settings: {
|
|
1397
|
+
defaults: require_contract.RENDER_DEFAULTS,
|
|
1398
|
+
registered: config
|
|
1399
|
+
},
|
|
1400
|
+
create: createRenderController
|
|
1027
1401
|
});
|
|
1028
1402
|
//#endregion
|
|
1403
|
+
exports.RENDER_DEFAULTS = require_contract.RENDER_DEFAULTS;
|
|
1029
1404
|
exports.RenderToken = require_contract.RenderToken;
|
|
1030
1405
|
exports.renderPlugin = renderPlugin;
|
|
1406
|
+
exports.samePageViewDemand = require_contract.samePageViewDemand;
|
|
1031
1407
|
|
|
1032
1408
|
//# sourceMappingURL=index.cjs.map
|