@onodocs/canvas 0.2.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 (50) hide show
  1. package/LICENSE +10 -0
  2. package/LICENSING.md +33 -0
  3. package/README.md +229 -0
  4. package/SDK.md +229 -0
  5. package/index.js +2428 -0
  6. package/package.json +44 -0
  7. package/types/canvas/canvas-axis-aligned-transform.d.ts +12 -0
  8. package/types/canvas/canvas-effect-surface.d.ts +9 -0
  9. package/types/canvas/canvas-materializer.d.ts +9 -0
  10. package/types/canvas/canvas-native-text.d.ts +6 -0
  11. package/types/canvas/canvas-physical-clips.d.ts +4 -0
  12. package/types/canvas/canvas-positioned-text-replay.d.ts +10 -0
  13. package/types/canvas/canvas-raster-replay.d.ts +7 -0
  14. package/types/canvas/canvas-shadow-replay.d.ts +19 -0
  15. package/types/canvas/canvas-soft-edge-replay.d.ts +9 -0
  16. package/types/canvas/canvas-table-border-replay.d.ts +7 -0
  17. package/types/canvas/canvas-target-pixel-alignment.d.ts +9 -0
  18. package/types/canvas/canvas-vector-shape-replay.d.ts +4 -0
  19. package/types/canvas/evaluation-watermark.d.ts +3 -0
  20. package/types/canvas/index.d.ts +30 -0
  21. package/types/canvas/presentation.d.ts +54 -0
  22. package/types/canvas/text-selection.d.ts +7 -0
  23. package/types/canvas/transport.d.ts +5 -0
  24. package/types/foundation/bounded-number-representation-cjk.d.ts +17 -0
  25. package/types/foundation/bounded-number-representation-decimal-roman-latin.d.ts +6 -0
  26. package/types/foundation/bounded-number-representation-hebrew.d.ts +5 -0
  27. package/types/foundation/bounded-number-representation-korean.d.ts +15 -0
  28. package/types/foundation/bounded-number-representation-mechanics.d.ts +17 -0
  29. package/types/foundation/bounded-number-representation-writer-alphabets.d.ts +22 -0
  30. package/types/foundation/bounded-number-representation-writer-tables.d.ts +13 -0
  31. package/types/foundation/bounded-number-representation.d.ts +18 -0
  32. package/types/foundation/browser-text.d.ts +23 -0
  33. package/types/foundation/chart-number-format.d.ts +3 -0
  34. package/types/foundation/device-rounding.d.ts +3 -0
  35. package/types/foundation/font-resources.d.ts +14 -0
  36. package/types/foundation/geometry-path.d.ts +27 -0
  37. package/types/foundation/host-task.d.ts +3 -0
  38. package/types/foundation/image-resources.d.ts +39 -0
  39. package/types/foundation/index.d.ts +17 -0
  40. package/types/foundation/page-interaction.d.ts +90 -0
  41. package/types/foundation/path-bounds.d.ts +10 -0
  42. package/types/foundation/raster-color-effect.d.ts +55 -0
  43. package/types/foundation/raster-source-rectangle.d.ts +8 -0
  44. package/types/foundation/vector-shape-fill.d.ts +34 -0
  45. package/types/foundation/vector-shape-shadow.d.ts +17 -0
  46. package/types/foundation/vector-shape.d.ts +56 -0
  47. package/types/foundation/writer-unmapped-ooxml-number-format.d.ts +6 -0
  48. package/types/render-api/index.d.ts +4 -0
  49. package/types/render-api/page-display-list.d.ts +508 -0
  50. package/types/render-api/page-source.d.ts +23 -0
package/index.js ADDED
@@ -0,0 +1,2428 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // packages/foundation/src/path-bounds.ts
5
+ function pathBounds(path) {
6
+ let left = Number.POSITIVE_INFINITY;
7
+ let right = Number.NEGATIVE_INFINITY;
8
+ let top = Number.POSITIVE_INFINITY;
9
+ let bottom = Number.NEGATIVE_INFINITY;
10
+ const include = /* @__PURE__ */ __name((x, y) => {
11
+ left = Math.min(left, x);
12
+ right = Math.max(right, x);
13
+ top = Math.min(top, y);
14
+ bottom = Math.max(bottom, y);
15
+ }, "include");
16
+ let current;
17
+ let start;
18
+ for (const operation of path) {
19
+ if (operation.kind === "nativeText") {
20
+ for (const x of [-operation.inkLeft, operation.inkRight]) for (const y of [-operation.ascent, operation.descent]) include(operation.x + operation.a * x + operation.c * y, operation.y + operation.b * x + operation.d * y);
21
+ } else if (operation.kind === "moveTo") {
22
+ current = operation;
23
+ start = operation;
24
+ include(operation.x, operation.y);
25
+ } else if (operation.kind === "closePath") {
26
+ current = start;
27
+ } else if (operation.kind === "lineTo") {
28
+ include(operation.x, operation.y);
29
+ current = operation;
30
+ } else if (operation.kind === "quadraticCurveTo") {
31
+ curveExtrema(current, operation.controlX, operation.controlY, operation.x, operation.y, include);
32
+ current = operation;
33
+ } else if (operation.kind === "bezierCurveTo") {
34
+ cubicExtrema(current, operation.control1X, operation.control1Y, operation.control2X, operation.control2Y, operation.x, operation.y, include);
35
+ current = operation;
36
+ }
37
+ }
38
+ return Number.isFinite(left) && Number.isFinite(right) && Number.isFinite(top) && Number.isFinite(bottom) ? { left, top, width: right - left, height: bottom - top } : void 0;
39
+ }
40
+ __name(pathBounds, "pathBounds");
41
+ function curveExtrema(from, controlX, controlY, x, y, include) {
42
+ include(from.x, from.y);
43
+ include(x, y);
44
+ const coordinate = /* @__PURE__ */ __name((start, control, end) => {
45
+ const denominator = start - 2 * control + end;
46
+ return denominator === 0 ? void 0 : (start - control) / denominator;
47
+ }, "coordinate");
48
+ const at = /* @__PURE__ */ __name((start, control, end, t) => (1 - t) ** 2 * start + 2 * (1 - t) * t * control + t ** 2 * end, "at");
49
+ for (const t of [coordinate(from.x, controlX, x), coordinate(from.y, controlY, y)]) if (t !== void 0 && t > 0 && t < 1) include(at(from.x, controlX, x, t), at(from.y, controlY, y, t));
50
+ }
51
+ __name(curveExtrema, "curveExtrema");
52
+ function cubicExtrema(from, control1X, control1Y, control2X, control2Y, x, y, include) {
53
+ include(from.x, from.y);
54
+ include(x, y);
55
+ const roots = /* @__PURE__ */ __name((start, control1, control2, end) => {
56
+ const a = -start + 3 * control1 - 3 * control2 + end;
57
+ const b = 2 * (start - 2 * control1 + control2);
58
+ const c = control1 - start;
59
+ if (a === 0) return b === 0 ? [] : [-c / b];
60
+ const discriminant = b * b - 4 * a * c;
61
+ return discriminant < 0 ? [] : [(-b + Math.sqrt(discriminant)) / (2 * a), (-b - Math.sqrt(discriminant)) / (2 * a)];
62
+ }, "roots");
63
+ const at = /* @__PURE__ */ __name((start, control1, control2, end, t) => {
64
+ const inverse = 1 - t;
65
+ return inverse ** 3 * start + 3 * inverse ** 2 * t * control1 + 3 * inverse * t ** 2 * control2 + t ** 3 * end;
66
+ }, "at");
67
+ for (const t of [...roots(from.x, control1X, control2X, x), ...roots(from.y, control1Y, control2Y, y)]) if (t > 0 && t < 1) include(at(from.x, control1X, control2X, x, t), at(from.y, control1Y, control2Y, y, t));
68
+ }
69
+ __name(cubicExtrema, "cubicExtrema");
70
+
71
+ // packages/foundation/src/device-rounding.ts
72
+ function roundHalfAwayFromZero(value) {
73
+ return value < 0 ? -Math.round(-value) : Math.round(value);
74
+ }
75
+ __name(roundHalfAwayFromZero, "roundHalfAwayFromZero");
76
+
77
+ // packages/foundation/src/page-interaction.ts
78
+ function geometryPoint(point, transform) {
79
+ return { x: transform.a * point.x + transform.c * point.y + transform.e, y: transform.b * point.x + transform.d * point.y + transform.f };
80
+ }
81
+ __name(geometryPoint, "geometryPoint");
82
+ function hitTestPage(interaction, point) {
83
+ for (let index = interaction.lines.length - 1; index >= 0; index--) {
84
+ const line = interaction.lines[index], fragment = line.fragment;
85
+ const local = inversePoint(point, fragment.transform);
86
+ if (!local || !insideRectangle(local, fragment.rectangle) || !fragment.clips.every((clip) => {
87
+ const p = inversePoint(point, clip.transform);
88
+ return p !== void 0 && insideRectangle(p, clip.rectangle);
89
+ })) continue;
90
+ const carets = line.carets;
91
+ if (!carets.length) continue;
92
+ let nearest = carets[0];
93
+ for (const caret of carets) {
94
+ const distance = Math.abs(local.x - caret.x), previous = Math.abs(local.x - nearest.x);
95
+ if (distance < previous || distance === previous && caret.order < nearest.order) nearest = caret;
96
+ }
97
+ let scalar = nearest.scalarOffset;
98
+ for (let i = 1; i < carets.length; i++) {
99
+ const a = carets[i - 1], b = carets[i];
100
+ if (local.x >= a.x && local.x < b.x && a.scalarOffset !== b.scalarOffset) {
101
+ scalar = (a.scalarOffset + b.scalarOffset) / 2;
102
+ break;
103
+ }
104
+ }
105
+ let span = line.spans.find((span2) => scalar >= span2.start && scalar < span2.end);
106
+ if (!span) {
107
+ for (let i = line.spans.length - 1; i >= 0; i--) if (line.spans[i].end === nearest.scalarOffset) {
108
+ span = line.spans[i];
109
+ break;
110
+ }
111
+ }
112
+ const caretPoint = geometryPoint({ x: nearest.x, y: fragment.rectangle.y }, fragment.transform);
113
+ if (span) {
114
+ const offset = span.offsets.find(([scalar2]) => scalar2 === nearest.scalarOffset)?.[1];
115
+ return { elementId: span.elementId, fragment: span.fragments.find((value) => insideRectangle(local, value.rectangle)) ?? fragment, ...span.paragraphId !== void 0 && offset !== void 0 ? { caret: { paragraphId: span.paragraphId, offset, point: caretPoint } } : {} };
116
+ }
117
+ if (line.elementId !== void 0) return { elementId: line.elementId, fragment, ...line.emptyParagraph ? { caret: { paragraphId: line.elementId, offset: 0, point: caretPoint } } : {} };
118
+ }
119
+ for (let index = interaction.areas.length - 1; index >= 0; index--) {
120
+ const area = interaction.areas[index];
121
+ if (insideRectangle(point, area.fragment.rectangle)) return area;
122
+ }
123
+ return void 0;
124
+ }
125
+ __name(hitTestPage, "hitTestPage");
126
+ function insideRectangle(point, rectangle) {
127
+ return point.x >= rectangle.x && point.x <= rectangle.x + rectangle.width && point.y >= rectangle.y && point.y <= rectangle.y + rectangle.height;
128
+ }
129
+ __name(insideRectangle, "insideRectangle");
130
+ function inversePoint(point, transform) {
131
+ const determinant = transform.a * transform.d - transform.b * transform.c;
132
+ if (determinant === 0) return void 0;
133
+ const x = point.x - transform.e, y = point.y - transform.f;
134
+ return { x: (transform.d * x - transform.c * y) / determinant, y: (transform.a * y - transform.b * x) / determinant };
135
+ }
136
+ __name(inversePoint, "inversePoint");
137
+
138
+ // packages/foundation/src/image-resources.ts
139
+ async function materializeImageResources(data, signal, onDispose = () => {
140
+ }) {
141
+ const gate = { checkpoint: /* @__PURE__ */ __name(() => signal?.throwIfAborted(), "checkpoint") };
142
+ gate.checkpoint();
143
+ return materializeImages(new Map(data.sources.map(({ id, source }) => [id, source])), data.selections, gate, onDispose);
144
+ }
145
+ __name(materializeImageResources, "materializeImageResources");
146
+ async function materializeImages(sources, selections, gate, onDispose) {
147
+ const images = /* @__PURE__ */ new Map();
148
+ const unavailable = /* @__PURE__ */ new Set();
149
+ const composites = /* @__PURE__ */ new Map();
150
+ try {
151
+ for (const selection of selections) {
152
+ gate?.checkpoint();
153
+ if (selection.resource.type === "ResourceId") {
154
+ await materializeOptional(selection.resource, sources, images, unavailable, gate);
155
+ continue;
156
+ }
157
+ if (selection.rasterFallbackOnly) {
158
+ if (selection.resource.rasterFallbackResourceId !== void 0) await materializeOptional(selection.resource.rasterFallbackResourceId, sources, images, unavailable, gate);
159
+ continue;
160
+ }
161
+ const svg = await materializeOptional(selection.resource.svgResourceId, sources, images, unavailable, gate);
162
+ const fallback = svg === void 0 && selection.resource.rasterFallbackResourceId !== void 0 ? await materializeOptional(selection.resource.rasterFallbackResourceId, sources, images, unavailable, gate) : void 0;
163
+ composites.set(compositeKey(selection.resource), svg ?? fallback);
164
+ }
165
+ return new RasterImageMaterializerOwner(images, composites, unavailable, onDispose);
166
+ } catch (error) {
167
+ for (const image of images.values()) image.close?.();
168
+ throw error;
169
+ }
170
+ }
171
+ __name(materializeImages, "materializeImages");
172
+ var RasterImageMaterializerOwner = class {
173
+ static {
174
+ __name(this, "RasterImageMaterializerOwner");
175
+ }
176
+ #images;
177
+ #composites;
178
+ #unavailableImages;
179
+ #preparedImages = /* @__PURE__ */ new Map();
180
+ #sourcePixels = /* @__PURE__ */ new Map();
181
+ #disposed = false;
182
+ #onDispose;
183
+ constructor(images, composites, unavailableImages, onDispose) {
184
+ this.#onDispose = onDispose;
185
+ this.#images = new Map(images);
186
+ this.#composites = new Map(composites);
187
+ this.#unavailableImages = unavailableImages;
188
+ }
189
+ resolveRasterImage(resource, colorEffect) {
190
+ return this.resolveImage(resource, colorEffect)?.image;
191
+ }
192
+ resolveRasterImageIntrinsicPixelSize(resource, colorEffect) {
193
+ return this.resolveImage(resource, colorEffect)?.intrinsicPixelSize;
194
+ }
195
+ resolvePreparedRasterImage(resource, devicePixelSize, colorEffect, flipHorizontal, flipVertical) {
196
+ if (this.#disposed) throw new TypeError("The raster-image resource materialization request is invalid.");
197
+ const image = this.resolveImage(resource, colorEffect);
198
+ if (image === void 0 || image.mediaType === "image/svg+xml") return void 0;
199
+ const effectKey = rasterColorEffectPlanKey(colorEffect);
200
+ const key = `${resourceKey(resource, colorEffect)}:${devicePixelSize.width}x${devicePixelSize.height}:${effectKey}:${flipHorizontal === true}:${flipVertical === true}`;
201
+ if (this.#preparedImages.has(key)) return this.#preparedImages.get(key) ?? void 0;
202
+ const pixels = this.resolveSourcePixels(image, colorEffect, effectKey);
203
+ const prepared = pixels === void 0 ? void 0 : prepareRasterImage(pixels, image.intrinsicPixelSize, devicePixelSize, flipHorizontal, flipVertical);
204
+ this.#preparedImages.set(key, prepared ?? null);
205
+ return prepared;
206
+ }
207
+ dispose() {
208
+ if (this.#disposed) return;
209
+ for (const image of this.#images.values()) image.close?.();
210
+ this.#images.clear();
211
+ this.#composites.clear();
212
+ this.#preparedImages.clear();
213
+ this.#sourcePixels.clear();
214
+ this.#disposed = true;
215
+ this.#onDispose();
216
+ }
217
+ /** Spec: pipeline.parsing.package.raster-image.prepared-sampling#shared-source-pixels */
218
+ resolveSourcePixels(image, colorEffect, effectKey) {
219
+ let variants = this.#sourcePixels.get(image);
220
+ const existing = variants?.get(effectKey);
221
+ if (existing !== void 0) return existing;
222
+ try {
223
+ let source = variants?.get("none");
224
+ if (source === void 0) {
225
+ source = readRasterImagePixels(image);
226
+ if (source === void 0) return void 0;
227
+ if (variants === void 0) this.#sourcePixels.set(image, variants = /* @__PURE__ */ new Map());
228
+ variants.set("none", source);
229
+ }
230
+ if (colorEffect === void 0) return source;
231
+ const pixels = source.slice();
232
+ applyRasterColorEffectPlan(pixels, colorEffect, image.mediaType, image.hasAlpha);
233
+ variants.set(effectKey, pixels);
234
+ return pixels;
235
+ } catch {
236
+ return void 0;
237
+ }
238
+ }
239
+ resolveImage(resource, colorEffect) {
240
+ if (this.#disposed) throw new TypeError("The raster-image resource materialization request is invalid.");
241
+ if (resource.type === "ResourceId") {
242
+ const image = this.#images.get(resource.serialized);
243
+ if (image === void 0 && this.#unavailableImages.has(resource.serialized)) return void 0;
244
+ if (image === void 0) throw new TypeError("The raster image is not available for materialization.");
245
+ return image;
246
+ }
247
+ if (colorEffect !== void 0) return resource.rasterFallbackResourceId === void 0 ? void 0 : this.#images.get(resource.rasterFallbackResourceId.serialized);
248
+ return this.#composites.get(compositeKey(resource));
249
+ }
250
+ };
251
+ async function materializeOptional(resource, resources, images, unavailable, gate) {
252
+ const existing = images.get(resource.serialized);
253
+ if (existing !== void 0) return existing;
254
+ if (unavailable.has(resource.serialized)) return void 0;
255
+ const source = resources.get(resource.serialized);
256
+ if (source === void 0) return void 0;
257
+ gate?.checkpoint();
258
+ try {
259
+ const image = source.mediaType === "image/svg+xml" ? await materializeSvgImage(source) : await materializeRasterImage(source);
260
+ try {
261
+ gate?.checkpoint();
262
+ } catch (error) {
263
+ image.close?.();
264
+ throw error;
265
+ }
266
+ images.set(resource.serialized, image);
267
+ return image;
268
+ } catch (error) {
269
+ gate?.checkpoint();
270
+ unavailable.add(resource.serialized);
271
+ return void 0;
272
+ }
273
+ }
274
+ __name(materializeOptional, "materializeOptional");
275
+ async function materializeRasterImage(source) {
276
+ if (typeof globalThis.createImageBitmap !== "function") throw new TypeError("Raster-image resource materialization requires createImageBitmap.");
277
+ const image = await globalThis.createImageBitmap(imageBlob(source));
278
+ try {
279
+ return { image, intrinsicPixelSize: intrinsicPixelSize(image), mediaType: source.mediaType, ...source.mediaType === "image/png" ? { hasAlpha: pngHasAlpha(source.bytes) } : {}, close: /* @__PURE__ */ __name(() => image.close(), "close") };
280
+ } catch (error) {
281
+ image.close();
282
+ throw error;
283
+ }
284
+ }
285
+ __name(materializeRasterImage, "materializeRasterImage");
286
+ function pngHasAlpha(bytes) {
287
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
288
+ if (bytes[25] === 4 || bytes[25] === 6) return true;
289
+ for (let offset = 8; offset + 12 <= bytes.length; ) {
290
+ const type = view.getUint32(offset + 4);
291
+ if (type === 1951551059) return true;
292
+ if (type === 1229209940 || type === 1229278788) break;
293
+ offset += view.getUint32(offset) + 12;
294
+ }
295
+ return false;
296
+ }
297
+ __name(pngHasAlpha, "pngHasAlpha");
298
+ async function materializeSvgImage(source) {
299
+ if (typeof globalThis.Image !== "function" || typeof globalThis.URL?.createObjectURL !== "function" || typeof globalThis.URL.revokeObjectURL !== "function") throw new TypeError("SVG resource materialization requires browser image URLs.");
300
+ const url = globalThis.URL.createObjectURL(imageBlob(source));
301
+ try {
302
+ const image = await new Promise((resolve, reject) => {
303
+ const element = new globalThis.Image();
304
+ element.onload = () => resolve(element);
305
+ element.onerror = () => reject(new TypeError("The SVG image could not be decoded."));
306
+ element.src = url;
307
+ });
308
+ if (!Number.isSafeInteger(image.naturalWidth) || !Number.isSafeInteger(image.naturalHeight) || image.naturalWidth <= 0 || image.naturalHeight <= 0) throw new TypeError("The SVG decoder produced an invalid intrinsic pixel size.");
309
+ return { image, intrinsicPixelSize: { width: image.naturalWidth, height: image.naturalHeight }, mediaType: source.mediaType };
310
+ } finally {
311
+ globalThis.URL.revokeObjectURL(url);
312
+ }
313
+ }
314
+ __name(materializeSvgImage, "materializeSvgImage");
315
+ function compositeKey(resource) {
316
+ return `${resource.svgResourceId.serialized}:${resource.rasterFallbackResourceId?.serialized ?? "none"}`;
317
+ }
318
+ __name(compositeKey, "compositeKey");
319
+ function resourceKey(resource, colorEffect) {
320
+ return resource.type === "ResourceId" ? resource.serialized : colorEffect === void 0 ? compositeKey(resource) : resource.rasterFallbackResourceId?.serialized ?? "none";
321
+ }
322
+ __name(resourceKey, "resourceKey");
323
+ function intrinsicPixelSize(image) {
324
+ if (!Number.isSafeInteger(image.width) || !Number.isSafeInteger(image.height) || image.width <= 0 || image.height <= 0) throw new TypeError("The raster-image decoder produced an invalid intrinsic pixel size.");
325
+ return { width: image.width, height: image.height };
326
+ }
327
+ __name(intrinsicPixelSize, "intrinsicPixelSize");
328
+ function readRasterImagePixels(image) {
329
+ if (typeof globalThis.OffscreenCanvas !== "function") return void 0;
330
+ const canvas = new OffscreenCanvas(image.intrinsicPixelSize.width, image.intrinsicPixelSize.height);
331
+ const context = canvas.getContext("2d", { willReadFrequently: true });
332
+ if (context === null) return void 0;
333
+ context.imageSmoothingEnabled = false;
334
+ context.drawImage(image.image, 0, 0);
335
+ return context.getImageData(0, 0, canvas.width, canvas.height).data;
336
+ }
337
+ __name(readRasterImagePixels, "readRasterImagePixels");
338
+ function prepareRasterImage(pixels, sourceSize, devicePixelSize, flipHorizontal, flipVertical) {
339
+ if (typeof globalThis.OffscreenCanvas !== "function") return void 0;
340
+ try {
341
+ const preparedCanvas = new OffscreenCanvas(devicePixelSize.width, devicePixelSize.height);
342
+ const preparedContext = preparedCanvas.getContext("2d");
343
+ if (preparedContext === null) return void 0;
344
+ const source = {
345
+ pixels,
346
+ offset: ((flipVertical ? (sourceSize.height - 1) * sourceSize.width : 0) + (flipHorizontal ? sourceSize.width - 1 : 0)) * 4,
347
+ columnStep: flipHorizontal ? -4 : 4,
348
+ rowStep: (flipVertical ? -1 : 1) * sourceSize.width * 4
349
+ };
350
+ const preparedImageData = preparedContext.createImageData(devicePixelSize.width, devicePixelSize.height);
351
+ if (usesOneAxisIntrinsicMagnification(sourceSize, devicePixelSize)) nearestNeighborRasterPixels(source, sourceSize, devicePixelSize, preparedImageData.data);
352
+ else if (devicePixelSize.width < sourceSize.width && devicePixelSize.height < sourceSize.height) averageReducedRasterPixels(source, sourceSize, devicePixelSize, preparedImageData.data);
353
+ else bilinearRasterPixels(source, sourceSize, devicePixelSize, preparedImageData.data);
354
+ preparedContext.putImageData(preparedImageData, 0, 0);
355
+ return preparedCanvas;
356
+ } catch {
357
+ return void 0;
358
+ }
359
+ }
360
+ __name(prepareRasterImage, "prepareRasterImage");
361
+ function applyRasterColorEffectPlan(pixels, plan, mediaType, hasAlpha) {
362
+ if (plan.colorChange !== void 0) applyRasterColorChange(pixels, plan.colorChange, mediaType, hasAlpha);
363
+ if (plan.duotone !== void 0) applyRasterDuotone(pixels, plan.duotone.color1, plan.duotone.color2);
364
+ if (plan.colorMode !== void 0) applyRasterColorEffect(pixels, plan.colorMode);
365
+ if (plan.luminance !== void 0) applyRasterLuminance(pixels, plan.luminance.brightness, plan.luminance.contrast);
366
+ }
367
+ __name(applyRasterColorEffectPlan, "applyRasterColorEffectPlan");
368
+ function applyRasterColorEffect(pixels, effect) {
369
+ const threshold = effect.kind === "biLevel" ? effect.threshold === 50000n ? 128 : Number(effect.threshold * 255n / 100000n) : 0;
370
+ for (let offset = 0; offset < pixels.length; offset += 4) {
371
+ const luminance = 77 * pixels[offset] + 151 * pixels[offset + 1] + 28 * pixels[offset + 2] >> 8;
372
+ const value = effect.kind === "grayscale" ? luminance : luminance >= threshold ? 255 : 0;
373
+ pixels[offset] = value;
374
+ pixels[offset + 1] = value;
375
+ pixels[offset + 2] = value;
376
+ }
377
+ }
378
+ __name(applyRasterColorEffect, "applyRasterColorEffect");
379
+ function applyRasterColorChange(pixels, effect, mediaType, hasAlpha) {
380
+ const { from, to } = effect;
381
+ const sourceHasAlpha = hasAlpha ?? pixels.some((value, index) => index % 4 === 3 && value !== 255);
382
+ const replacementAlpha = colorChangeDestinationAlphaByte(effect.useAlpha ? effect.destinationAlpha ?? 100000n : 100000n);
383
+ if (from.red === to.red && from.green === to.green && from.blue === to.blue && (!effect.useAlpha || effect.destinationAlpha === void 0 || effect.destinationAlpha === 100000n)) return;
384
+ const destinationAlpha = !sourceHasAlpha && replacementAlpha === 0 ? 0 : void 0;
385
+ const tolerance = mediaType === "image/jpeg" ? 15 : mediaType === "image/png" ? 1 : mediaType === "image/bmp" ? 0 : 9;
386
+ const minRed = Math.max(0, from.red - tolerance);
387
+ const maxRed = Math.min(255, from.red + tolerance);
388
+ const minGreen = Math.max(0, from.green - tolerance);
389
+ const maxGreen = Math.min(255, from.green + tolerance);
390
+ const minBlue = Math.max(0, from.blue - tolerance);
391
+ const maxBlue = Math.min(255, from.blue + tolerance);
392
+ const destinationRed = destinationAlpha === 0 ? 0 : to.red;
393
+ const destinationGreen = destinationAlpha === 0 ? 0 : to.green;
394
+ const destinationBlue = destinationAlpha === 0 ? 0 : to.blue;
395
+ for (let offset = 0; offset < pixels.length; offset += 4) {
396
+ if (sourceHasAlpha && pixels[offset + 3] === from.blue) pixels[offset + 3] = replacementAlpha;
397
+ if (pixels[offset] >= minRed && pixels[offset] <= maxRed && pixels[offset + 1] >= minGreen && pixels[offset + 1] <= maxGreen && pixels[offset + 2] >= minBlue && pixels[offset + 2] <= maxBlue) {
398
+ pixels[offset] = destinationRed;
399
+ pixels[offset + 1] = destinationGreen;
400
+ pixels[offset + 2] = destinationBlue;
401
+ if (destinationAlpha !== void 0) pixels[offset + 3] = destinationAlpha;
402
+ }
403
+ if (pixels[offset + 3] === 0) pixels.fill(0, offset, offset + 3);
404
+ }
405
+ }
406
+ __name(applyRasterColorChange, "applyRasterColorChange");
407
+ function colorChangeDestinationAlphaByte(value) {
408
+ const transparency = (100000n - value + 500n) / 1000n;
409
+ return Math.trunc(Number(100n - transparency) * 2.55);
410
+ }
411
+ __name(colorChangeDestinationAlphaByte, "colorChangeDestinationAlphaByte");
412
+ function applyRasterDuotone(pixels, color1, color2) {
413
+ for (let offset = 0; offset < pixels.length; offset += 4) {
414
+ const luminance = 76 * pixels[offset] + 151 * pixels[offset + 1] + 29 * pixels[offset + 2] >> 8;
415
+ pixels[offset] = Math.floor(color2.red * luminance / 255) + Math.floor(color1.red * (255 - luminance) / 255);
416
+ pixels[offset + 1] = Math.floor(color2.green * luminance / 255) + Math.floor(color1.green * (255 - luminance) / 255);
417
+ pixels[offset + 2] = Math.floor(color2.blue * luminance / 255) + Math.floor(color1.blue * (255 - luminance) / 255);
418
+ }
419
+ }
420
+ __name(applyRasterDuotone, "applyRasterDuotone");
421
+ function applyRasterLuminance(pixels, brightness, contrast) {
422
+ const lookup = rasterLuminanceLookupTable(brightness, contrast);
423
+ for (let offset = 0; offset < pixels.length; offset += 4) {
424
+ pixels[offset] = lookup[pixels[offset]];
425
+ pixels[offset + 1] = lookup[pixels[offset + 1]];
426
+ pixels[offset + 2] = lookup[pixels[offset + 2]];
427
+ }
428
+ }
429
+ __name(applyRasterLuminance, "applyRasterLuminance");
430
+ function rasterLuminanceLookupTable(brightness, contrast) {
431
+ const brightnessPercent = Math.trunc(Number(brightness) / 1e3);
432
+ const contrastPercent = Math.trunc(Number(contrast) / 1e3);
433
+ const multiplier = contrastPercent >= 0 ? 128 / (128 - 1.27 * contrastPercent) : (128 + 1.27 * contrastPercent) / 128;
434
+ const offset = 2.55 * brightnessPercent;
435
+ const watermark = brightnessPercent === 70 && contrastPercent === -70;
436
+ const lookup = new Uint8Array(256);
437
+ for (let value = 0; value < lookup.length; value++) {
438
+ const raw = watermark ? value * multiplier + 50 * 2.55 + 128 - multiplier * 128 : (value + offset / 2 - 128) * multiplier + 128 + offset / 2;
439
+ lookup[value] = Math.min(255, Math.max(0, Math.floor(raw + 0.5)));
440
+ }
441
+ return lookup;
442
+ }
443
+ __name(rasterLuminanceLookupTable, "rasterLuminanceLookupTable");
444
+ function rasterColorEffectPlanKey(value) {
445
+ if (value === void 0) return "none";
446
+ return [
447
+ value.colorChange === void 0 ? "" : `colorChange:${value.colorChange.from.red},${value.colorChange.from.green},${value.colorChange.from.blue}:${value.colorChange.to.red},${value.colorChange.to.green},${value.colorChange.to.blue}:${value.colorChange.destinationAlpha === void 0 ? "absent" : value.colorChange.destinationAlpha}:${value.colorChange.useAlpha}`,
448
+ value.duotone === void 0 ? "" : `duotone:${value.duotone.color1.red},${value.duotone.color1.green},${value.duotone.color1.blue}:${value.duotone.color2.red},${value.duotone.color2.green},${value.duotone.color2.blue}`,
449
+ value.colorMode === void 0 ? "" : value.colorMode.kind === "grayscale" ? "grayscale" : `biLevel:${value.colorMode.threshold}`,
450
+ value.luminance === void 0 ? "" : `luminance:${value.luminance.brightness}:${value.luminance.contrast}`
451
+ ].join("|");
452
+ }
453
+ __name(rasterColorEffectPlanKey, "rasterColorEffectPlanKey");
454
+ function usesOneAxisIntrinsicMagnification(sourceSize, devicePixelSize) {
455
+ return devicePixelSize.width === sourceSize.width && devicePixelSize.height > sourceSize.height || devicePixelSize.height === sourceSize.height && devicePixelSize.width > sourceSize.width;
456
+ }
457
+ __name(usesOneAxisIntrinsicMagnification, "usesOneAxisIntrinsicMagnification");
458
+ function nearestNeighborRasterPixels(source, sourceSize, devicePixelSize, scaled) {
459
+ for (let y = 0; y < devicePixelSize.height; y++) {
460
+ const sourceY = Math.min(Math.floor((y + 0.5) * sourceSize.height / devicePixelSize.height), sourceSize.height - 1);
461
+ for (let x = 0; x < devicePixelSize.width; x++) {
462
+ const sourceX = Math.min(Math.floor((x + 0.5) * sourceSize.width / devicePixelSize.width), sourceSize.width - 1);
463
+ const sourceOffset = source.offset + sourceY * source.rowStep + sourceX * source.columnStep;
464
+ const targetOffset = (y * devicePixelSize.width + x) * 4;
465
+ scaled[targetOffset] = source.pixels[sourceOffset];
466
+ scaled[targetOffset + 1] = source.pixels[sourceOffset + 1];
467
+ scaled[targetOffset + 2] = source.pixels[sourceOffset + 2];
468
+ scaled[targetOffset + 3] = source.pixels[sourceOffset + 3];
469
+ }
470
+ }
471
+ }
472
+ __name(nearestNeighborRasterPixels, "nearestNeighborRasterPixels");
473
+ function averageReducedRasterPixels(source, sourceSize, devicePixelSize, scaled) {
474
+ for (let y = 0; y < devicePixelSize.height; y++) {
475
+ const top = Math.floor(y * sourceSize.height / devicePixelSize.height);
476
+ const bottom = Math.floor((y + 1) * sourceSize.height / devicePixelSize.height);
477
+ for (let x = 0; x < devicePixelSize.width; x++) {
478
+ const left = Math.floor(x * sourceSize.width / devicePixelSize.width);
479
+ const right = Math.floor((x + 1) * sourceSize.width / devicePixelSize.width);
480
+ const count = (right - left) * (bottom - top);
481
+ const destination = (y * devicePixelSize.width + x) * 4;
482
+ for (let channel = 0; channel < 4; channel++) {
483
+ let sum = 0;
484
+ for (let row = top; row < bottom; row++) {
485
+ for (let column = left; column < right; column++) sum += source.pixels[source.offset + row * source.rowStep + column * source.columnStep + channel];
486
+ }
487
+ scaled[destination + channel] = Math.floor(sum / count);
488
+ }
489
+ }
490
+ }
491
+ }
492
+ __name(averageReducedRasterPixels, "averageReducedRasterPixels");
493
+ function bilinearRasterPixels(source, sourceSize, devicePixelSize, scaled) {
494
+ const rowBytes = devicePixelSize.width * 4;
495
+ let primaryRow = new Uint8ClampedArray(rowBytes);
496
+ let secondaryRow = new Uint8ClampedArray(rowBytes);
497
+ let primaryY = -1;
498
+ let secondaryY = -1;
499
+ const verticalStep = sourceSize.height / devicePixelSize.height;
500
+ let coordinate = 0;
501
+ for (let y = 0; y < devicePixelSize.height; y++) {
502
+ const primary = Math.floor(coordinate);
503
+ const secondary = Math.min(primary + 1, sourceSize.height - 1);
504
+ const fraction = coordinate - primary;
505
+ if (primary !== primaryY) {
506
+ if (primary === secondaryY) {
507
+ const previousRow = primaryRow;
508
+ primaryRow = secondaryRow;
509
+ secondaryRow = previousRow;
510
+ secondaryY = primaryY;
511
+ } else {
512
+ horizontallySampleRasterRow(source, sourceSize.width, primary, primaryRow);
513
+ }
514
+ primaryY = primary;
515
+ }
516
+ if (secondary !== primary && secondary !== secondaryY) {
517
+ horizontallySampleRasterRow(source, sourceSize.width, secondary, secondaryRow);
518
+ secondaryY = secondary;
519
+ }
520
+ const adjacentRow = secondary === primary ? primaryRow : secondaryRow;
521
+ const targetOffset = y * rowBytes;
522
+ for (let offset = 0; offset < rowBytes; offset++) scaled[targetOffset + offset] = Math.trunc(primaryRow[offset] * (1 - fraction) + adjacentRow[offset] * fraction);
523
+ coordinate += verticalStep;
524
+ }
525
+ }
526
+ __name(bilinearRasterPixels, "bilinearRasterPixels");
527
+ function horizontallySampleRasterRow(source, sourceWidth, sourceY, scaled) {
528
+ const horizontalStep = sourceWidth / (scaled.length / 4);
529
+ let coordinate = 0;
530
+ for (let offset = 0; offset < scaled.length; offset += 4) {
531
+ const primary = Math.floor(coordinate);
532
+ const secondary = Math.min(primary + 1, sourceWidth - 1);
533
+ const fraction = coordinate - primary;
534
+ const sourceOffset = source.offset + sourceY * source.rowStep + primary * source.columnStep;
535
+ const adjacentOffset = source.offset + sourceY * source.rowStep + secondary * source.columnStep;
536
+ for (let channel = 0; channel < 4; channel++) scaled[offset + channel] = Math.trunc(source.pixels[sourceOffset + channel] * (1 - fraction) + source.pixels[adjacentOffset + channel] * fraction);
537
+ coordinate += horizontalStep;
538
+ }
539
+ }
540
+ __name(horizontallySampleRasterRow, "horizontallySampleRasterRow");
541
+ function imageBlob(source) {
542
+ const copy = new Uint8Array(source.bytes.byteLength);
543
+ copy.set(source.bytes);
544
+ return new Blob([copy.buffer], { type: source.mediaType });
545
+ }
546
+ __name(imageBlob, "imageBlob");
547
+
548
+ // packages/canvas/src/canvas-axis-aligned-transform.ts
549
+ function positiveAxisAlignedTransform(target) {
550
+ const transform = target.getTransform?.();
551
+ if (transform === void 0 || transform.a <= 0 || transform.d <= 0 || transform.b !== 0 || transform.c !== 0 || !Number.isFinite(transform.a) || !Number.isFinite(transform.d) || !Number.isFinite(transform.e) || !Number.isFinite(transform.f)) return void 0;
552
+ return transform;
553
+ }
554
+ __name(positiveAxisAlignedTransform, "positiveAxisAlignedTransform");
555
+ function rectangleForDeviceBounds(transform, left, top, deviceWidth, deviceHeight) {
556
+ const x = (left - transform.e) / transform.a;
557
+ const y = (top - transform.f) / transform.d;
558
+ const width = deviceWidth / transform.a;
559
+ const height = deviceHeight / transform.d;
560
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return void 0;
561
+ return { x, y, width, height };
562
+ }
563
+ __name(rectangleForDeviceBounds, "rectangleForDeviceBounds");
564
+
565
+ // packages/canvas/src/canvas-physical-clips.ts
566
+ function replayPhysicalClips(target, clips) {
567
+ for (const clip of clips ?? []) {
568
+ target.beginPath();
569
+ const r = clip.rectangle, t = clip.transform;
570
+ if (t === void 0) target.rect(r.x, r.y, r.width, r.height);
571
+ else {
572
+ const x = t.a * r.x + t.c * r.y + t.e, y = t.b * r.x + t.d * r.y + t.f;
573
+ target.moveTo(x, y);
574
+ target.lineTo(x + t.a * r.width, y + t.b * r.width);
575
+ target.lineTo(x + t.a * r.width + t.c * r.height, y + t.b * r.width + t.d * r.height);
576
+ target.lineTo(x + t.c * r.height, y + t.d * r.height);
577
+ target.closePath();
578
+ }
579
+ target.clip();
580
+ }
581
+ }
582
+ __name(replayPhysicalClips, "replayPhysicalClips");
583
+
584
+ // packages/canvas/src/canvas-native-text.ts
585
+ var rasters = /* @__PURE__ */ new WeakMap();
586
+ var rasterPoolSize = 4;
587
+ function drawNativeText(target, operation, color) {
588
+ const device = target.getTransform();
589
+ const t = { a: device.a, b: device.b, c: device.c, d: device.d, e: device.e, f: operation.strokeWidth === void 0 ? Math.floor(device.f) : device.f };
590
+ const determinant = t.a * t.d - t.b * t.c;
591
+ if (determinant === 0) return;
592
+ let minX = Infinity;
593
+ let minY = Infinity;
594
+ let maxX = -Infinity;
595
+ let maxY = -Infinity;
596
+ for (let corner = 0; corner < 4; corner++) {
597
+ const x = corner & 1 ? operation.inkRight : -operation.inkLeft;
598
+ const y = corner & 2 ? operation.descent : -operation.ascent;
599
+ const transformedX = t.a * x + t.c * y + t.e;
600
+ const transformedY = t.b * x + t.d * y + t.f;
601
+ minX = Math.min(minX, transformedX);
602
+ minY = Math.min(minY, transformedY);
603
+ maxX = Math.max(maxX, transformedX);
604
+ maxY = Math.max(maxY, transformedY);
605
+ }
606
+ const left = Math.floor(minX) - 1;
607
+ const top = Math.floor(minY) - 1;
608
+ const width = Math.ceil(maxX) - left + 1;
609
+ const height = Math.ceil(maxY) - top + 1;
610
+ let pool = rasters.get(target);
611
+ if (pool === void 0) {
612
+ pool = [];
613
+ rasters.set(target, pool);
614
+ }
615
+ const rasterWidth = width * 4;
616
+ const rasterHeight = height * 4;
617
+ let index = 0;
618
+ while (index < pool.length && (pool[index].canvas.width !== rasterWidth || pool[index].canvas.height !== rasterHeight)) index++;
619
+ let retained;
620
+ if (index < pool.length) retained = pool[index];
621
+ else if (pool.length < rasterPoolSize) {
622
+ const canvas2 = document.createElement("canvas");
623
+ retained = { canvas: canvas2, context: canvas2.getContext("2d") };
624
+ pool.push(retained);
625
+ } else {
626
+ index = pool.length - 1;
627
+ retained = pool[index];
628
+ }
629
+ for (; index > 0; index--) pool[index] = pool[index - 1];
630
+ pool[0] = retained;
631
+ const { canvas, context: raster } = retained;
632
+ const resized = canvas.width !== rasterWidth || canvas.height !== rasterHeight;
633
+ if (canvas.width !== rasterWidth) canvas.width = rasterWidth;
634
+ if (canvas.height !== rasterHeight) canvas.height = rasterHeight;
635
+ raster.setTransform(1, 0, 0, 1, 0, 0);
636
+ if (!resized) raster.clearRect(0, 0, rasterWidth, rasterHeight);
637
+ raster.setTransform(t.a * 4, t.b * 4, t.c * 4, t.d * 4, (t.e - left) * 4, (t.f - top) * 4);
638
+ raster.font = operation.font;
639
+ raster.fontKerning = operation.kerning;
640
+ raster.letterSpacing = operation.letterSpacing;
641
+ raster.direction = operation.direction;
642
+ raster.textAlign = "left";
643
+ raster.textBaseline = "alphabetic";
644
+ canvas.lang = operation.language;
645
+ raster.fillStyle = color;
646
+ raster.fillText(operation.text, 0, 0);
647
+ if (operation.strokeWidth !== void 0) {
648
+ raster.strokeStyle = color;
649
+ raster.lineWidth = operation.strokeWidth;
650
+ raster.lineJoin = "round";
651
+ raster.strokeText(operation.text, 0, 0);
652
+ }
653
+ target.transform(device.d / determinant, -device.b / determinant, -device.c / determinant, device.a / determinant, (device.c * device.f - device.d * device.e) / determinant, (device.b * device.e - device.a * device.f) / determinant);
654
+ target.imageSmoothingEnabled = true;
655
+ target.imageSmoothingQuality = "high";
656
+ target.drawImage(canvas, left, top, width, height);
657
+ }
658
+ __name(drawNativeText, "drawNativeText");
659
+
660
+ // packages/canvas/src/canvas-target-pixel-alignment.ts
661
+ function fillTargetPixelAlignedRectangle(target, rectangle) {
662
+ const aligned = targetPixelAlignedRectangleFor(target, rectangle);
663
+ target.fillRect(aligned?.x ?? rectangle.x, aligned?.y ?? rectangle.y, aligned?.width ?? rectangle.width, aligned?.height ?? rectangle.height);
664
+ }
665
+ __name(fillTargetPixelAlignedRectangle, "fillTargetPixelAlignedRectangle");
666
+ function targetPixelAlignedRectangleFor(target, rectangle) {
667
+ const transform = positiveAxisAlignedTransform(target);
668
+ if (transform === void 0 || !Number.isFinite(rectangle.x) || !Number.isFinite(rectangle.y) || !Number.isFinite(rectangle.width) || !Number.isFinite(rectangle.height) || rectangle.width <= 0 || rectangle.height <= 0) return void 0;
669
+ const deviceLeft = rectangle.x * transform.a + transform.e;
670
+ const deviceTop = rectangle.y * transform.d + transform.f;
671
+ const deviceRight = (rectangle.x + rectangle.width) * transform.a + transform.e;
672
+ const deviceBottom = (rectangle.y + rectangle.height) * transform.d + transform.f;
673
+ if (!Number.isFinite(deviceLeft) || !Number.isFinite(deviceTop) || !Number.isFinite(deviceRight) || !Number.isFinite(deviceBottom)) return void 0;
674
+ const left = roundHalfAwayFromZero(deviceLeft);
675
+ const top = roundHalfAwayFromZero(deviceTop);
676
+ const right = roundHalfAwayFromZero(deviceRight);
677
+ const bottom = roundHalfAwayFromZero(deviceBottom);
678
+ if (!Number.isSafeInteger(left) || !Number.isSafeInteger(top) || !Number.isSafeInteger(right) || !Number.isSafeInteger(bottom)) return void 0;
679
+ return rectangleForDeviceBounds(transform, left, top, right - left, bottom - top);
680
+ }
681
+ __name(targetPixelAlignedRectangleFor, "targetPixelAlignedRectangleFor");
682
+
683
+ // packages/canvas/src/canvas-positioned-text-replay.ts
684
+ function drawPositionedText(target, command) {
685
+ if (command.clip !== null || command.transform !== void 0 || command.physicalClips !== void 0) target.save();
686
+ try {
687
+ replayPhysicalClips(target, command.physicalClips);
688
+ if (command.transform !== void 0) target.transform(command.transform.a, command.transform.b, command.transform.c, command.transform.d, command.transform.e, command.transform.f);
689
+ if (command.clip !== null) {
690
+ target.beginPath();
691
+ target.rect(command.clip.x, command.clip.y, command.clip.width, command.clip.height);
692
+ target.clip();
693
+ }
694
+ for (const shading of command.shadings) {
695
+ target.fillStyle = shading.color;
696
+ target.fillRect(shading.rectangle.x, shading.rectangle.y, shading.rectangle.width, shading.rectangle.height);
697
+ }
698
+ for (const highlight of command.highlights) {
699
+ target.fillStyle = highlight.color;
700
+ fillTargetPixelAlignedRectangle(target, highlight.rectangle);
701
+ }
702
+ for (const shadow of command.shadows ?? []) {
703
+ target.fillStyle = shadow.color;
704
+ target.fillRect(shadow.rectangle.x, shadow.rectangle.y, shadow.rectangle.width, shadow.rectangle.height);
705
+ }
706
+ for (const border of command.borders) {
707
+ target.fillStyle = border.color;
708
+ target.fillRect(border.rectangle.x, border.rectangle.y, border.rectangle.width, border.rectangle.height);
709
+ }
710
+ if (command.effectPasses !== void 0) {
711
+ for (const pass of command.effectPasses) {
712
+ target.save();
713
+ try {
714
+ if (pass.clip !== void 0) {
715
+ target.beginPath();
716
+ target.rect(pass.clip.x, pass.clip.y, pass.clip.width, pass.clip.height);
717
+ target.clip();
718
+ }
719
+ target.fillStyle = pass.color;
720
+ fillGlyphRun(target, pass.glyphRun, pass.layoutOffset, pass.color);
721
+ for (const underline of pass.underlines) fillUnderline(target, underline, pass.color, pass.layoutOffset);
722
+ for (const strike of pass.strikes) fillStrike(target, strike, pass.color, pass.layoutOffset);
723
+ } finally {
724
+ target.restore();
725
+ }
726
+ }
727
+ }
728
+ for (const run of command.glyphRuns) {
729
+ target.fillStyle = run.color;
730
+ fillGlyphRun(target, run, noPaintOffset, run.color);
731
+ }
732
+ for (const underline of command.underlines) {
733
+ fillUnderline(target, underline, underline.color, noPaintOffset);
734
+ }
735
+ for (const strike of command.strikes) {
736
+ fillStrike(target, strike, strike.color, noPaintOffset);
737
+ }
738
+ for (const emphasisMark of command.emphasisMarks) {
739
+ target.fillStyle = emphasisMark.color;
740
+ target.beginPath();
741
+ for (const operation of emphasisMark.path) replayPathOperation(target, operation, noPaintOffset);
742
+ target.fill();
743
+ }
744
+ } finally {
745
+ if (command.clip !== null || command.transform !== void 0 || command.physicalClips !== void 0) target.restore();
746
+ }
747
+ }
748
+ __name(drawPositionedText, "drawPositionedText");
749
+ var noPaintOffset = { x: 0, y: 0 };
750
+ function fillGlyphRun(target, run, offset, color) {
751
+ for (const glyph of run.glyphs) {
752
+ for (const operation of glyph.path) {
753
+ target.save();
754
+ try {
755
+ target.transform(operation.a, operation.b, operation.c, operation.d, operation.x + offset.x, operation.y + offset.y);
756
+ drawNativeText(target, operation, color);
757
+ } finally {
758
+ target.restore();
759
+ }
760
+ }
761
+ }
762
+ }
763
+ __name(fillGlyphRun, "fillGlyphRun");
764
+ function fillUnderline(target, underline, color, offset) {
765
+ target.fillStyle = color;
766
+ if (underline.path === void 0) {
767
+ target.fillRect(underline.rectangle.x + offset.x, underline.rectangle.y + offset.y, underline.rectangle.width, underline.rectangle.height);
768
+ return;
769
+ }
770
+ target.beginPath();
771
+ for (const operation of underline.path) replayPathOperation(target, operation, offset);
772
+ if (underline.strokeWidth === void 0) target.fill();
773
+ else {
774
+ target.strokeStyle = color;
775
+ target.lineWidth = underline.strokeWidth;
776
+ target.lineJoin = "miter";
777
+ target.lineCap = "butt";
778
+ target.stroke();
779
+ }
780
+ }
781
+ __name(fillUnderline, "fillUnderline");
782
+ function fillStrike(target, strike, color, offset) {
783
+ target.fillStyle = color;
784
+ target.fillRect(strike.rectangle.x + offset.x, strike.rectangle.y + offset.y, strike.rectangle.width, strike.rectangle.height);
785
+ }
786
+ __name(fillStrike, "fillStrike");
787
+ function replayPathOperation(target, operation, offset) {
788
+ switch (operation.kind) {
789
+ case "moveTo":
790
+ target.moveTo(operation.x + offset.x, operation.y + offset.y);
791
+ break;
792
+ case "lineTo":
793
+ target.lineTo(operation.x + offset.x, operation.y + offset.y);
794
+ break;
795
+ case "quadraticCurveTo":
796
+ target.quadraticCurveTo(operation.controlX + offset.x, operation.controlY + offset.y, operation.x + offset.x, operation.y + offset.y);
797
+ break;
798
+ case "bezierCurveTo":
799
+ target.bezierCurveTo(operation.control1X + offset.x, operation.control1Y + offset.y, operation.control2X + offset.x, operation.control2Y + offset.y, operation.x + offset.x, operation.y + offset.y);
800
+ break;
801
+ case "closePath":
802
+ target.closePath();
803
+ break;
804
+ }
805
+ }
806
+ __name(replayPathOperation, "replayPathOperation");
807
+
808
+ // packages/canvas/src/canvas-effect-surface.ts
809
+ var surfaces = /* @__PURE__ */ new WeakMap();
810
+ function effectSurfaceFor(target, width, height) {
811
+ let surface = surfaces.get(target);
812
+ if (surface === void 0) {
813
+ const canvas2 = document.createElement("canvas");
814
+ surface = { canvas: canvas2, context: canvas2.getContext("2d") };
815
+ surfaces.set(target, surface);
816
+ }
817
+ const { canvas, context } = surface;
818
+ const resized = canvas.width !== width || canvas.height !== height;
819
+ if (canvas.width !== width) canvas.width = width;
820
+ if (canvas.height !== height) canvas.height = height;
821
+ if (!resized) context.reset();
822
+ return surface;
823
+ }
824
+ __name(effectSurfaceFor, "effectSurfaceFor");
825
+
826
+ // packages/canvas/src/canvas-soft-edge-replay.ts
827
+ var retainedWorkspace;
828
+ function drawSoftEdgeRaster(target, rectangle, radius, paint) {
829
+ const transform = target.getTransform();
830
+ const sx = 2 * Math.hypot(transform.a, transform.b), sy = 2 * Math.hypot(transform.c, transform.d);
831
+ const width = Math.ceil(rectangle.width * sx), height = Math.ceil(rectangle.height * sy);
832
+ if (width < 1 || height < 1) return;
833
+ const { canvas, context: raster } = effectSurfaceFor(target, width, height);
834
+ raster.setTransform(sx, 0, 0, sy, -rectangle.x * sx, -rectangle.y * sy);
835
+ paint(raster);
836
+ const pixels = raster.getImageData(0, 0, width, height);
837
+ const workspace = maskWorkspaceFor(width * height, Math.max(width, height) + 1);
838
+ const rx = Math.ceil(radius * sx), ry = Math.ceil(radius * sy);
839
+ erodedMask(pixels.data, width, height, rx, ry, workspace.horizontal, workspace.mask);
840
+ blurMask(workspace.mask, workspace.blur, width, height, rx, false, workspace.sums, workspace.weighted);
841
+ blurMask(workspace.blur, workspace.mask, width, height, ry, true, workspace.sums, workspace.weighted);
842
+ for (let index = 0; index < width * height; index++) pixels.data[index * 4 + 3] = Math.round(pixels.data[index * 4 + 3] * workspace.mask[index]);
843
+ raster.putImageData(pixels, 0, 0);
844
+ target.drawImage(canvas, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
845
+ }
846
+ __name(drawSoftEdgeRaster, "drawSoftEdgeRaster");
847
+ function maskWorkspaceFor(pixels, prefix) {
848
+ const previous = retainedWorkspace;
849
+ if (previous !== void 0 && previous.mask.length >= pixels && previous.sums.length >= prefix) return previous;
850
+ const growPixels = previous === void 0 || previous.mask.length < pixels;
851
+ const growPrefix = previous === void 0 || previous.sums.length < prefix;
852
+ const workspace = {
853
+ horizontal: growPixels ? new Uint8Array(pixels) : previous.horizontal,
854
+ mask: growPixels ? new Float64Array(pixels) : previous.mask,
855
+ blur: growPixels ? new Float64Array(pixels) : previous.blur,
856
+ sums: growPrefix ? new Float64Array(prefix) : previous.sums,
857
+ weighted: growPrefix ? new Float64Array(prefix) : previous.weighted
858
+ };
859
+ retainedWorkspace = workspace;
860
+ return workspace;
861
+ }
862
+ __name(maskWorkspaceFor, "maskWorkspaceFor");
863
+ function erodedMask(pixels, width, height, rx, ry, horizontal, result) {
864
+ for (let y = 0; y < height; y++) {
865
+ let nearest = -1;
866
+ for (let x = 0; x < width; x++) {
867
+ const index = y * width + x;
868
+ if (pixels[index * 4 + 3] === 0) nearest = x;
869
+ horizontal[index] = x - nearest > rx ? 1 : 0;
870
+ }
871
+ nearest = width;
872
+ for (let x = width - 1; x >= 0; x--) {
873
+ const index = y * width + x;
874
+ if (pixels[index * 4 + 3] === 0) nearest = x;
875
+ if (nearest - x <= rx) horizontal[index] = 0;
876
+ }
877
+ }
878
+ for (let x = 0; x < width; x++) {
879
+ let nearest = -1;
880
+ for (let y = 0; y < height; y++) {
881
+ const index = y * width + x;
882
+ if (horizontal[index] === 0) nearest = y;
883
+ result[index] = y - nearest > ry ? 1 : 0;
884
+ }
885
+ nearest = height;
886
+ for (let y = height - 1; y >= 0; y--) {
887
+ const index = y * width + x;
888
+ if (horizontal[index] === 0) nearest = y;
889
+ if (nearest - y <= ry) result[index] = 0;
890
+ }
891
+ }
892
+ }
893
+ __name(erodedMask, "erodedMask");
894
+ function blurMask(source, result, width, height, radius, vertical, sums, weighted) {
895
+ const length = vertical ? height : width, lines = vertical ? width : height;
896
+ const divisor = (radius + 1) ** 2;
897
+ for (let line = 0; line < lines; line++) {
898
+ const offset = vertical ? line : line * width, stride = vertical ? width : 1;
899
+ for (let i = 0; i < length; i++) {
900
+ const value = source[offset + i * stride];
901
+ sums[i + 1] = sums[i] + value;
902
+ weighted[i + 1] = weighted[i] + i * value;
903
+ }
904
+ for (let i = 0; i < length; i++) {
905
+ const left = Math.max(0, i - radius), right = Math.min(length, i + radius + 1);
906
+ const before = (radius + 1 - i) * (sums[i + 1] - sums[left]) + weighted[i + 1] - weighted[left];
907
+ const after = (radius + 1 + i) * (sums[right] - sums[i + 1]) - weighted[right] + weighted[i + 1];
908
+ result[offset + i * stride] = (before + after) / divisor;
909
+ }
910
+ }
911
+ }
912
+ __name(blurMask, "blurMask");
913
+
914
+ // packages/canvas/src/canvas-shadow-replay.ts
915
+ var ShadowBounds = class {
916
+ constructor(device, transform) {
917
+ this.device = device;
918
+ this.transform = transform;
919
+ }
920
+ device;
921
+ transform;
922
+ static {
923
+ __name(this, "ShadowBounds");
924
+ }
925
+ #left = Infinity;
926
+ #top = Infinity;
927
+ #right = -Infinity;
928
+ #bottom = -Infinity;
929
+ includeRectangle(left, top, width, height, padding = 0) {
930
+ const { device, transform } = this;
931
+ for (let corner = 0; corner < 4; corner++) {
932
+ const x = corner & 1 ? left + width + padding : left - padding;
933
+ const y = corner & 2 ? top + height + padding : top - padding;
934
+ const sx = transform === void 0 ? x : transform.a * x + transform.c * y + transform.e;
935
+ const sy = transform === void 0 ? y : transform.b * x + transform.d * y + transform.f;
936
+ const dx = device.a * sx + device.c * sy + device.e;
937
+ const dy = device.b * sx + device.d * sy + device.f;
938
+ this.#left = Math.min(this.#left, dx);
939
+ this.#top = Math.min(this.#top, dy);
940
+ this.#right = Math.max(this.#right, dx);
941
+ this.#bottom = Math.max(this.#bottom, dy);
942
+ }
943
+ }
944
+ get rectangle() {
945
+ if (this.#left === Infinity) return void 0;
946
+ const left = Math.floor(this.#left) - 2, top = Math.floor(this.#top) - 2;
947
+ return { left, top, width: Math.ceil(this.#right) - left + 2, height: Math.ceil(this.#bottom) - top + 2 };
948
+ }
949
+ };
950
+ function drawShadowedContent(target, shadow, bounds, clip, paint) {
951
+ const { device } = bounds;
952
+ const determinant = device.a * device.d - device.b * device.c;
953
+ if (determinant === 0) return;
954
+ const rectangle = bounds.rectangle;
955
+ if (rectangle === void 0) return;
956
+ const { left, top, width, height } = rectangle;
957
+ const { canvas, context: raster } = effectSurfaceFor(target, width, height);
958
+ raster.setTransform(device.a, device.b, device.c, device.d, device.e - left, device.f - top);
959
+ paint(raster);
960
+ target.save();
961
+ try {
962
+ if (clip !== null) {
963
+ target.beginPath();
964
+ target.rect(clip.x, clip.y, clip.width, clip.height);
965
+ target.clip();
966
+ }
967
+ target.transform(device.d / determinant, -device.b / determinant, -device.c / determinant, device.a / determinant, (device.c * device.f - device.d * device.e) / determinant, (device.b * device.e - device.a * device.f) / determinant);
968
+ target.shadowColor = `${shadow.color}${Math.round(shadow.opacity * 255).toString(16).padStart(2, "0")}`;
969
+ target.shadowBlur = shadow.blurRadius * Math.max(Math.hypot(device.a, device.b), Math.hypot(device.c, device.d)) * Math.sqrt(2 / 3);
970
+ target.shadowOffsetX = device.a * shadow.offsetX + device.c * shadow.offsetY;
971
+ target.shadowOffsetY = device.b * shadow.offsetX + device.d * shadow.offsetY;
972
+ target.drawImage(canvas, left, top, width, height);
973
+ } finally {
974
+ target.restore();
975
+ }
976
+ }
977
+ __name(drawShadowedContent, "drawShadowedContent");
978
+
979
+ // packages/canvas/src/canvas-raster-replay.ts
980
+ function drawInlineRasterImage(target, command, resources) {
981
+ if (command.shadow !== void 0) {
982
+ drawShadowedRaster(target, command, resources);
983
+ return;
984
+ }
985
+ const clip = command.clip ?? null;
986
+ const transform = command.transform;
987
+ const opacity = command.opacity;
988
+ const scoped = clip !== null || transform !== void 0 || opacity !== 1 || command.physicalClips !== void 0;
989
+ const quality = target.imageSmoothingQuality;
990
+ if (scoped) target.save();
991
+ try {
992
+ replayPhysicalClips(target, command.physicalClips);
993
+ if (opacity !== 1) target.globalAlpha *= opacity;
994
+ if (clip !== null) {
995
+ target.beginPath();
996
+ target.rect(clip.x, clip.y, clip.width, clip.height);
997
+ target.clip();
998
+ }
999
+ if (transform !== void 0) target.transform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
1000
+ if (quality !== void 0 && usesRasterMinification(target, command)) target.imageSmoothingQuality = "high";
1001
+ if (command.softEdgeRadius === void 0) drawInlineRasterImageUnclipped(target, command, resources);
1002
+ else {
1003
+ const { sampling, ...softened } = command;
1004
+ drawSoftEdgeRaster(target, command.rectangle, command.softEdgeRadius, (raster) => drawInlineRasterImageUnclipped(raster, softened, resources));
1005
+ }
1006
+ } finally {
1007
+ if (quality !== void 0) target.imageSmoothingQuality = quality;
1008
+ if (scoped) target.restore();
1009
+ }
1010
+ }
1011
+ __name(drawInlineRasterImage, "drawInlineRasterImage");
1012
+ function usesRasterMinification(target, command) {
1013
+ const source = command.sampling?.intrinsicPixelSize;
1014
+ const transform = target.getTransform?.();
1015
+ if (source === void 0 || transform === void 0) return false;
1016
+ const crop = command.sourceCrop;
1017
+ const sourceWidth = source.width * (crop === void 0 ? 1 : Number(100000n - crop.left - crop.right) / 1e5);
1018
+ const sourceHeight = source.height * (crop === void 0 ? 1 : Number(100000n - crop.top - crop.bottom) / 1e5);
1019
+ const width = command.rectangle.width * Math.hypot(transform.a, transform.b);
1020
+ const height = command.rectangle.height * Math.hypot(transform.c, transform.d);
1021
+ return width <= sourceWidth && height <= sourceHeight && (width < sourceWidth * 0.6 || height < sourceHeight * 0.6);
1022
+ }
1023
+ __name(usesRasterMinification, "usesRasterMinification");
1024
+ function drawInlineRasterImageUnclipped(target, command, resources) {
1025
+ const colorEffect = command.colorEffect;
1026
+ const sourceCrop = command.sourceCrop;
1027
+ if (sourceCrop !== void 0 && (sourceCrop.left !== 0n || sourceCrop.right !== 0n || sourceCrop.top !== 0n || sourceCrop.bottom !== 0n)) {
1028
+ const crop = croppedRasterRectangleFor(command, resources);
1029
+ if (crop === void 0) return;
1030
+ const previousImageSmoothing2 = target.imageSmoothingEnabled;
1031
+ const useIntrinsicPixelGrid = intrinsicPixelGridMagnification(rasterReplayProjection(target, command), command);
1032
+ if (useIntrinsicPixelGrid) target.imageSmoothingEnabled = false;
1033
+ try {
1034
+ const image = resolveReplayRasterImage(command, resources);
1035
+ if (image === void 0) return;
1036
+ target.drawImage(image, crop.source.x, crop.source.y, crop.source.width, crop.source.height, crop.destination.x, crop.destination.y, crop.destination.width, crop.destination.height);
1037
+ } finally {
1038
+ if (useIntrinsicPixelGrid) target.imageSmoothingEnabled = previousImageSmoothing2;
1039
+ }
1040
+ return;
1041
+ }
1042
+ const projection = rasterReplayProjection(target, command);
1043
+ const intrinsicRectangle = intrinsicPixelGridMagnification(projection, command);
1044
+ if (intrinsicRectangle !== void 0) {
1045
+ const previousImageSmoothing2 = target.imageSmoothingEnabled;
1046
+ target.imageSmoothingEnabled = false;
1047
+ try {
1048
+ const image = resolveReplayRasterImage(command, resources);
1049
+ if (image === void 0) return;
1050
+ target.drawImage(image, intrinsicRectangle.x, intrinsicRectangle.y, intrinsicRectangle.width, intrinsicRectangle.height);
1051
+ } finally {
1052
+ target.imageSmoothingEnabled = previousImageSmoothing2;
1053
+ }
1054
+ return;
1055
+ }
1056
+ const preparedReplay = preparedMixedAxisRasterReplay(projection) ?? preparedFractionalRasterReplay(projection);
1057
+ if (preparedReplay === void 0) {
1058
+ const image = resolveReplayRasterImage(command, resources);
1059
+ if (image === void 0) return;
1060
+ target.drawImage(image, command.rectangle.x, command.rectangle.y, command.rectangle.width, command.rectangle.height);
1061
+ return;
1062
+ }
1063
+ let prepared = colorEffect === void 0 ? void 0 : resources.resolvePreparedRasterImage(command.resource, preparedReplay.devicePixelSize, colorEffect, preparedReplay.flipHorizontal, preparedReplay.flipVertical);
1064
+ if (colorEffect !== void 0 && prepared === void 0) return;
1065
+ const previousImageSmoothing = target.imageSmoothingEnabled;
1066
+ let smoothingChanged = false;
1067
+ target.save();
1068
+ try {
1069
+ if (preparedReplay.clipToRectangle) {
1070
+ target.beginPath();
1071
+ target.rect(command.rectangle.x, command.rectangle.y, command.rectangle.width, command.rectangle.height);
1072
+ target.clip();
1073
+ }
1074
+ if (colorEffect === void 0) prepared = resources.resolvePreparedRasterImage(command.resource, preparedReplay.devicePixelSize, void 0, preparedReplay.flipHorizontal, preparedReplay.flipVertical);
1075
+ if (prepared !== void 0) {
1076
+ target.imageSmoothingEnabled = false;
1077
+ smoothingChanged = true;
1078
+ drawPreparedRasterImage(target, prepared, preparedReplay);
1079
+ }
1080
+ } finally {
1081
+ if (smoothingChanged) target.imageSmoothingEnabled = previousImageSmoothing;
1082
+ target.restore();
1083
+ }
1084
+ if (prepared === void 0) {
1085
+ const image = resources.resolveRasterImage(command.resource);
1086
+ if (image === void 0) return;
1087
+ target.drawImage(image, command.rectangle.x, command.rectangle.y, command.rectangle.width, command.rectangle.height);
1088
+ }
1089
+ }
1090
+ __name(drawInlineRasterImageUnclipped, "drawInlineRasterImageUnclipped");
1091
+ function resolveReplayRasterImage(command, resources) {
1092
+ if (command.colorEffect === void 0) return resources.resolveRasterImage(command.resource);
1093
+ const size = command.sampling?.intrinsicPixelSize ?? resources.resolveRasterImageIntrinsicPixelSize?.(command.resource, command.colorEffect);
1094
+ return size === void 0 ? void 0 : resources.resolvePreparedRasterImage(command.resource, size, command.colorEffect);
1095
+ }
1096
+ __name(resolveReplayRasterImage, "resolveReplayRasterImage");
1097
+ function drawPreparedRasterImage(target, image, replay) {
1098
+ const { rectangle, flipHorizontal, flipVertical } = replay;
1099
+ if (flipHorizontal || flipVertical) target.transform(flipHorizontal ? -1 : 1, 0, 0, flipVertical ? -1 : 1, flipHorizontal ? 2 * rectangle.x + rectangle.width : 0, flipVertical ? 2 * rectangle.y + rectangle.height : 0);
1100
+ target.drawImage(image, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
1101
+ }
1102
+ __name(drawPreparedRasterImage, "drawPreparedRasterImage");
1103
+ function croppedRasterRectangleFor(command, resources) {
1104
+ const crop = command.sourceCrop;
1105
+ if (crop === void 0) return void 0;
1106
+ const size = command.sampling?.intrinsicPixelSize ?? resources.resolveRasterImageIntrinsicPixelSize?.(command.resource, command.colorEffect);
1107
+ if (size === void 0 || !Number.isSafeInteger(size.width) || !Number.isSafeInteger(size.height) || size.width <= 0 || size.height <= 0) return void 0;
1108
+ const horizontal = croppedAxis(crop.left, 100000n - crop.right, size.width, command.rectangle.x, command.rectangle.width);
1109
+ const vertical = croppedAxis(crop.top, 100000n - crop.bottom, size.height, command.rectangle.y, command.rectangle.height);
1110
+ if (horizontal === void 0 || vertical === void 0) return void 0;
1111
+ return {
1112
+ source: { x: horizontal.sourceStart, y: vertical.sourceStart, width: horizontal.sourceLength, height: vertical.sourceLength },
1113
+ destination: { x: horizontal.destinationStart, y: vertical.destinationStart, width: horizontal.destinationLength, height: vertical.destinationLength }
1114
+ };
1115
+ }
1116
+ __name(croppedRasterRectangleFor, "croppedRasterRectangleFor");
1117
+ function croppedAxis(start, end, intrinsicLength, destinationStart, destinationLength) {
1118
+ if (end <= start || !Number.isFinite(destinationStart) || !Number.isFinite(destinationLength) || destinationLength <= 0) return void 0;
1119
+ const clippedStart = start < 0n ? 0n : start;
1120
+ const clippedEnd = end > 100000n ? 100000n : end;
1121
+ if (clippedEnd <= clippedStart) return void 0;
1122
+ const sourceStart = intrinsicLength * boundedRatio(clippedStart, 100000n);
1123
+ const sourceLength = intrinsicLength * boundedRatio(clippedEnd - clippedStart, 100000n);
1124
+ if (!Number.isFinite(sourceStart) || !Number.isFinite(sourceLength) || sourceLength <= 0) return void 0;
1125
+ if (clippedStart === start && clippedEnd === end) return { sourceStart, sourceLength, destinationStart, destinationLength };
1126
+ const requestedLength = end - start;
1127
+ const destinationOffset = destinationLength * boundedRatio(clippedStart - start, requestedLength);
1128
+ const clippedDestinationLength = positiveFiniteProduct(destinationLength, boundedRatio(clippedEnd - clippedStart, requestedLength));
1129
+ const clippedDestinationStart = destinationStart + destinationOffset;
1130
+ if (!Number.isFinite(clippedDestinationStart) || clippedDestinationLength === void 0) return void 0;
1131
+ return { sourceStart, sourceLength, destinationStart: clippedDestinationStart, destinationLength: clippedDestinationLength };
1132
+ }
1133
+ __name(croppedAxis, "croppedAxis");
1134
+ function positiveFiniteProduct(value, ratio) {
1135
+ const product = value * ratio;
1136
+ if (!Number.isFinite(product)) return void 0;
1137
+ return product > 0 ? product : Number.MIN_VALUE;
1138
+ }
1139
+ __name(positiveFiniteProduct, "positiveFiniteProduct");
1140
+ function boundedRatio(numerator, denominator) {
1141
+ if (numerator === 0n) return 0;
1142
+ if (numerator === denominator) return 1;
1143
+ const shift = Math.max(0, denominator.toString(2).length - 53);
1144
+ const scaledNumerator = Number(numerator >> BigInt(shift));
1145
+ const scaledDenominator = Number(denominator >> BigInt(shift));
1146
+ const ratio = scaledNumerator / scaledDenominator;
1147
+ return ratio === 0 ? Number.MIN_VALUE : ratio;
1148
+ }
1149
+ __name(boundedRatio, "boundedRatio");
1150
+ function rasterReplayProjection(target, command) {
1151
+ const sampling = command.sampling;
1152
+ const transform = target.getTransform?.();
1153
+ if (sampling?.type !== "intrinsicPixelGrid" || transform === void 0) return void 0;
1154
+ const swapped = transform.a === 0 && transform.d === 0;
1155
+ if (!swapped && (transform.b !== 0 || transform.c !== 0)) return void 0;
1156
+ const horizontalScale = swapped ? transform.b : transform.a;
1157
+ const verticalScale = swapped ? transform.c : transform.d;
1158
+ const horizontalOffset = swapped ? transform.f : transform.e;
1159
+ const verticalOffset = swapped ? transform.e : transform.f;
1160
+ if (horizontalScale === 0 || verticalScale === 0 || !Number.isFinite(horizontalScale) || !Number.isFinite(verticalScale) || !Number.isFinite(horizontalOffset) || !Number.isFinite(verticalOffset)) return void 0;
1161
+ const source = sampling.intrinsicPixelSize;
1162
+ if (!Number.isSafeInteger(source.width) || !Number.isSafeInteger(source.height) || source.width <= 0 || source.height <= 0) return void 0;
1163
+ const rectangle = command.rectangle;
1164
+ if (!Number.isFinite(rectangle.x) || !Number.isFinite(rectangle.y) || !Number.isFinite(rectangle.width) || !Number.isFinite(rectangle.height) || rectangle.width <= 0 || rectangle.height <= 0) return void 0;
1165
+ return {
1166
+ rectangle,
1167
+ sampling,
1168
+ source,
1169
+ horizontal: { scale: horizontalScale, offset: horizontalOffset, start: rectangle.x * horizontalScale + horizontalOffset, end: (rectangle.x + rectangle.width) * horizontalScale + horizontalOffset },
1170
+ vertical: { scale: verticalScale, offset: verticalOffset, start: rectangle.y * verticalScale + verticalOffset, end: (rectangle.y + rectangle.height) * verticalScale + verticalOffset },
1171
+ swapped
1172
+ };
1173
+ }
1174
+ __name(rasterReplayProjection, "rasterReplayProjection");
1175
+ function preparedMixedAxisRasterReplay(projection) {
1176
+ if (projection === void 0 || projection.swapped) return void 0;
1177
+ const { source, horizontal, vertical } = projection;
1178
+ if (!hasSafeRasterPixelBufferSize(source)) return void 0;
1179
+ const left = horizontal.start, right = horizontal.end, top = vertical.start, bottom = vertical.end;
1180
+ if (!isDeviceInteger(left) || !isDeviceInteger(right) || !isDeviceInteger(bottom - top)) return void 0;
1181
+ const deviceLeft = Math.round(left);
1182
+ const deviceRight = Math.round(right);
1183
+ const deviceWidth = Math.abs(deviceRight - deviceLeft);
1184
+ const deviceHeight = Math.abs(Math.round(bottom - top));
1185
+ if (!hasSafeRasterPixelBufferSize({ width: deviceWidth, height: deviceHeight }) || deviceWidth <= source.width || deviceHeight >= source.height || deviceHeight / source.height < 0.6) return void 0;
1186
+ const preparedPixelSize = { width: deviceWidth, height: source.height };
1187
+ if (!hasSafeRasterPixelBufferSize(preparedPixelSize) || preparedPixelSize.width * preparedPixelSize.height >= source.width * source.height * 16) return void 0;
1188
+ const x = (deviceLeft - horizontal.offset) / horizontal.scale;
1189
+ const y = ((vertical.scale < 0 ? bottom + preparedPixelSize.height : top) - vertical.offset) / vertical.scale;
1190
+ const width = preparedPixelSize.width / Math.abs(horizontal.scale);
1191
+ const height = preparedPixelSize.height / Math.abs(vertical.scale);
1192
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return void 0;
1193
+ return { clipToRectangle: true, devicePixelSize: preparedPixelSize, flipHorizontal: horizontal.scale < 0, flipVertical: vertical.scale < 0, rectangle: { x, y, width, height } };
1194
+ }
1195
+ __name(preparedMixedAxisRasterReplay, "preparedMixedAxisRasterReplay");
1196
+ function intrinsicPixelGridMagnification(projection, command) {
1197
+ if (projection === void 0) return void 0;
1198
+ const { rectangle, source, horizontal, vertical } = projection;
1199
+ const left = horizontal.start, right = horizontal.end, top = vertical.start, bottom = vertical.end;
1200
+ const horizontalScale = rectangle.width * Math.abs(horizontal.scale) / source.width;
1201
+ const verticalScale = rectangle.height * Math.abs(vertical.scale) / source.height;
1202
+ const largeMagnification = horizontalScale >= 1 && verticalScale >= 1 && (horizontalScale >= 4 || verticalScale >= 4);
1203
+ if (!largeMagnification) {
1204
+ if (!isDeviceInteger(horizontalScale) || !isDeviceInteger(verticalScale)) return void 0;
1205
+ const horizontalScaleInteger = Math.round(horizontalScale), verticalScaleInteger = Math.round(verticalScale);
1206
+ if (horizontalScaleInteger < 1 || verticalScaleInteger < 1 || horizontalScaleInteger === 1 && verticalScaleInteger === 1) return void 0;
1207
+ }
1208
+ if (command.sampling?.shapeFill) {
1209
+ const x = Math.round(Math.min(left, right)), y = Math.round(Math.min(top, bottom));
1210
+ const width = Math.round(Math.max(left, right)) - x, height = Math.round(Math.max(top, bottom)) - y;
1211
+ return { x: ((horizontal.scale < 0 ? x + width : x) - horizontal.offset) / horizontal.scale, y: ((vertical.scale < 0 ? y + height : y) - vertical.offset) / vertical.scale, width: width / Math.abs(horizontal.scale), height: height / Math.abs(vertical.scale) };
1212
+ }
1213
+ if (command.sourceCrop !== void 0 || source.width === 1 || source.height === 1) return rectangle;
1214
+ if (isDeviceInteger(left) && isDeviceInteger(right) && isDeviceInteger(top) && isDeviceInteger(bottom)) return rectangle;
1215
+ const deviceLeft = Math.floor(Math.min(left, right)), deviceTop = Math.floor(Math.min(top, bottom));
1216
+ const deviceRight = Math.floor(Math.max(left, right)) + 1, deviceBottom = Math.floor(Math.max(top, bottom)) + 1;
1217
+ return { x: ((horizontal.scale < 0 ? deviceRight : deviceLeft) - horizontal.offset) / horizontal.scale, y: ((vertical.scale < 0 ? deviceBottom : deviceTop) - vertical.offset) / vertical.scale, width: (deviceRight - deviceLeft) / Math.abs(horizontal.scale), height: (deviceBottom - deviceTop) / Math.abs(vertical.scale) };
1218
+ }
1219
+ __name(intrinsicPixelGridMagnification, "intrinsicPixelGridMagnification");
1220
+ function preparedFractionalRasterReplay(projection) {
1221
+ if (projection === void 0 || projection.swapped) return void 0;
1222
+ const { sampling, source, rectangle, horizontal, vertical } = projection;
1223
+ const reduction = rectangle.width * Math.abs(horizontal.scale) < source.width && rectangle.height * Math.abs(vertical.scale) < source.height;
1224
+ const verticalOriginOffset = reduction ? sampling.verticalOriginOffset ?? 0 : 0;
1225
+ const left = horizontal.start;
1226
+ const top = (rectangle.y + verticalOriginOffset) * vertical.scale + vertical.offset;
1227
+ const right = horizontal.end;
1228
+ const bottom = (rectangle.y + verticalOriginOffset + rectangle.height) * vertical.scale + vertical.offset;
1229
+ const deviceLeft = Math.floor(Math.min(left, right));
1230
+ const deviceTop = Math.floor(Math.min(top, bottom));
1231
+ const deviceRight = Math.floor(Math.max(left, right)) + 1;
1232
+ const deviceBottom = Math.floor(Math.max(top, bottom)) + 1;
1233
+ if (!Number.isSafeInteger(deviceLeft) || !Number.isSafeInteger(deviceTop) || !Number.isSafeInteger(deviceRight) || !Number.isSafeInteger(deviceBottom)) return void 0;
1234
+ const deviceWidth = Math.abs(deviceRight - deviceLeft);
1235
+ const deviceHeight = deviceBottom - deviceTop;
1236
+ if (!Number.isSafeInteger(deviceWidth) || !Number.isSafeInteger(deviceHeight)) return void 0;
1237
+ if (reduction ? deviceWidth >= source.width || deviceHeight >= source.height : deviceWidth <= source.width || deviceHeight <= source.height || deviceWidth >= source.width * 4 || deviceHeight >= source.height * 4) return void 0;
1238
+ const horizontalScale = rectangle.width * Math.abs(horizontal.scale) / source.width;
1239
+ const verticalScale = rectangle.height * Math.abs(vertical.scale) / source.height;
1240
+ if (!Number.isFinite(horizontalScale) || !Number.isFinite(verticalScale) || !reduction && (horizontalScale <= 1 || verticalScale <= 1 || isDeviceInteger(horizontalScale) && isDeviceInteger(verticalScale))) return void 0;
1241
+ const x = ((horizontal.scale < 0 ? deviceRight : deviceLeft) - horizontal.offset) / horizontal.scale;
1242
+ const y = ((vertical.scale < 0 ? deviceBottom : deviceTop) - vertical.offset) / vertical.scale;
1243
+ const width = deviceWidth / Math.abs(horizontal.scale);
1244
+ const height = deviceHeight / Math.abs(vertical.scale);
1245
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return void 0;
1246
+ return { clipToRectangle: !reduction || sampling.clipToRectangle !== false, devicePixelSize: { width: deviceWidth, height: deviceHeight }, flipHorizontal: horizontal.scale < 0, flipVertical: vertical.scale < 0, rectangle: { x, y, width, height } };
1247
+ }
1248
+ __name(preparedFractionalRasterReplay, "preparedFractionalRasterReplay");
1249
+ function isDeviceInteger(value) {
1250
+ const rounded = Math.round(value);
1251
+ return Number.isSafeInteger(rounded) && Math.abs(value - rounded) <= Math.max(1, Math.abs(value)) * 5e-8;
1252
+ }
1253
+ __name(isDeviceInteger, "isDeviceInteger");
1254
+ function hasSafeRasterPixelBufferSize(size) {
1255
+ return Number.isSafeInteger(size.width) && Number.isSafeInteger(size.height) && size.width > 0 && size.height > 0 && Number.isSafeInteger(size.width * size.height) && size.width * size.height <= Math.floor(Number.MAX_SAFE_INTEGER / 4);
1256
+ }
1257
+ __name(hasSafeRasterPixelBufferSize, "hasSafeRasterPixelBufferSize");
1258
+ function drawShadowedRaster(target, command, resources) {
1259
+ const { shadow, ...plain } = command;
1260
+ const rectangle = command.rectangle;
1261
+ const bounds = new ShadowBounds(target.getTransform(), command.transform);
1262
+ bounds.includeRectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
1263
+ target.save();
1264
+ try {
1265
+ replayPhysicalClips(target, command.physicalClips);
1266
+ drawShadowedContent(target, shadow, bounds, command.clip, (raster) => drawInlineRasterImage(raster, { ...plain, clip: null, physicalClips: void 0 }, resources));
1267
+ } finally {
1268
+ target.restore();
1269
+ }
1270
+ }
1271
+ __name(drawShadowedRaster, "drawShadowedRaster");
1272
+
1273
+ // packages/canvas/src/canvas-table-border-replay.ts
1274
+ function fillTableBorderRectangle(target, command) {
1275
+ const marker = command.tableBorderDeviceMinimum;
1276
+ if (marker.compound !== void 0) {
1277
+ fillCompoundTableBorderRectangle(target, command);
1278
+ return;
1279
+ }
1280
+ const thickness = Number(marker.authoredThickness.numerator) / Number(marker.authoredThickness.denominator);
1281
+ const startExtension = marker.startJunction === void 0 ? 0 : fractionNumber(marker.startJunction) / 2;
1282
+ const endExtension = marker.endJunction === void 0 ? 0 : fractionNumber(marker.endJunction) / 2;
1283
+ const rectangle = command.rectangle;
1284
+ const x = Number(rectangle.x);
1285
+ const y = Number(rectangle.y);
1286
+ const length = marker.orientation === "horizontal" ? Number(rectangle.width) : Number(rectangle.height);
1287
+ if (!Number.isFinite(thickness) || thickness <= 0 || !Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(length) || length <= 0) {
1288
+ target.fillRect(x, y, marker.orientation === "horizontal" ? length : 0, marker.orientation === "horizontal" ? 0 : length);
1289
+ return;
1290
+ }
1291
+ const transform = positiveAxisAlignedTransform(target);
1292
+ if (transform === void 0) {
1293
+ fillExactTableBorderRectangle(target, marker.orientation, x - startExtension, y, length + startExtension + endExtension, thickness);
1294
+ return;
1295
+ }
1296
+ const scale = marker.orientation === "horizontal" ? transform.d : transform.a;
1297
+ const deviceThickness = thickness * scale;
1298
+ const deviceBoundary = marker.orientation === "horizontal" ? y * transform.d + transform.f : x * transform.a + transform.e;
1299
+ if (!Number.isFinite(deviceThickness) || deviceThickness <= 0 || !Number.isFinite(deviceBoundary)) {
1300
+ fillExactTableBorderRectangle(target, marker.orientation, x - startExtension, y, length + startExtension + endExtension, thickness);
1301
+ return;
1302
+ }
1303
+ const deviceStart = marker.orientation === "horizontal" ? x * transform.a + transform.e : y * transform.d + transform.f;
1304
+ const deviceEnd = marker.orientation === "horizontal" ? (x + length) * transform.a + transform.e : (y + length) * transform.d + transform.f;
1305
+ const start = marker.startJunction === void 0 ? Math.floor(deviceStart) : Math.floor(Math.fround(deviceStart - startExtension * transform.a) + 0.5);
1306
+ const end = marker.endJunction === void 0 ? Math.max(start + 1, Math.floor(deviceEnd) + (marker.endExclusive === true ? 0 : 1)) : Math.max(Math.floor(Math.fround(deviceEnd - endExtension * transform.a) + 0.5) + 1, Math.floor(Math.fround(deviceEnd + endExtension * transform.a) + 0.5));
1307
+ const leading = deviceBoundary - (marker.orientation === "vertical" ? deviceThickness / 2 : 0);
1308
+ const perpendicular = Math.floor(Math.fround(leading) + 0.5);
1309
+ const perpendicularEnd = Math.max(perpendicular + 1, Math.floor(Math.fround(leading + deviceThickness) + 0.5));
1310
+ if (![start, end, perpendicular, perpendicularEnd].every(Number.isSafeInteger)) {
1311
+ fillExactTableBorderRectangle(target, marker.orientation, x - startExtension, y, length + startExtension + endExtension, thickness);
1312
+ return;
1313
+ }
1314
+ const adjusted = marker.orientation === "horizontal" ? rectangleForDeviceBounds(transform, start, perpendicular, end - start, perpendicularEnd - perpendicular) : rectangleForDeviceBounds(transform, perpendicular, start, perpendicularEnd - perpendicular, end - start);
1315
+ if (adjusted === void 0) {
1316
+ fillExactTableBorderRectangle(target, marker.orientation, x - startExtension, y, length + startExtension + endExtension, thickness);
1317
+ return;
1318
+ }
1319
+ target.fillRect(adjusted.x, adjusted.y, adjusted.width, adjusted.height);
1320
+ }
1321
+ __name(fillTableBorderRectangle, "fillTableBorderRectangle");
1322
+ function fillCompoundTableBorderRectangle(target, command) {
1323
+ const marker = command.tableBorderDeviceMinimum;
1324
+ const compound = marker.compound;
1325
+ const rectangle = command.rectangle;
1326
+ const x = Number(rectangle.x);
1327
+ const y = Number(rectangle.y);
1328
+ const length = marker.orientation === "horizontal" ? Number(rectangle.width) : Number(rectangle.height);
1329
+ const outer = fractionNumber(compound.outer);
1330
+ const gap = fractionNumber(compound.gap);
1331
+ const inner = fractionNumber(compound.inner);
1332
+ const thickness = fractionNumber(marker.authoredThickness);
1333
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(length) || length <= 0 || !Number.isFinite(outer) || outer <= 0 || !Number.isFinite(gap) || gap < 0 || !Number.isFinite(inner) || inner < 0 || !Number.isFinite(thickness) || thickness <= 0) {
1334
+ fillExactTableBorderRectangle(target, marker.orientation, x, y, length, thickness);
1335
+ return;
1336
+ }
1337
+ const parts = compound.mirrored ? [inner, gap, outer] : [outer, gap, inner];
1338
+ const stripeIndex = compound.mirrored ? compound.stripe === "outer" ? 2 : 0 : compound.stripe === "outer" ? 0 : 2;
1339
+ const offset = parts.slice(0, stripeIndex).reduce((sum, value) => sum + value, 0);
1340
+ const total = outer + gap + inner;
1341
+ const transform = positiveAxisAlignedTransform(target);
1342
+ if (transform === void 0) {
1343
+ fillExactCompoundTableBorderRectangle(target, marker.orientation, x, y, length, total, offset, thickness);
1344
+ return;
1345
+ }
1346
+ const scale = marker.orientation === "horizontal" ? transform.d : transform.a;
1347
+ const deviceParts = parts.map((value) => value * scale);
1348
+ const authoredDeviceThickness = deviceParts[stripeIndex];
1349
+ const deviceOffset = deviceParts.slice(0, stripeIndex).reduce((sum, value) => sum + value, 0);
1350
+ const deviceTotal = deviceParts.reduce((sum, value) => sum + value, 0);
1351
+ const deviceBoundary = marker.orientation === "horizontal" ? y * transform.d + transform.f : x * transform.a + transform.e;
1352
+ const deviceStart = marker.orientation === "horizontal" ? x * transform.a + transform.e : y * transform.d + transform.f;
1353
+ const deviceEnd = marker.orientation === "horizontal" ? (x + length) * transform.a + transform.e : (y + length) * transform.d + transform.f;
1354
+ const parallelStart = Math.floor(deviceStart);
1355
+ const parallelEnd = Math.floor(deviceEnd) + 1;
1356
+ const stripeStart = deviceBoundary + deviceOffset - (marker.orientation === "vertical" ? deviceTotal / 2 : 0);
1357
+ const perpendicularStart = Math.floor(Math.fround(stripeStart) + 0.5);
1358
+ const deviceThickness = Math.max(1, Math.floor(Math.fround(stripeStart + authoredDeviceThickness) + 0.5) - perpendicularStart);
1359
+ if (!Number.isFinite(deviceThickness) || deviceThickness <= 0 || !Number.isFinite(perpendicularStart) || !Number.isSafeInteger(parallelStart) || !Number.isSafeInteger(parallelEnd) || parallelEnd <= parallelStart) {
1360
+ fillExactCompoundTableBorderRectangle(target, marker.orientation, x, y, length, total, offset, thickness);
1361
+ return;
1362
+ }
1363
+ const adjusted = marker.orientation === "horizontal" ? rectangleForDeviceBounds(transform, parallelStart, perpendicularStart, parallelEnd - parallelStart, deviceThickness) : rectangleForDeviceBounds(transform, perpendicularStart, parallelStart, deviceThickness, parallelEnd - parallelStart);
1364
+ if (adjusted === void 0) {
1365
+ fillExactCompoundTableBorderRectangle(target, marker.orientation, x, y, length, total, offset, thickness);
1366
+ return;
1367
+ }
1368
+ target.fillRect(adjusted.x, adjusted.y, adjusted.width, adjusted.height);
1369
+ }
1370
+ __name(fillCompoundTableBorderRectangle, "fillCompoundTableBorderRectangle");
1371
+ function fillExactCompoundTableBorderRectangle(target, orientation, x, y, length, total, offset, thickness) {
1372
+ if (orientation === "horizontal") target.fillRect(x, y + offset, length, thickness);
1373
+ else target.fillRect(x - total / 2 + offset, y, thickness, length);
1374
+ }
1375
+ __name(fillExactCompoundTableBorderRectangle, "fillExactCompoundTableBorderRectangle");
1376
+ function fractionNumber(value) {
1377
+ return Number(value.numerator) / Number(value.denominator);
1378
+ }
1379
+ __name(fractionNumber, "fractionNumber");
1380
+ function fillExactTableBorderRectangle(target, orientation, x, y, length, thickness) {
1381
+ if (orientation === "horizontal") target.fillRect(x, y, length, thickness);
1382
+ else target.fillRect(x - thickness / 2, y, thickness, length);
1383
+ }
1384
+ __name(fillExactTableBorderRectangle, "fillExactTableBorderRectangle");
1385
+
1386
+ // packages/canvas/src/canvas-vector-shape-replay.ts
1387
+ function drawVectorShape(target, command) {
1388
+ if (command.shadow !== void 0) {
1389
+ drawShadowedVectorShape(target, command);
1390
+ return;
1391
+ }
1392
+ target.save();
1393
+ try {
1394
+ if (command.clip !== null) {
1395
+ target.beginPath();
1396
+ target.rect(command.clip.x, command.clip.y, command.clip.width, command.clip.height);
1397
+ target.clip();
1398
+ }
1399
+ if (command.transform !== void 0) target.transform(command.transform.a, command.transform.b, command.transform.c, command.transform.d, command.transform.e, command.transform.f);
1400
+ for (const record of command.paths) {
1401
+ target.beginPath();
1402
+ for (const operation of record.operations) replayVectorShapePathOperation(target, operation);
1403
+ const pathFill = record.fill;
1404
+ if (pathFill.kind === "solidSrgb") {
1405
+ target.fillStyle = pathFill.color;
1406
+ const alpha = target.globalAlpha;
1407
+ target.globalAlpha = alpha * (pathFill.opacity ?? 1);
1408
+ target.fill(record.fillRule);
1409
+ target.globalAlpha = alpha;
1410
+ } else if (pathFill.kind === "linearGradient" || pathFill.kind === "radialGradient") {
1411
+ const gradient = pathFill.kind === "radialGradient" ? target.createRadialGradient(pathFill.center.x, pathFill.center.y, 0, pathFill.center.x, pathFill.center.y, pathFill.radius) : target.createLinearGradient(pathFill.start.x, pathFill.start.y, pathFill.end.x, pathFill.end.y);
1412
+ for (const stop of pathFill.stops) gradient.addColorStop(stop.offset, `${stop.color}${Math.round(stop.opacity * 255).toString(16).padStart(2, "0")}`);
1413
+ target.fillStyle = gradient;
1414
+ target.fill(record.fillRule);
1415
+ } else if (pathFill.kind === "gradientPolygons") {
1416
+ target.save();
1417
+ target.clip(record.fillRule);
1418
+ for (const polygon of pathFill.polygons) {
1419
+ const gradient = target.createLinearGradient(polygon.start.x, polygon.start.y, polygon.end.x, polygon.end.y);
1420
+ for (const stop of polygon.stops) gradient.addColorStop(stop.offset, `${stop.color}${Math.round(stop.opacity * 255).toString(16).padStart(2, "0")}`);
1421
+ target.fillStyle = gradient;
1422
+ target.beginPath();
1423
+ target.moveTo(polygon.points[0].x, polygon.points[0].y);
1424
+ for (const point of polygon.points.slice(1)) target.lineTo(point.x, point.y);
1425
+ target.closePath();
1426
+ target.fill();
1427
+ }
1428
+ target.restore();
1429
+ target.beginPath();
1430
+ for (const operation of record.operations) replayVectorShapePathOperation(target, operation);
1431
+ }
1432
+ if (record.stroke && command.stroke !== void 0) {
1433
+ target.strokeStyle = command.stroke.color;
1434
+ target.lineWidth = command.stroke.deviceHairline === true ? deviceHairlineWidth(target) : command.stroke.width;
1435
+ target.lineCap = command.stroke.cap;
1436
+ target.lineJoin = command.stroke.join;
1437
+ const alpha = target.globalAlpha;
1438
+ target.globalAlpha = alpha * (command.stroke.opacity ?? 1);
1439
+ target.stroke();
1440
+ target.globalAlpha = alpha;
1441
+ }
1442
+ }
1443
+ } finally {
1444
+ target.restore();
1445
+ }
1446
+ }
1447
+ __name(drawVectorShape, "drawVectorShape");
1448
+ function replayVectorShapePathOperation(target, operation) {
1449
+ if (operation.kind === "moveTo") target.moveTo(operation.x, operation.y);
1450
+ else if (operation.kind === "lineTo") target.lineTo(operation.x, operation.y);
1451
+ else if (operation.kind === "bezierCurveTo") target.bezierCurveTo(operation.control1X, operation.control1Y, operation.control2X, operation.control2Y, operation.x, operation.y);
1452
+ else target.closePath();
1453
+ }
1454
+ __name(replayVectorShapePathOperation, "replayVectorShapePathOperation");
1455
+ function deviceHairlineWidth(target) {
1456
+ const transform = target.getTransform?.();
1457
+ if (transform === void 0) return 1;
1458
+ const scale = Math.max(Math.hypot(transform.a, transform.b), Math.hypot(transform.c, transform.d));
1459
+ const width = 1 / scale;
1460
+ return Number.isFinite(width) && width > 0 ? width : 1;
1461
+ }
1462
+ __name(deviceHairlineWidth, "deviceHairlineWidth");
1463
+ function drawShadowedVectorShape(target, command) {
1464
+ const { shadow, ...plain } = command;
1465
+ const shadowBounds = new ShadowBounds(target.getTransform(), command.transform);
1466
+ for (const path of command.paths) {
1467
+ const bounds = pathBounds(path.operations);
1468
+ if (bounds === void 0) continue;
1469
+ const stroke = path.stroke ? (command.stroke?.width ?? 0) / 2 : 0;
1470
+ shadowBounds.includeRectangle(bounds.left, bounds.top, bounds.width, bounds.height, stroke);
1471
+ }
1472
+ drawShadowedContent(target, shadow, shadowBounds, command.clip, (raster) => drawVectorShape(raster, { ...plain, clip: null }));
1473
+ }
1474
+ __name(drawShadowedVectorShape, "drawShadowedVectorShape");
1475
+
1476
+ // packages/canvas/src/canvas-materializer.ts
1477
+ function materializePageDisplayListToCanvas(displayList, target, resources) {
1478
+ for (const command of displayList.commands) {
1479
+ switch (command.kind) {
1480
+ case "fillRectangle":
1481
+ target.fillStyle = command.color;
1482
+ fillRectangle(target, command);
1483
+ break;
1484
+ case "fillPolygon":
1485
+ if (command.clip !== void 0) target.save();
1486
+ try {
1487
+ if (command.clip !== void 0) {
1488
+ target.beginPath();
1489
+ target.rect(Number(command.clip.x), Number(command.clip.y), Number(command.clip.width), Number(command.clip.height));
1490
+ target.clip();
1491
+ }
1492
+ target.fillStyle = command.color;
1493
+ target.beginPath();
1494
+ target.moveTo(Number(command.points[0].x), Number(command.points[0].y));
1495
+ for (const point of command.points.slice(1)) target.lineTo(Number(point.x), Number(point.y));
1496
+ target.closePath();
1497
+ target.fill();
1498
+ } finally {
1499
+ if (command.clip !== void 0) target.restore();
1500
+ }
1501
+ break;
1502
+ case "positionedText":
1503
+ drawPositionedText(target, command);
1504
+ break;
1505
+ case "inlineRasterImage":
1506
+ if (resources === void 0) throw new TypeError("Inline raster-image Canvas materialization requires an explicit resource materializer.");
1507
+ drawInlineRasterImage(target, command, resources);
1508
+ break;
1509
+ case "vectorShape":
1510
+ drawVectorShape(target, command);
1511
+ break;
1512
+ case "commentAnnotation":
1513
+ for (const part of command.indicatorParts) {
1514
+ target.fillStyle = part.color;
1515
+ target.fillRect(part.rectangle.x, part.rectangle.y, part.rectangle.width, part.rectangle.height);
1516
+ }
1517
+ break;
1518
+ }
1519
+ }
1520
+ }
1521
+ __name(materializePageDisplayListToCanvas, "materializePageDisplayListToCanvas");
1522
+ function fillRectangle(target, command) {
1523
+ const rectangle = command.rectangle;
1524
+ if (command.tableBorderDeviceMinimum !== void 0) {
1525
+ fillTableBorderRectangle(target, command);
1526
+ return;
1527
+ }
1528
+ if (command.sectionColumnSeparatorDeviceMinimum !== void 0) {
1529
+ fillSectionColumnSeparatorRectangle(target, command);
1530
+ return;
1531
+ }
1532
+ if (command.targetPixelAligned !== true) {
1533
+ target.fillRect(Number(rectangle.x), Number(rectangle.y), Number(rectangle.width), Number(rectangle.height));
1534
+ return;
1535
+ }
1536
+ fillTargetPixelAlignedRectangle(target, { x: Number(rectangle.x), y: Number(rectangle.y), width: Number(rectangle.width), height: Number(rectangle.height) });
1537
+ }
1538
+ __name(fillRectangle, "fillRectangle");
1539
+ function fillSectionColumnSeparatorRectangle(target, command) {
1540
+ const thickness = Number(command.sectionColumnSeparatorDeviceMinimum.authoredThickness.numerator) / Number(command.sectionColumnSeparatorDeviceMinimum.authoredThickness.denominator);
1541
+ const rectangle = command.rectangle;
1542
+ const x = Number(rectangle.x);
1543
+ const y = Number(rectangle.y);
1544
+ const height = Number(rectangle.height);
1545
+ if (!Number.isFinite(thickness) || thickness <= 0 || !Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(height) || height <= 0) {
1546
+ target.fillRect(x, y, 0, height);
1547
+ return;
1548
+ }
1549
+ const transform = positiveAxisAlignedTransform(target);
1550
+ if (transform === void 0) {
1551
+ fillExactSectionColumnSeparator(target, x, y, height, thickness);
1552
+ return;
1553
+ }
1554
+ const deviceThickness = thickness * transform.a;
1555
+ const deviceBoundary = x * transform.a + transform.e;
1556
+ if (!Number.isFinite(deviceThickness) || deviceThickness <= 0 || !Number.isFinite(deviceBoundary)) {
1557
+ fillExactSectionColumnSeparator(target, x, y, height, thickness);
1558
+ return;
1559
+ }
1560
+ if (deviceThickness > 1) {
1561
+ fillExactSectionColumnSeparator(target, x, y, height, thickness);
1562
+ return;
1563
+ }
1564
+ const deviceStart = roundHalfAwayFromZero(deviceBoundary - 0.5);
1565
+ const adjustedX = (deviceStart - transform.e) / transform.a;
1566
+ const adjustedWidth = 1 / transform.a;
1567
+ if (!Number.isFinite(adjustedX) || !Number.isFinite(adjustedWidth) || adjustedWidth <= 0) {
1568
+ fillExactSectionColumnSeparator(target, x, y, height, thickness);
1569
+ return;
1570
+ }
1571
+ target.fillRect(adjustedX, y, adjustedWidth, height);
1572
+ }
1573
+ __name(fillSectionColumnSeparatorRectangle, "fillSectionColumnSeparatorRectangle");
1574
+ function fillExactSectionColumnSeparator(target, x, y, height, thickness) {
1575
+ target.fillRect(x - thickness / 2, y, thickness, height);
1576
+ }
1577
+ __name(fillExactSectionColumnSeparator, "fillExactSectionColumnSeparator");
1578
+
1579
+ // packages/canvas/src/text-selection.ts
1580
+ var nextView = 0;
1581
+ function mountTextSelection(pageText, viewport, shells, pageBounds, nearby, signal) {
1582
+ const document2 = viewport.ownerDocument;
1583
+ const window = document2.defaultView;
1584
+ const svg = "http://www.w3.org/2000/svg";
1585
+ const viewId = nextView++;
1586
+ const matrix = /* @__PURE__ */ __name((value) => value ? new window.DOMMatrix([value.a, value.b, value.c, value.d, value.e, value.f]) : new window.DOMMatrix(), "matrix");
1587
+ const lines = [...pageText].sort((a, b) => {
1588
+ const left = a.selection, right = b.selection;
1589
+ const leftOrder = left.sourceOrder ?? 0n, rightOrder = right.sourceOrder ?? 0n;
1590
+ return leftOrder < rightOrder ? -1 : leftOrder > rightOrder ? 1 : a.pageIndex - b.pageIndex || left.scalarStart - right.scalarStart;
1591
+ });
1592
+ const pages = shells.map(() => []);
1593
+ const stops = [];
1594
+ const chunks = [];
1595
+ let length = 0;
1596
+ let previous;
1597
+ for (const item of lines) {
1598
+ const value = item.selection;
1599
+ if (previous && previous.paragraphId !== value.paragraphId) {
1600
+ chunks.push("\n");
1601
+ length++;
1602
+ }
1603
+ const offsets = [0];
1604
+ for (const scalar of value.scalars) offsets.push(offsets.at(-1) + scalar.length);
1605
+ const transform = matrix(item.transform).multiply(matrix(value.transform));
1606
+ const line = { item, transform, start: length, end: length + offsets.at(-1), stops: [], index: pages[item.pageIndex].length };
1607
+ for (const caret of value.carets) {
1608
+ const offset = offsets[caret.scalarOffset - value.scalarStart];
1609
+ if (offset === void 0) continue;
1610
+ const stop = { line, offset: length + offset, x: caret.x, index: stops.length };
1611
+ line.stops.push(stop);
1612
+ stops.push(stop);
1613
+ }
1614
+ line.stops.sort((a, b) => a.x - b.x || a.offset - b.offset);
1615
+ pages[item.pageIndex].push(line);
1616
+ chunks.push(value.scalars.join(""));
1617
+ length = line.end;
1618
+ previous = value;
1619
+ }
1620
+ const text = chunks.join("");
1621
+ const logical = [...stops].sort((a, b) => a.offset - b.offset || a.index - b.index);
1622
+ const logicalIndex = new Map(logical.map((stop, index) => [stop, index]));
1623
+ const layers = /* @__PURE__ */ new Map();
1624
+ function layerFor(index) {
1625
+ const existing = layers.get(index);
1626
+ if (existing) return existing;
1627
+ const layer = document2.createElementNS("http://www.w3.org/2000/svg", "svg");
1628
+ const bounds = pageBounds[index];
1629
+ layer.setAttribute("viewBox", `0 0 ${bounds.width} ${bounds.height}`);
1630
+ layer.setAttribute("aria-hidden", "true");
1631
+ Object.assign(layer.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
1632
+ layers.set(index, layer);
1633
+ return layer;
1634
+ }
1635
+ __name(layerFor, "layerFor");
1636
+ let anchor, focus, pointer, preferredX;
1637
+ let frame = 0;
1638
+ let pointerPosition = { x: 0, y: 0 };
1639
+ let menu;
1640
+ viewport.tabIndex = 0;
1641
+ viewport.style.userSelect = "none";
1642
+ for (const shell of shells) shell.style.cursor = "text";
1643
+ function paint() {
1644
+ for (const [index, layer] of layers) {
1645
+ layer.remove();
1646
+ layer.replaceChildren();
1647
+ if (!nearby.has(index)) layers.delete(index);
1648
+ }
1649
+ if (!anchor || !focus || document2.activeElement !== viewport && !menu?.contains(document2.activeElement)) return;
1650
+ const start = Math.min(anchor.offset, focus.offset), end = Math.max(anchor.offset, focus.offset);
1651
+ for (const index of nearby) for (const line of pages[index] ?? []) {
1652
+ let rectangle2 = function(x, width, caret = false) {
1653
+ const rect = document2.createElementNS(svg, "rect");
1654
+ rect.setAttribute("x", String(x));
1655
+ rect.setAttribute("y", String(value.top));
1656
+ rect.setAttribute("width", String(width));
1657
+ rect.setAttribute("height", String(value.height));
1658
+ rect.setAttribute("fill", caret ? "#1769c2" : "#4285f4");
1659
+ rect.setAttribute("fill-opacity", caret ? "1" : "0.28");
1660
+ group.append(rect);
1661
+ };
1662
+ var rectangle = rectangle2;
1663
+ __name(rectangle2, "rectangle");
1664
+ if (line.end < start || line.start > end) continue;
1665
+ const value = line.item.selection;
1666
+ const layer = layerFor(line.item.pageIndex);
1667
+ const group = document2.createElementNS(svg, "g");
1668
+ const transform = line.transform;
1669
+ if (transform) group.setAttribute("transform", `matrix(${transform.a} ${transform.b} ${transform.c} ${transform.d} ${transform.e} ${transform.f})`);
1670
+ if (start === end && focus.line === line) rectangle2(focus.x, Number(pageBounds[line.item.pageIndex].width) / shells[line.item.pageIndex].getBoundingClientRect().width * 1.5, true);
1671
+ else {
1672
+ let left, right = 0;
1673
+ for (let index2 = 1; index2 < line.stops.length; index2++) {
1674
+ const a = line.stops[index2 - 1], b = line.stops[index2];
1675
+ if (Math.min(a.offset, b.offset) >= start && Math.max(a.offset, b.offset) <= end && a.offset !== b.offset) {
1676
+ left ??= a.x;
1677
+ right = b.x;
1678
+ } else if (left !== void 0) {
1679
+ rectangle2(left, right - left);
1680
+ left = void 0;
1681
+ }
1682
+ }
1683
+ if (left !== void 0) rectangle2(left, right - left);
1684
+ }
1685
+ let clipped = group;
1686
+ const clips = [...line.item.clip ? [{ rectangle: line.item.clip, transform: line.item.transform }] : [], ...line.item.physicalClips ?? []];
1687
+ for (let index2 = 0; index2 < clips.length; index2++) {
1688
+ const clip = clips[index2], rectangle3 = clip.rectangle, transform2 = matrix(clip.transform);
1689
+ const path = document2.createElementNS(svg, "clipPath");
1690
+ path.id = `selection-${viewId}-${line.item.pageIndex}-${line.index}-${index2}`;
1691
+ const polygon = document2.createElementNS(svg, "polygon");
1692
+ polygon.setAttribute("points", [[rectangle3.x, rectangle3.y], [rectangle3.x + rectangle3.width, rectangle3.y], [rectangle3.x + rectangle3.width, rectangle3.y + rectangle3.height], [rectangle3.x, rectangle3.y + rectangle3.height]].map(([x, y]) => {
1693
+ const point = transform2.transformPoint({ x, y });
1694
+ return `${point.x},${point.y}`;
1695
+ }).join(" "));
1696
+ path.append(polygon);
1697
+ const wrapper = document2.createElementNS(svg, "g");
1698
+ wrapper.setAttribute("clip-path", `url(#${path.id})`);
1699
+ wrapper.append(clipped);
1700
+ layer.append(path);
1701
+ clipped = wrapper;
1702
+ }
1703
+ layer.append(clipped);
1704
+ shells[line.item.pageIndex].append(layer);
1705
+ }
1706
+ }
1707
+ __name(paint, "paint");
1708
+ function nearest(clientX, clientY) {
1709
+ if (!logical.length) return;
1710
+ let result, best = Infinity;
1711
+ let first = 0, last = shells.length - 1;
1712
+ while (first < last) {
1713
+ const middle = first + last >>> 1;
1714
+ if (shells[middle].getBoundingClientRect().bottom < clientY) first = middle + 1;
1715
+ else last = middle;
1716
+ }
1717
+ let pageIndex = first;
1718
+ if (!pages[first]?.length) pageIndex = pages.findIndex((lines2, index) => index >= first && lines2.length > 0);
1719
+ if (pageIndex === -1) pageIndex = logical.at(-1).line.item.pageIndex;
1720
+ {
1721
+ const index = pageIndex;
1722
+ const box = shells[index].getBoundingClientRect();
1723
+ const width = Number(pageBounds[index].width), height = Number(pageBounds[index].height);
1724
+ const px = (clientX - box.left) * width / box.width, py = (clientY - box.top) * height / box.height;
1725
+ for (const line of pages[index]) {
1726
+ const transform = line.transform;
1727
+ const determinant = transform ? transform.a * transform.d - transform.b * transform.c : 1;
1728
+ if (!determinant) continue;
1729
+ const x = transform ? (transform.d * (px - transform.e) - transform.c * (py - transform.f)) / determinant : px;
1730
+ const y = transform ? (-transform.b * (px - transform.e) + transform.a * (py - transform.f)) / determinant : py;
1731
+ const value = line.item.selection;
1732
+ const dy = Math.max(value.top - y, 0, y - value.top - value.height) * box.height / height;
1733
+ for (const stop of line.stops) {
1734
+ const dx = Math.abs(stop.x - x) * box.width / width;
1735
+ const distance = dy * dy * 16 + dx * dx;
1736
+ if (distance < best) {
1737
+ best = distance;
1738
+ result = stop;
1739
+ }
1740
+ }
1741
+ }
1742
+ }
1743
+ return result;
1744
+ }
1745
+ __name(nearest, "nearest");
1746
+ function select(stop, extend, scroll = false) {
1747
+ if (!stop) return;
1748
+ focus = stop;
1749
+ if (!extend || !anchor) anchor = stop;
1750
+ paint();
1751
+ if (scroll) {
1752
+ const shell = shells[stop.line.item.pageIndex];
1753
+ const point = stop.line.transform.transformPoint({ x: stop.x, y: stop.line.item.selection.top });
1754
+ const marker = document2.createElement("span");
1755
+ Object.assign(marker.style, { position: "absolute", left: `${point.x / pageBounds[stop.line.item.pageIndex].width * 100}%`, top: `${point.y / pageBounds[stop.line.item.pageIndex].height * 100}%`, height: "1em", pointerEvents: "none" });
1756
+ shell.append(marker);
1757
+ marker.scrollIntoView({ block: "nearest", inline: "nearest" });
1758
+ marker.remove();
1759
+ }
1760
+ }
1761
+ __name(select, "select");
1762
+ let scroller = viewport.parentElement;
1763
+ while (scroller && !/(auto|scroll)/.test(window.getComputedStyle(scroller).overflowY)) scroller = scroller.parentElement;
1764
+ const scrollElement = scroller ?? document2.scrollingElement;
1765
+ function drag() {
1766
+ if (pointer === void 0) return;
1767
+ const bounds = scroller?.getBoundingClientRect() ?? { top: 0, bottom: window.innerHeight };
1768
+ scrollElement.scrollTop += Math.min(30, Math.max(-30, pointerPosition.y < bounds.top + 20 ? pointerPosition.y - bounds.top - 20 : pointerPosition.y > bounds.bottom - 20 ? pointerPosition.y - bounds.bottom + 20 : 0));
1769
+ select(nearest(pointerPosition.x, pointerPosition.y), true);
1770
+ frame = window.requestAnimationFrame(drag);
1771
+ }
1772
+ __name(drag, "drag");
1773
+ viewport.addEventListener("pointerdown", (event) => {
1774
+ if (event.button !== 0 || event.target.closest("button,input,textarea,select,a,[contenteditable], [data-onodocs-attachment]")) return;
1775
+ const stop = nearest(event.clientX, event.clientY);
1776
+ if (!stop) return;
1777
+ event.preventDefault();
1778
+ viewport.focus({ preventScroll: true });
1779
+ preferredX = void 0;
1780
+ select(stop, event.shiftKey);
1781
+ pointer = event.pointerId;
1782
+ pointerPosition = { x: event.clientX, y: event.clientY };
1783
+ viewport.setPointerCapture(pointer);
1784
+ frame = window.requestAnimationFrame(drag);
1785
+ }, { signal });
1786
+ viewport.addEventListener("pointermove", (event) => {
1787
+ if (event.pointerId !== pointer) return;
1788
+ pointerPosition = { x: event.clientX, y: event.clientY };
1789
+ select(nearest(event.clientX, event.clientY), true);
1790
+ }, { signal });
1791
+ for (const type of ["pointerup", "pointercancel", "lostpointercapture"]) viewport.addEventListener(type, (event) => {
1792
+ if (event.pointerId !== pointer) return;
1793
+ pointer = void 0;
1794
+ window.cancelAnimationFrame(frame);
1795
+ }, { signal });
1796
+ viewport.addEventListener("keydown", (event) => {
1797
+ if (event.target !== viewport || !logical.length) return;
1798
+ const command = event.ctrlKey || event.metaKey;
1799
+ if (command && event.key.toLowerCase() === "a") {
1800
+ event.preventDefault();
1801
+ anchor = logical[0];
1802
+ select(logical.at(-1), true);
1803
+ return;
1804
+ }
1805
+ if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key) || event.altKey) return;
1806
+ event.preventDefault();
1807
+ if (!focus) {
1808
+ select(logical[0], false);
1809
+ return;
1810
+ }
1811
+ let next;
1812
+ if (event.key === "Home" || event.key === "End") next = command ? event.key === "Home" ? logical[0] : logical.at(-1) : event.key === "Home" ? focus.line.stops[0] : focus.line.stops.at(-1);
1813
+ else if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
1814
+ const delta = event.key === "ArrowLeft" ? -1 : 1;
1815
+ if (!event.shiftKey && anchor.offset !== focus.offset) next = delta < 0 ? anchor.offset < focus.offset ? anchor : focus : anchor.offset > focus.offset ? anchor : focus;
1816
+ else {
1817
+ let index = logicalIndex.get(focus) + delta;
1818
+ while (logical[index]?.offset === focus.offset) index += delta;
1819
+ next = logical[Math.max(0, Math.min(logical.length - 1, index))];
1820
+ }
1821
+ } else {
1822
+ preferredX ??= focus.x;
1823
+ const delta = event.key === "ArrowUp" ? -1 : 1;
1824
+ let index = logicalIndex.get(focus) + delta;
1825
+ while (logical[index]?.line === focus.line) index += delta;
1826
+ const line = logical[index]?.line;
1827
+ next = line?.stops.reduce((best, stop) => Math.abs(stop.x - preferredX) < Math.abs(best.x - preferredX) ? stop : best);
1828
+ }
1829
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") preferredX = void 0;
1830
+ select(next, event.shiftKey, true);
1831
+ }, { signal });
1832
+ document2.addEventListener("copy", (event) => {
1833
+ if (document2.activeElement !== viewport && !menu?.contains(document2.activeElement) || !focus || anchor.offset === focus.offset) return;
1834
+ event.clipboardData?.setData("text/plain", selectedText());
1835
+ event.preventDefault();
1836
+ }, { signal });
1837
+ function selectedText() {
1838
+ return focus ? text.slice(Math.min(anchor.offset, focus.offset), Math.max(anchor.offset, focus.offset)) : "";
1839
+ }
1840
+ __name(selectedText, "selectedText");
1841
+ function closeMenu(restoreFocus = false) {
1842
+ if (!menu) return;
1843
+ menu.remove();
1844
+ menu = void 0;
1845
+ if (restoreFocus) viewport.focus({ preventScroll: true });
1846
+ paint();
1847
+ }
1848
+ __name(closeMenu, "closeMenu");
1849
+ viewport.addEventListener("contextmenu", (event) => {
1850
+ if (event.target.closest("button,input,textarea,select,a,[contenteditable], [data-onodocs-attachment]")) return;
1851
+ event.preventDefault();
1852
+ closeMenu();
1853
+ viewport.focus({ preventScroll: true });
1854
+ menu = document2.createElement("div");
1855
+ menu.setAttribute("role", "menu");
1856
+ menu.setAttribute("aria-label", "Document");
1857
+ menu.setAttribute("popover", "manual");
1858
+ Object.assign(menu.style, { position: "fixed", margin: "0", padding: "4px", border: "1px solid #d0d0d0", borderRadius: "6px", background: "white", boxShadow: "0 4px 16px #0002", minWidth: "140px" });
1859
+ const copy = document2.createElement("button");
1860
+ copy.type = "button";
1861
+ copy.textContent = "Copy";
1862
+ copy.setAttribute("role", "menuitem");
1863
+ copy.setAttribute("aria-disabled", String(!selectedText()));
1864
+ Object.assign(copy.style, { display: "block", width: "100%", margin: "0", padding: "6px 12px", border: "0", borderRadius: "3px", textAlign: "left", font: "14px system-ui", color: selectedText() ? "#222" : "#888", background: "transparent", cursor: selectedText() ? "pointer" : "default" });
1865
+ copy.addEventListener("click", async () => {
1866
+ const value = selectedText();
1867
+ if (!value) return;
1868
+ const opened = menu;
1869
+ viewport.focus({ preventScroll: true });
1870
+ if (!document2.execCommand("copy")) {
1871
+ try {
1872
+ await window.navigator.clipboard.writeText(value);
1873
+ } catch {
1874
+ if (menu === opened && !signal.aborted) {
1875
+ copy.title = "Unable to copy. Use your keyboard copy shortcut.";
1876
+ copy.focus();
1877
+ }
1878
+ return;
1879
+ }
1880
+ }
1881
+ if (menu === opened) closeMenu(true);
1882
+ });
1883
+ menu.addEventListener("keydown", (event2) => {
1884
+ if (event2.key === "Escape" || event2.key === "Tab") {
1885
+ event2.preventDefault();
1886
+ closeMenu(true);
1887
+ } else if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event2.key)) {
1888
+ event2.preventDefault();
1889
+ copy.focus();
1890
+ }
1891
+ });
1892
+ menu.append(copy);
1893
+ document2.body.append(menu);
1894
+ menu.showPopover();
1895
+ const caret = (focus ? layers.get(focus.line.item.pageIndex) : void 0)?.querySelector("rect")?.getBoundingClientRect() ?? viewport.getBoundingClientRect();
1896
+ const x = event.clientX || caret.left, y = event.clientY || caret.bottom;
1897
+ const bounds = menu.getBoundingClientRect();
1898
+ menu.style.left = `${Math.max(0, Math.min(x, window.innerWidth - bounds.width))}px`;
1899
+ menu.style.top = `${Math.max(0, Math.min(y, window.innerHeight - bounds.height))}px`;
1900
+ copy.focus({ preventScroll: true });
1901
+ paint();
1902
+ }, { signal });
1903
+ document2.addEventListener("pointerdown", (event) => {
1904
+ if (menu && !menu.contains(event.target)) closeMenu();
1905
+ }, { signal });
1906
+ document2.addEventListener("focusin", (event) => {
1907
+ if (menu && event.target !== viewport && !menu.contains(event.target)) closeMenu();
1908
+ }, { signal });
1909
+ document2.addEventListener("scroll", () => closeMenu(), { capture: true, signal });
1910
+ window.addEventListener("resize", () => closeMenu(), { signal });
1911
+ viewport.addEventListener("focus", () => {
1912
+ if (!focus) select(logical[0], false);
1913
+ else paint();
1914
+ }, { signal });
1915
+ viewport.addEventListener("blur", paint, { signal });
1916
+ signal.addEventListener("abort", () => {
1917
+ menu?.remove();
1918
+ window.cancelAnimationFrame(frame);
1919
+ for (const layer of layers.values()) layer.remove();
1920
+ }, { once: true });
1921
+ return { paint };
1922
+ }
1923
+ __name(mountTextSelection, "mountTextSelection");
1924
+
1925
+ // packages/canvas/src/presentation.ts
1926
+ var cssScale = 96 / 1440;
1927
+ var attachedElements = /* @__PURE__ */ new WeakMap();
1928
+ var placementPoints = {
1929
+ overlay: [0, 0, 0, 0],
1930
+ "inside-top-left": [0, 0, 0, 0],
1931
+ "inside-top-center": [0.5, 0, 0.5, 0],
1932
+ "inside-top-right": [1, 0, 1, 0],
1933
+ "inside-center-left": [0, 0.5, 0, 0.5],
1934
+ "inside-center-center": [0.5, 0.5, 0.5, 0.5],
1935
+ "inside-center-right": [1, 0.5, 1, 0.5],
1936
+ "inside-bottom-left": [0, 1, 0, 1],
1937
+ "inside-bottom-center": [0.5, 1, 0.5, 1],
1938
+ "inside-bottom-right": [1, 1, 1, 1],
1939
+ "outside-top": [0.5, 0, 0.5, 1],
1940
+ "outside-right": [1, 0.5, 0, 0.5],
1941
+ "outside-bottom": [0.5, 1, 0.5, 0],
1942
+ "outside-left": [0, 0.5, 1, 0.5]
1943
+ };
1944
+ function mountDocument(document2, container, options = {}, onDispose) {
1945
+ const dom = container.ownerDocument;
1946
+ const window = dom.defaultView;
1947
+ let zoom = options.zoom ?? "fit-width";
1948
+ checkZoom(zoom);
1949
+ const gap = options.gap ?? 16;
1950
+ if (!Number.isFinite(gap) || gap < 0) throw new RangeError("gap must be a nonnegative number.");
1951
+ const element = dom.createElement("div");
1952
+ element.setAttribute("role", "region");
1953
+ element.setAttribute("aria-label", options.title ?? "Document");
1954
+ element.tabIndex = 0;
1955
+ Object.assign(element.style, { display: "flex", flexDirection: "column", alignItems: "center", gap: `${gap}px`, width: "100%" });
1956
+ const attachments = /* @__PURE__ */ new Set();
1957
+ let disposed = false;
1958
+ let frame = 0;
1959
+ let painting = false;
1960
+ let visibilityReady = false;
1961
+ let paintError;
1962
+ let pendingPaint;
1963
+ let paintingIndex;
1964
+ const lifetime = new AbortController();
1965
+ let selection;
1966
+ let selectionLifetime;
1967
+ function prepareSelection(event) {
1968
+ if (event.target.closest("[data-onodocs-attachment]")) return;
1969
+ if (selection) return;
1970
+ selectionLifetime = new AbortController();
1971
+ selection = mountTextSelection(document2.pages.flatMap((page) => page.interaction.text), element, shells.map((entry) => entry.shell), document2.pages.map((page) => page.size), nearby, AbortSignal.any([lifetime.signal, selectionLifetime.signal]));
1972
+ }
1973
+ __name(prepareSelection, "prepareSelection");
1974
+ for (const type of ["pointerdown", "keydown", "contextmenu", "focus"]) element.addEventListener(type, prepareSelection, { capture: true, signal: lifetime.signal });
1975
+ const waiters = /* @__PURE__ */ new Set();
1976
+ const nearby = /* @__PURE__ */ new Set();
1977
+ const intersections = /* @__PURE__ */ new Set();
1978
+ const painted = /* @__PURE__ */ new Set();
1979
+ let shells = createShells();
1980
+ function createShells(previous = []) {
1981
+ return document2.pages.map((page) => {
1982
+ const prior = previous[page.index];
1983
+ if (prior) {
1984
+ if (prior.page.size.width !== page.size.width || prior.page.size.height !== page.size.height) {
1985
+ prior.width = 0;
1986
+ Object.assign(prior.layer.style, { width: `${page.size.width * cssScale}px`, height: `${page.size.height * cssScale}px` });
1987
+ }
1988
+ prior.page = page;
1989
+ prior.paintedWidth = 0;
1990
+ return prior;
1991
+ }
1992
+ const shell = dom.createElement("section");
1993
+ shell.setAttribute("aria-label", `Page ${page.index + 1}`);
1994
+ Object.assign(shell.style, { position: "relative", flex: "none", background: "white" });
1995
+ const canvas = dom.createElement("canvas");
1996
+ canvas.width = 0;
1997
+ canvas.height = 0;
1998
+ Object.assign(canvas.style, { width: "100%", height: "100%", display: "block" });
1999
+ const layer = dom.createElement("div");
2000
+ Object.assign(layer.style, { position: "absolute", left: "0", top: "0", transformOrigin: "0 0", width: `${page.size.width * cssScale}px`, height: `${page.size.height * cssScale}px`, pointerEvents: "none" });
2001
+ shell.append(canvas, layer);
2002
+ element.append(shell);
2003
+ return { shell, canvas, layer, page, width: 0, paintedWidth: 0, pixelRatio: 0 };
2004
+ });
2005
+ }
2006
+ __name(createShells, "createShells");
2007
+ const observer = new window.ResizeObserver(resize);
2008
+ const visibility = new window.IntersectionObserver((entries) => {
2009
+ visibilityReady = true;
2010
+ for (const entry of entries) {
2011
+ const index = Number(entry.target.dataset.pageIndex);
2012
+ if (entry.target !== shells[index]?.shell) continue;
2013
+ if (entry.isIntersecting) intersections.add(index);
2014
+ else intersections.delete(index);
2015
+ }
2016
+ nearby.clear();
2017
+ for (const index of intersections) for (let neighbor = Math.max(0, index - 1); neighbor <= Math.min(shells.length - 1, index + 1); neighbor++) nearby.add(neighbor);
2018
+ if (paintingIndex !== void 0 && !nearby.has(paintingIndex)) pendingPaint?.abort();
2019
+ schedulePaint();
2020
+ selection?.paint();
2021
+ }, { rootMargin: "100% 0px" });
2022
+ function observePages() {
2023
+ for (const entry of shells) {
2024
+ entry.shell.dataset.pageIndex = String(entry.page.index);
2025
+ visibility.observe(entry.shell);
2026
+ }
2027
+ }
2028
+ __name(observePages, "observePages");
2029
+ try {
2030
+ container.append(element);
2031
+ resize();
2032
+ observer.observe(container);
2033
+ observePages();
2034
+ window.addEventListener("resize", resize);
2035
+ } catch (error) {
2036
+ observer.disconnect();
2037
+ visibility.disconnect();
2038
+ window.cancelAnimationFrame(frame);
2039
+ element.remove();
2040
+ throw error;
2041
+ }
2042
+ const view = {
2043
+ element,
2044
+ get disposed() {
2045
+ return disposed;
2046
+ },
2047
+ whenRendered() {
2048
+ checkOpen();
2049
+ if (paintError) return Promise.reject(paintError);
2050
+ const result = new Promise((resolve, reject) => waiters.add({ resolve, reject }));
2051
+ schedulePaint();
2052
+ return result;
2053
+ },
2054
+ refresh() {
2055
+ checkOpen();
2056
+ selectionLifetime?.abort();
2057
+ selection = void 0;
2058
+ pendingPaint?.abort();
2059
+ paintError = void 0;
2060
+ for (const attachment of attachments) attachment.dispose();
2061
+ for (const entry of shells.slice(document2.pages.length)) {
2062
+ visibility.unobserve(entry.shell);
2063
+ nearby.delete(entry.page.index);
2064
+ intersections.delete(entry.page.index);
2065
+ painted.delete(entry.page.index);
2066
+ entry.canvas.width = 0;
2067
+ entry.canvas.height = 0;
2068
+ entry.shell.remove();
2069
+ }
2070
+ shells = createShells(shells);
2071
+ resize();
2072
+ observePages();
2073
+ },
2074
+ setZoom(value) {
2075
+ checkOpen();
2076
+ checkZoom(value);
2077
+ pendingPaint?.abort();
2078
+ paintError = void 0;
2079
+ zoom = value;
2080
+ resize();
2081
+ },
2082
+ toClient(pageIndex, point) {
2083
+ checkOpen();
2084
+ const entry = shells[pageIndex];
2085
+ if (!entry || !Number.isSafeInteger(pageIndex)) throw new RangeError("Page index is outside the document.");
2086
+ const rectangle = entry.shell.getBoundingClientRect();
2087
+ return { x: rectangle.left + point.x / entry.page.size.width * rectangle.width, y: rectangle.top + point.y / entry.page.size.height * rectangle.height };
2088
+ },
2089
+ fromClient(point) {
2090
+ checkOpen();
2091
+ let first = 0, last = shells.length - 1;
2092
+ while (first <= last) {
2093
+ const middle = first + last >>> 1;
2094
+ const entry = shells[middle];
2095
+ const rectangle = entry.shell.getBoundingClientRect();
2096
+ if (rectangle.width > 0 && point.x >= rectangle.left && point.x <= rectangle.right && point.y >= rectangle.top && point.y <= rectangle.bottom) return { pageIndex: entry.page.index, point: { x: (point.x - rectangle.left) / rectangle.width * entry.page.size.width, y: (point.y - rectangle.top) / rectangle.height * entry.page.size.height } };
2097
+ if (point.y < rectangle.top) last = middle - 1;
2098
+ else first = middle + 1;
2099
+ }
2100
+ return void 0;
2101
+ },
2102
+ hitTest(point) {
2103
+ const location = view.fromClient(point);
2104
+ return location && hitTestPage(document2.pages[location.pageIndex].interaction, location.point);
2105
+ },
2106
+ scrollTo(anchor, options2 = {}) {
2107
+ checkOpen();
2108
+ const fragment = fragmentsFor(anchor)[0];
2109
+ if (!fragment) return false;
2110
+ const marker = dom.createElement("span");
2111
+ const attachment = view.attach(marker, { anchor: fragment });
2112
+ try {
2113
+ marker.scrollIntoView({ block: "nearest", inline: "nearest", ...options2 });
2114
+ } finally {
2115
+ attachment.dispose();
2116
+ }
2117
+ return true;
2118
+ },
2119
+ attach(node, options2) {
2120
+ checkOpen();
2121
+ const fragments = fragmentsFor(options2.anchor);
2122
+ if (fragments.length !== 1) throw new RangeError(`Anchor has ${fragments.length} fragments. Pass one document fragment explicitly.`);
2123
+ const fragment = fragments[0];
2124
+ const entry = shells[fragment.pageIndex];
2125
+ const outer = dom.createElement("div");
2126
+ outer.dataset.onodocsAttachment = "";
2127
+ Object.assign(outer.style, { position: "absolute", inset: "0", pointerEvents: "none" });
2128
+ let parent = outer;
2129
+ for (const clip of fragment.clips) {
2130
+ const wrapper = dom.createElement("div");
2131
+ const { x: x2, y: y2, width, height } = clip.rectangle;
2132
+ const t2 = clip.transform;
2133
+ const points = [[x2, y2], [x2 + width, y2], [x2 + width, y2 + height], [x2, y2 + height]].map(([x3, y3]) => `${(t2.a * x3 + t2.c * y3 + t2.e) * cssScale}px ${(t2.b * x3 + t2.d * y3 + t2.f) * cssScale}px`);
2134
+ Object.assign(wrapper.style, { position: "absolute", inset: "0", clipPath: `polygon(${points.join(",")})` });
2135
+ parent.append(wrapper);
2136
+ parent = wrapper;
2137
+ }
2138
+ const box = dom.createElement("div");
2139
+ const r = fragment.rectangle, t = fragment.transform;
2140
+ const inset = options2.inset ?? 0, gap2 = options2.gap ?? 0;
2141
+ const size = options2.size ?? { width: Math.max(0, r.width - 2 * inset), height: Math.max(0, r.height - 2 * inset) };
2142
+ const [anchorX, anchorY, elementX, elementY] = placementPoints[options2.placement ?? "overlay"];
2143
+ const x = r.x + anchorX * r.width - elementX * size.width + (1 - 2 * anchorX) * inset + (anchorX - elementX) * gap2 + (options2.offset?.x ?? 0);
2144
+ const y = r.y + anchorY * r.height - elementY * size.height + (1 - 2 * anchorY) * inset + (anchorY - elementY) * gap2 + (options2.offset?.y ?? 0);
2145
+ Object.assign(box.style, { position: "absolute", left: "0", top: "0", width: `${size.width * cssScale}px`, height: `${size.height * cssScale}px`, transformOrigin: "0 0", transform: `matrix(${t.a},${t.b},${t.c},${t.d},${(t.a * x + t.c * y + t.e) * cssScale},${(t.b * x + t.d * y + t.f) * cssScale})`, pointerEvents: "auto" });
2146
+ attachedElements.get(node)?.dispose();
2147
+ const oldStyle = node.getAttribute("style");
2148
+ Object.assign(node.style, { width: "100%", height: "100%", boxSizing: "border-box", margin: "0" });
2149
+ box.append(node);
2150
+ parent.append(box);
2151
+ entry.layer.append(outer);
2152
+ let detached = false;
2153
+ const attachment = { element: node, get disposed() {
2154
+ return detached;
2155
+ }, dispose() {
2156
+ if (detached) return;
2157
+ detached = true;
2158
+ outer.remove();
2159
+ node.remove();
2160
+ if (oldStyle === null) node.removeAttribute("style");
2161
+ else node.setAttribute("style", oldStyle);
2162
+ attachments.delete(attachment);
2163
+ attachedElements.delete(node);
2164
+ } };
2165
+ attachments.add(attachment);
2166
+ attachedElements.set(node, attachment);
2167
+ return attachment;
2168
+ },
2169
+ dispose() {
2170
+ if (disposed) return;
2171
+ disposed = true;
2172
+ lifetime.abort();
2173
+ for (const waiter of waiters) waiter.reject(new DOMException("View is disposed.", "AbortError"));
2174
+ waiters.clear();
2175
+ observer.disconnect();
2176
+ visibility.disconnect();
2177
+ window.removeEventListener("resize", resize);
2178
+ window.cancelAnimationFrame(frame);
2179
+ nearby.clear();
2180
+ intersections.clear();
2181
+ painted.clear();
2182
+ for (const attachment of attachments) attachment.dispose();
2183
+ element.remove();
2184
+ for (const { canvas } of shells) {
2185
+ canvas.width = 0;
2186
+ canvas.height = 0;
2187
+ }
2188
+ element.replaceChildren();
2189
+ onDispose();
2190
+ }
2191
+ };
2192
+ return view;
2193
+ function fragmentsFor(anchor) {
2194
+ if ("index" in anchor) {
2195
+ const rectangle = { x: 0, y: 0, ...anchor.size };
2196
+ return [{ pageIndex: anchor.index, rectangle, bounds: rectangle, transform: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, clips: [] }];
2197
+ }
2198
+ return [anchor];
2199
+ }
2200
+ __name(fragmentsFor, "fragmentsFor");
2201
+ function resize() {
2202
+ if (disposed) return;
2203
+ const available = container.clientWidth;
2204
+ for (const entry of shells) {
2205
+ const width = zoom === "fit-width" ? available : entry.page.size.width * cssScale * zoom;
2206
+ if (width <= 0 || width === entry.width) continue;
2207
+ entry.width = width;
2208
+ Object.assign(entry.shell.style, { width: `${width}px`, height: `${width * entry.page.size.height / entry.page.size.width}px` });
2209
+ entry.layer.style.transform = `scale(${width / (entry.page.size.width * cssScale)})`;
2210
+ }
2211
+ element.style.minWidth = `${shells.reduce((width, entry) => Math.max(width, entry.width), available)}px`;
2212
+ selection?.paint();
2213
+ schedulePaint();
2214
+ }
2215
+ __name(resize, "resize");
2216
+ function schedulePaint() {
2217
+ if (!disposed && !painting && !frame) frame = window.requestAnimationFrame(() => {
2218
+ void paint();
2219
+ });
2220
+ }
2221
+ __name(schedulePaint, "schedulePaint");
2222
+ async function paint() {
2223
+ frame = 0;
2224
+ if (disposed || !visibilityReady) return;
2225
+ painting = true;
2226
+ try {
2227
+ for (const index of painted) if (!nearby.has(index)) {
2228
+ const entry = shells[index];
2229
+ entry.canvas.width = 0;
2230
+ entry.canvas.height = 0;
2231
+ entry.paintedWidth = 0;
2232
+ painted.delete(index);
2233
+ }
2234
+ const pixelRatio = window.devicePixelRatio || 1;
2235
+ const pending = [...nearby].map((index) => shells[index]).filter((entry) => entry.width > 0 && (entry.paintedWidth !== entry.width || entry.pixelRatio !== pixelRatio));
2236
+ const center = window.innerHeight / 2;
2237
+ pending.sort((a, b) => Math.abs(a.shell.getBoundingClientRect().top - center) - Math.abs(b.shell.getBoundingClientRect().top - center));
2238
+ const start = window.performance.now();
2239
+ for (const entry of pending) {
2240
+ const width = entry.width;
2241
+ const page = entry.page;
2242
+ paintingIndex = page.index;
2243
+ pendingPaint = new AbortController();
2244
+ await page.render(entry.canvas, { dpi: width / page.size.width * 1440 * pixelRatio, signal: AbortSignal.any([lifetime.signal, pendingPaint.signal]) });
2245
+ if (shells[page.index] !== entry) {
2246
+ entry.canvas.width = 0;
2247
+ entry.canvas.height = 0;
2248
+ continue;
2249
+ }
2250
+ entry.paintedWidth = page === entry.page ? width : 0;
2251
+ entry.pixelRatio = pixelRatio;
2252
+ painted.add(entry.page.index);
2253
+ if (window.performance.now() - start >= 8) break;
2254
+ }
2255
+ } catch (error) {
2256
+ if (!disposed && !pendingPaint?.signal.aborted) {
2257
+ paintError = error;
2258
+ for (const waiter of waiters) waiter.reject(error);
2259
+ waiters.clear();
2260
+ element.dispatchEvent(new ErrorEvent("error", { error, message: String(error), bubbles: true }));
2261
+ }
2262
+ } finally {
2263
+ painting = false;
2264
+ pendingPaint = void 0;
2265
+ paintingIndex = void 0;
2266
+ }
2267
+ if (disposed || paintError) return;
2268
+ if ([...nearby].some((index) => {
2269
+ const entry = shells[index];
2270
+ return entry.width > 0 && (entry.paintedWidth !== entry.width || entry.pixelRatio !== (window.devicePixelRatio || 1));
2271
+ }) || [...painted].some((index) => !nearby.has(index))) schedulePaint();
2272
+ else {
2273
+ for (const waiter of waiters) waiter.resolve();
2274
+ waiters.clear();
2275
+ }
2276
+ }
2277
+ __name(paint, "paint");
2278
+ function checkOpen() {
2279
+ if (disposed) throw new Error("View is disposed.");
2280
+ }
2281
+ __name(checkOpen, "checkOpen");
2282
+ }
2283
+ __name(mountDocument, "mountDocument");
2284
+ function checkZoom(zoom) {
2285
+ if (zoom !== "fit-width" && (typeof zoom !== "number" || !Number.isFinite(zoom) || zoom <= 0)) throw new RangeError("zoom must be positive or 'fit-width'.");
2286
+ }
2287
+ __name(checkZoom, "checkZoom");
2288
+
2289
+ // packages/canvas/src/evaluation-watermark.ts
2290
+ function paintEvaluationWatermark(context, width, height) {
2291
+ context.save();
2292
+ context.resetTransform();
2293
+ const scale = Math.min(width / 600, height / 200);
2294
+ context.translate(width / 2, height - 18 * scale);
2295
+ context.scale(scale, scale);
2296
+ context.fillStyle = "#555555";
2297
+ context.font = "12px sans-serif";
2298
+ context.textAlign = "center";
2299
+ context.textBaseline = "middle";
2300
+ context.fillText("OnoDocs evaluation \u2014 not for production use", 0, 0);
2301
+ context.restore();
2302
+ }
2303
+ __name(paintEvaluationWatermark, "paintEvaluationWatermark");
2304
+
2305
+ // packages/canvas/src/transport.ts
2306
+ function encode(page) {
2307
+ return JSON.stringify(page, (_key, value) => typeof value === "bigint" ? { $integer: value.toString() } : value instanceof Uint8Array ? { $bytes: base64(value) } : value);
2308
+ }
2309
+ __name(encode, "encode");
2310
+ function decode(value) {
2311
+ return JSON.parse(value, (_key, value2) => value2 && typeof value2 === "object" ? "$integer" in value2 ? BigInt(value2.$integer) : "$bytes" in value2 ? Uint8Array.from(atob(value2.$bytes), (value3) => value3.charCodeAt(0)) : value2 : value2);
2312
+ }
2313
+ __name(decode, "decode");
2314
+ function base64(bytes) {
2315
+ const parts = [];
2316
+ for (let index = 0; index < bytes.length; index += 32768) parts.push(String.fromCharCode(...bytes.subarray(index, index + 32768)));
2317
+ return btoa(parts.join(""));
2318
+ }
2319
+ __name(base64, "base64");
2320
+
2321
+ // packages/canvas/src/index.ts
2322
+ function createDocument(source, options = {}) {
2323
+ const lifetime = new AbortController();
2324
+ const views = /* @__PURE__ */ new Set();
2325
+ const faces = /* @__PURE__ */ new Map();
2326
+ const ownedFaces = /* @__PURE__ */ new Set();
2327
+ let pages = [];
2328
+ let disposed = false;
2329
+ let mounted;
2330
+ let unsubscribe;
2331
+ const result = {
2332
+ get pages() {
2333
+ return pages;
2334
+ },
2335
+ get disposed() {
2336
+ return disposed;
2337
+ },
2338
+ get view() {
2339
+ return mounted;
2340
+ },
2341
+ mount(container, options2) {
2342
+ checkOpen();
2343
+ const view = mountDocument(result, container, options2, () => views.delete(view));
2344
+ views.add(view);
2345
+ return view;
2346
+ },
2347
+ refresh() {
2348
+ checkOpen();
2349
+ pages = source.pages.map((page) => ({ index: page.index, size: page.size, get interaction() {
2350
+ return page.interaction;
2351
+ }, async render(canvas, options2 = {}) {
2352
+ checkOpen();
2353
+ const signal = options2.signal ? AbortSignal.any([options2.signal, lifetime.signal]) : lifetime.signal;
2354
+ signal.throwIfAborted();
2355
+ const dpi = options2.dpi ?? 96;
2356
+ if (!Number.isFinite(dpi) || dpi <= 0) throw new RangeError("dpi must be positive and finite.");
2357
+ const data = await page.load(signal);
2358
+ signal.throwIfAborted();
2359
+ await Promise.all((data.fonts ?? []).map(loadFont));
2360
+ signal.throwIfAborted();
2361
+ const images = data.images && await materializeImageResources(data.images, signal);
2362
+ try {
2363
+ signal.throwIfAborted();
2364
+ const ratio = dpi / 1440;
2365
+ canvas.width = Math.ceil(page.size.width * ratio);
2366
+ canvas.height = Math.ceil(page.size.height * ratio);
2367
+ const context = canvas.getContext("2d");
2368
+ if (!context) throw new Error("Canvas 2D context is unavailable.");
2369
+ context.fillStyle = "white";
2370
+ context.fillRect(0, 0, canvas.width, canvas.height);
2371
+ context.scale(ratio, ratio);
2372
+ materializePageDisplayListToCanvas(data, context, images);
2373
+ if (data.evaluation) paintEvaluationWatermark(context, canvas.width, canvas.height);
2374
+ } finally {
2375
+ images?.dispose();
2376
+ }
2377
+ } }));
2378
+ for (const view of views) view.refresh();
2379
+ },
2380
+ dispose() {
2381
+ if (disposed) return;
2382
+ disposed = true;
2383
+ lifetime.abort();
2384
+ unsubscribe?.();
2385
+ for (const view of views) view.dispose();
2386
+ for (const face of ownedFaces) document.fonts.delete(face);
2387
+ views.clear();
2388
+ ownedFaces.clear();
2389
+ faces.clear();
2390
+ }
2391
+ };
2392
+ unsubscribe = source.subscribe?.(() => source.disposed ? result.dispose() : result.refresh());
2393
+ try {
2394
+ result.refresh();
2395
+ if (options.container) mounted = result.mount(options.container, options.viewOptions);
2396
+ } catch (error) {
2397
+ result.dispose();
2398
+ throw error;
2399
+ }
2400
+ return result;
2401
+ function checkOpen() {
2402
+ if (disposed || source.disposed) throw new Error("Canvas document is disposed.");
2403
+ }
2404
+ __name(checkOpen, "checkOpen");
2405
+ function loadFont(resource) {
2406
+ const key = JSON.stringify([resource.family, resource.descriptors]);
2407
+ let pending = faces.get(key);
2408
+ if (!pending) {
2409
+ const face = new FontFace(resource.family, typeof resource.source === "string" ? resource.source : resource.source.slice().buffer, resource.descriptors);
2410
+ pending = face.load().then(() => {
2411
+ checkOpen();
2412
+ document.fonts.add(face);
2413
+ ownedFaces.add(face);
2414
+ });
2415
+ faces.set(key, pending);
2416
+ void pending.catch(() => faces.delete(key));
2417
+ }
2418
+ return pending;
2419
+ }
2420
+ __name(loadFont, "loadFont");
2421
+ }
2422
+ __name(createDocument, "createDocument");
2423
+ export {
2424
+ createDocument,
2425
+ decode,
2426
+ encode,
2427
+ materializePageDisplayListToCanvas
2428
+ };