@embedpdf/engine-core 3.0.0-next.0 → 3.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AbortError-CjSasIlL.js → AbortError-8-9JAX1Y.js} +2 -2
- package/dist/{AbortError-CjSasIlL.js.map → AbortError-8-9JAX1Y.js.map} +1 -1
- package/dist/{AbortError-ByBflvEl.cjs → AbortError-CRSooKsk.cjs} +2 -2
- package/dist/{AbortError-ByBflvEl.cjs.map → AbortError-CRSooKsk.cjs.map} +1 -1
- package/dist/{AnnotationListSnapshot-W0RCsyDE.d.cts → AnnotationListSnapshot-DDxHpJQU.d.cts} +538 -41
- package/dist/{AnnotationListSnapshot-W0RCsyDE.d.ts → AnnotationListSnapshot-DDxHpJQU.d.ts} +538 -41
- package/dist/{DocumentSecurityService-Df79fgpe.d.cts → DocumentSecurityService-BV4_YXkU.d.cts} +33 -2
- package/dist/{DocumentSecurityService-B-tULKNY.d.ts → DocumentSecurityService-o9aeTHwT.d.ts} +33 -2
- package/dist/{Engine-D9loFZc1.d.cts → Engine-D9je6GdU.d.cts} +50 -19
- package/dist/{Engine-B7Oe7f5i.d.ts → Engine-DOoVem7_.d.ts} +50 -19
- package/dist/{subtype-qejjVr6h.cjs → PageInsertBlankInput-CzShuSJI.cjs} +184 -1
- package/dist/PageInsertBlankInput-CzShuSJI.cjs.map +1 -0
- package/dist/{subtype-BvAzLCGC.js → PageInsertBlankInput-WKY0tr6S.js} +149 -2
- package/dist/PageInsertBlankInput-WKY0tr6S.js.map +1 -0
- package/dist/{builders-C548U47M.js → builders-B_KufGJ9.js} +547 -169
- package/dist/builders-B_KufGJ9.js.map +1 -0
- package/dist/{builders-BvikWRuO.cjs → builders-Cp_rlGob.cjs} +603 -225
- package/dist/builders-Cp_rlGob.cjs.map +1 -0
- package/dist/conformance.cjs +822 -198
- package/dist/conformance.cjs.map +1 -1
- package/dist/conformance.d.cts +124 -6
- package/dist/conformance.d.ts +124 -6
- package/dist/conformance.js +777 -157
- package/dist/conformance.js.map +1 -1
- package/dist/{fold-CB4fAOOy.js → fold-BfyxxwS6.js} +162 -3
- package/dist/fold-BfyxxwS6.js.map +1 -0
- package/dist/{fold-IaMph3CO.cjs → fold-C7I5GpBz.cjs} +235 -4
- package/dist/fold-C7I5GpBz.cjs.map +1 -0
- package/dist/index.cjs +36 -18
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/runtime.cjs +41 -23
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +103 -5
- package/dist/runtime.d.ts +103 -5
- package/dist/runtime.js +5 -5
- package/dist/{schemas-B_1VyFsm.cjs → schemas-D1pxEv3t.cjs} +138 -32
- package/dist/schemas-D1pxEv3t.cjs.map +1 -0
- package/dist/{schemas-9OpSSeM6.js → schemas-DAnu8tuW.js} +95 -13
- package/dist/schemas-DAnu8tuW.js.map +1 -0
- package/dist/wire.cjs +118 -3
- package/dist/wire.cjs.map +1 -1
- package/dist/wire.d.cts +139 -27
- package/dist/wire.d.ts +139 -27
- package/dist/wire.js +111 -4
- package/dist/wire.js.map +1 -1
- package/package.json +1 -1
- package/dist/builders-BvikWRuO.cjs.map +0 -1
- package/dist/builders-C548U47M.js.map +0 -1
- package/dist/fold-CB4fAOOy.js.map +0 -1
- package/dist/fold-IaMph3CO.cjs.map +0 -1
- package/dist/schemas-9OpSSeM6.js.map +0 -1
- package/dist/schemas-B_1VyFsm.cjs.map +0 -1
- package/dist/subtype-BvAzLCGC.js.map +0 -1
- package/dist/subtype-qejjVr6h.cjs.map +0 -1
package/dist/conformance.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as AbortError } from "./AbortError-
|
|
4
|
-
import { A as FormSetValueResultSchema,
|
|
1
|
+
import { f as pdfQuadBounds, i as EngineError, n as foldText, u as isRotatedGeometryRun } from "./fold-BfyxxwS6.js";
|
|
2
|
+
import { f as EngineErrorCode, o as sliceTextByChars } from "./PageInsertBlankInput-WKY0tr6S.js";
|
|
3
|
+
import { t as AbortError } from "./AbortError-8-9JAX1Y.js";
|
|
4
|
+
import { A as FormSetValueResultSchema, B as PageDeleteResultSchema, O as FormImportResultSchema, Ot as FormSnapshotSchema, Pt as AnnotationDTOSchema, U as PageFlattenResultSchema, W as PageGeometrySnapshotSchema, Y as PageListSnapshotSchema, Z as PageMoveResultSchema, a as AnnotationCreateResultSchema, at as PageTextSnapshotSchema, c as AnnotationListPageSnapshotSchema, d as AnnotationUpdateResultSchema, k as FormRepairResultSchema, l as AnnotationListSnapshotAllPagesSchema, mr as DocumentActionsSnapshotSchema, mt as SearchSliceSchema, nt as PageRotateResultSchema, o as AnnotationDeleteResultSchema, st as RedactionApplyResultSchema, u as AnnotationMoveResultSchema } from "./schemas-DAnu8tuW.js";
|
|
5
5
|
//#region src/conformance/runMetadataConformance.ts
|
|
6
6
|
/**
|
|
7
7
|
* The conformance harness is transport-agnostic. It takes the test runner as
|
|
@@ -18,7 +18,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
18
18
|
if (engine) await engine.destroy();
|
|
19
19
|
});
|
|
20
20
|
test("reads metadata from sample fixture", async () => {
|
|
21
|
-
const doc = await openFixture$
|
|
21
|
+
const doc = await openFixture$17(engine, opts);
|
|
22
22
|
try {
|
|
23
23
|
const meta = await doc.metadata.read();
|
|
24
24
|
expect(meta).toMatchObject(opts.fixture.expected);
|
|
@@ -28,7 +28,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
test("abort() before completion rejects with AbortError", async () => {
|
|
31
|
-
const doc = await openFixture$
|
|
31
|
+
const doc = await openFixture$17(engine, opts);
|
|
32
32
|
try {
|
|
33
33
|
const p = doc.metadata.read();
|
|
34
34
|
p.abort("test");
|
|
@@ -38,7 +38,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
test("aborting one read does not affect a concurrent one", async () => {
|
|
41
|
-
const doc = await openFixture$
|
|
41
|
+
const doc = await openFixture$17(engine, opts);
|
|
42
42
|
try {
|
|
43
43
|
const a = doc.metadata.read();
|
|
44
44
|
const b = doc.metadata.read();
|
|
@@ -50,7 +50,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
50
50
|
}
|
|
51
51
|
});
|
|
52
52
|
test("metadata after close throws DocNotOpen", async () => {
|
|
53
|
-
const doc = await openFixture$
|
|
53
|
+
const doc = await openFixture$17(engine, opts);
|
|
54
54
|
await doc.close();
|
|
55
55
|
let caught;
|
|
56
56
|
try {
|
|
@@ -62,7 +62,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
62
62
|
expect(EngineError.is(caught, EngineErrorCode.DocNotOpen)).toBe(true);
|
|
63
63
|
});
|
|
64
64
|
test("update() sets standard + custom fields and a subsequent read observes them", async () => {
|
|
65
|
-
const doc = await openFixture$
|
|
65
|
+
const doc = await openFixture$17(engine, opts);
|
|
66
66
|
try {
|
|
67
67
|
const result = await doc.metadata.update({
|
|
68
68
|
title: "Conformance Title",
|
|
@@ -81,7 +81,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
81
81
|
}
|
|
82
82
|
});
|
|
83
83
|
test("update() clears a field with null", async () => {
|
|
84
|
-
const doc = await openFixture$
|
|
84
|
+
const doc = await openFixture$17(engine, opts);
|
|
85
85
|
try {
|
|
86
86
|
await doc.metadata.update({ title: "To Be Cleared" });
|
|
87
87
|
const set = await doc.metadata.read();
|
|
@@ -94,7 +94,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
96
|
test("update() after close throws DocNotOpen", async () => {
|
|
97
|
-
const doc = await openFixture$
|
|
97
|
+
const doc = await openFixture$17(engine, opts);
|
|
98
98
|
await doc.close();
|
|
99
99
|
let caught;
|
|
100
100
|
try {
|
|
@@ -107,7 +107,7 @@ function runMetadataConformance(runner, opts) {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
async function openFixture$
|
|
110
|
+
async function openFixture$17(engine, opts) {
|
|
111
111
|
if (opts.openKind === "bytes") {
|
|
112
112
|
const bytes = await opts.fixture.bytes();
|
|
113
113
|
return engine.open({
|
|
@@ -134,11 +134,12 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
134
134
|
if (engine) await engine.destroy();
|
|
135
135
|
});
|
|
136
136
|
test("listRawAll returns one entry per page with valid PageState", async () => {
|
|
137
|
-
const doc = await openFixture$
|
|
137
|
+
const doc = await openFixture$16(engine, opts);
|
|
138
138
|
try {
|
|
139
139
|
const snap = await doc.annotations.listRawAll();
|
|
140
140
|
expect(AnnotationListSnapshotAllPagesSchema.safeParse(snap).success).toBe(true);
|
|
141
141
|
expect(snap.pages.length >= 1).toBe(true);
|
|
142
|
+
if (snap.auditHead !== void 0) expect(Number.isInteger(snap.auditHead) && snap.auditHead >= 0).toBe(true);
|
|
142
143
|
const target = snap.pages.find((p) => p.pageState.pageObjectNumber === opts.fixture.pageObjectNumber);
|
|
143
144
|
expect(target !== void 0).toBe(true);
|
|
144
145
|
expect(target.annotations.length).toBe(opts.fixture.expectedAnnotationCount);
|
|
@@ -147,7 +148,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
147
148
|
}
|
|
148
149
|
});
|
|
149
150
|
test("listRaw on the test page returns the expected counts and DTO shape", async () => {
|
|
150
|
-
const doc = await openFixture$
|
|
151
|
+
const doc = await openFixture$16(engine, opts);
|
|
151
152
|
try {
|
|
152
153
|
const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
|
|
153
154
|
expect(AnnotationListPageSnapshotSchema.safeParse(snap).success).toBe(true);
|
|
@@ -177,7 +178,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
177
178
|
}
|
|
178
179
|
});
|
|
179
180
|
test("shape/vertex/line annotations expose their family fields", async () => {
|
|
180
|
-
const doc = await openFixture$
|
|
181
|
+
const doc = await openFixture$16(engine, opts);
|
|
181
182
|
try {
|
|
182
183
|
const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
|
|
183
184
|
for (const a of snap.annotations) {
|
|
@@ -236,7 +237,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
236
237
|
}
|
|
237
238
|
});
|
|
238
239
|
test("full page list dispatches per-subtype and matches raw counts", async () => {
|
|
239
|
-
const doc = await openFixture$
|
|
240
|
+
const doc = await openFixture$16(engine, opts);
|
|
240
241
|
try {
|
|
241
242
|
const full = await doc.page(opts.fixture.pageObjectNumber).annotations.list();
|
|
242
243
|
expect(AnnotationListPageSnapshotSchema.safeParse(full).success).toBe(true);
|
|
@@ -246,7 +247,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
246
247
|
}
|
|
247
248
|
});
|
|
248
249
|
test("every annotation DTO satisfies AnnotationDTOSchema (discriminated union)", async () => {
|
|
249
|
-
const doc = await openFixture$
|
|
250
|
+
const doc = await openFixture$16(engine, opts);
|
|
250
251
|
try {
|
|
251
252
|
const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
|
|
252
253
|
for (const a of snap.annotations) {
|
|
@@ -286,7 +287,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
286
287
|
});
|
|
287
288
|
if (opts.fixture.expectsWeakAnnotation) {
|
|
288
289
|
test("weak annotations carry identityQuality: weak and ref.kind: index", async () => {
|
|
289
|
-
const doc = await openFixture$
|
|
290
|
+
const doc = await openFixture$16(engine, opts);
|
|
290
291
|
try {
|
|
291
292
|
const weak = (await doc.annotations.listRaw(opts.fixture.pageObjectNumber)).annotations.find((a) => a.identityQuality === "weak");
|
|
292
293
|
expect(weak !== void 0).toBe(true);
|
|
@@ -296,7 +297,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
296
297
|
}
|
|
297
298
|
});
|
|
298
299
|
test("a stale revision token throws InvalidReference", async () => {
|
|
299
|
-
const doc = await openFixture$
|
|
300
|
+
const doc = await openFixture$16(engine, opts);
|
|
300
301
|
try {
|
|
301
302
|
const snap = await doc.page(opts.fixture.pageObjectNumber).annotations.list();
|
|
302
303
|
const weak = snap.annotations.find((a) => a.identityQuality === "weak");
|
|
@@ -312,7 +313,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
312
313
|
});
|
|
313
314
|
}
|
|
314
315
|
test("abort() on listRaw rejects with AbortError", async () => {
|
|
315
|
-
const doc = await openFixture$
|
|
316
|
+
const doc = await openFixture$16(engine, opts);
|
|
316
317
|
try {
|
|
317
318
|
const p = doc.annotations.listRaw(opts.fixture.pageObjectNumber);
|
|
318
319
|
p.abort("test");
|
|
@@ -322,7 +323,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
322
323
|
}
|
|
323
324
|
});
|
|
324
325
|
test("listRaw on an unknown pageObjectNumber throws NotFound", async () => {
|
|
325
|
-
const doc = await openFixture$
|
|
326
|
+
const doc = await openFixture$16(engine, opts);
|
|
326
327
|
try {
|
|
327
328
|
let caught;
|
|
328
329
|
try {
|
|
@@ -338,7 +339,7 @@ function runAnnotationReadConformance(runner, opts) {
|
|
|
338
339
|
});
|
|
339
340
|
});
|
|
340
341
|
}
|
|
341
|
-
async function openFixture$
|
|
342
|
+
async function openFixture$16(engine, opts) {
|
|
342
343
|
if (opts.openKind === "bytes") {
|
|
343
344
|
const bytes = await opts.fixture.bytes();
|
|
344
345
|
return engine.open({
|
|
@@ -596,7 +597,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
596
597
|
if (engine) await engine.destroy();
|
|
597
598
|
});
|
|
598
599
|
test("create appends without shifting indices and leaves weak refs valid", async () => {
|
|
599
|
-
const doc = await openFixture$
|
|
600
|
+
const doc = await openFixture$15(engine, opts);
|
|
600
601
|
try {
|
|
601
602
|
const page = doc.page(fix.pageObjectNumber);
|
|
602
603
|
const before = await page.annotations.list();
|
|
@@ -632,7 +633,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
632
633
|
}
|
|
633
634
|
});
|
|
634
635
|
test("create + update honor annotation flags (set on create, merge on patch)", async () => {
|
|
635
|
-
const doc = await openFixture$
|
|
636
|
+
const doc = await openFixture$15(engine, opts);
|
|
636
637
|
try {
|
|
637
638
|
const page = doc.page(fix.pageObjectNumber);
|
|
638
639
|
const draft = {
|
|
@@ -667,7 +668,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
667
668
|
}
|
|
668
669
|
});
|
|
669
670
|
test("create shape annotations (circle + square) round-trip shape fields", async () => {
|
|
670
|
-
const doc = await openFixture$
|
|
671
|
+
const doc = await openFixture$15(engine, opts);
|
|
671
672
|
try {
|
|
672
673
|
const page = doc.page(fix.pageObjectNumber);
|
|
673
674
|
const circleDraft = {
|
|
@@ -748,7 +749,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
748
749
|
}
|
|
749
750
|
});
|
|
750
751
|
test("cloudy border (/BE) + rect differences (/RD) are tri-state", async () => {
|
|
751
|
-
const doc = await openFixture$
|
|
752
|
+
const doc = await openFixture$15(engine, opts);
|
|
752
753
|
try {
|
|
753
754
|
const page = doc.page(fix.pageObjectNumber);
|
|
754
755
|
const plainDraft = {
|
|
@@ -827,7 +828,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
827
828
|
}
|
|
828
829
|
});
|
|
829
830
|
test("update echoes the appearance verdict: moves preserve /AP, restyles re-bake", async () => {
|
|
830
|
-
const doc = await openFixture$
|
|
831
|
+
const doc = await openFixture$15(engine, opts);
|
|
831
832
|
try {
|
|
832
833
|
const page = doc.page(fix.pageObjectNumber);
|
|
833
834
|
const draft = {
|
|
@@ -902,7 +903,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
902
903
|
}
|
|
903
904
|
});
|
|
904
905
|
test("a partial /DA patch preserves the unpatched font members", async () => {
|
|
905
|
-
const doc = await openFixture$
|
|
906
|
+
const doc = await openFixture$15(engine, opts);
|
|
906
907
|
try {
|
|
907
908
|
const page = doc.page(fix.pageObjectNumber);
|
|
908
909
|
const draft = {
|
|
@@ -958,7 +959,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
958
959
|
}
|
|
959
960
|
});
|
|
960
961
|
test("box transform is tri-state: rect-only moves keep rotation, null flattens", async () => {
|
|
961
|
-
const doc = await openFixture$
|
|
962
|
+
const doc = await openFixture$15(engine, opts);
|
|
962
963
|
try {
|
|
963
964
|
const page = doc.page(fix.pageObjectNumber);
|
|
964
965
|
const draft = {
|
|
@@ -1019,7 +1020,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1019
1020
|
}
|
|
1020
1021
|
});
|
|
1021
1022
|
test("create vertex + line annotations (polygon/polyline/line) round-trip geometry", async () => {
|
|
1022
|
-
const doc = await openFixture$
|
|
1023
|
+
const doc = await openFixture$15(engine, opts);
|
|
1023
1024
|
try {
|
|
1024
1025
|
const page = doc.page(fix.pageObjectNumber);
|
|
1025
1026
|
const polygonDraft = {
|
|
@@ -1150,7 +1151,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1150
1151
|
}
|
|
1151
1152
|
});
|
|
1152
1153
|
test("create free-text + callout round-trip text/colour/intent fields", async () => {
|
|
1153
|
-
const doc = await openFixture$
|
|
1154
|
+
const doc = await openFixture$15(engine, opts);
|
|
1154
1155
|
try {
|
|
1155
1156
|
const page = doc.page(fix.pageObjectNumber);
|
|
1156
1157
|
const freeTextDraft = {
|
|
@@ -1246,7 +1247,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1246
1247
|
}
|
|
1247
1248
|
});
|
|
1248
1249
|
test("update a free-text patches alignment + colours and is non-structural", async () => {
|
|
1249
|
-
const doc = await openFixture$
|
|
1250
|
+
const doc = await openFixture$15(engine, opts);
|
|
1250
1251
|
try {
|
|
1251
1252
|
const page = doc.page(fix.pageObjectNumber);
|
|
1252
1253
|
const created = await page.annotations.create({
|
|
@@ -1307,7 +1308,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1307
1308
|
}
|
|
1308
1309
|
});
|
|
1309
1310
|
test("create redact (area + text) round-trips label + colour fields", async () => {
|
|
1310
|
-
const doc = await openFixture$
|
|
1311
|
+
const doc = await openFixture$15(engine, opts);
|
|
1311
1312
|
try {
|
|
1312
1313
|
const page = doc.page(fix.pageObjectNumber);
|
|
1313
1314
|
const areaDraft = {
|
|
@@ -1387,7 +1388,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1387
1388
|
}
|
|
1388
1389
|
});
|
|
1389
1390
|
test("update a redact patches the label and clears it, non-structurally", async () => {
|
|
1390
|
-
const doc = await openFixture$
|
|
1391
|
+
const doc = await openFixture$15(engine, opts);
|
|
1391
1392
|
try {
|
|
1392
1393
|
const page = doc.page(fix.pageObjectNumber);
|
|
1393
1394
|
const created = await page.annotations.create({
|
|
@@ -1464,7 +1465,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1464
1465
|
}
|
|
1465
1466
|
});
|
|
1466
1467
|
test("create + update a caret round-trips color/opacity/rect differences", async () => {
|
|
1467
|
-
const doc = await openFixture$
|
|
1468
|
+
const doc = await openFixture$15(engine, opts);
|
|
1468
1469
|
try {
|
|
1469
1470
|
const page = doc.page(fix.pageObjectNumber);
|
|
1470
1471
|
const caretDraft = {
|
|
@@ -1544,7 +1545,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1544
1545
|
}
|
|
1545
1546
|
});
|
|
1546
1547
|
test("update an ink annotation patches strokes + color and is non-structural", async () => {
|
|
1547
|
-
const doc = await openFixture$
|
|
1548
|
+
const doc = await openFixture$15(engine, opts);
|
|
1548
1549
|
try {
|
|
1549
1550
|
const page = doc.page(fix.pageObjectNumber);
|
|
1550
1551
|
const created = await page.annotations.create({
|
|
@@ -1595,7 +1596,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1595
1596
|
}
|
|
1596
1597
|
});
|
|
1597
1598
|
test("ink highlight intent + blend round-trip and unrelated patches preserve blend", async () => {
|
|
1598
|
-
const doc = await openFixture$
|
|
1599
|
+
const doc = await openFixture$15(engine, opts);
|
|
1599
1600
|
try {
|
|
1600
1601
|
const page = doc.page(fix.pageObjectNumber);
|
|
1601
1602
|
const created = await page.annotations.create({
|
|
@@ -1638,7 +1639,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1638
1639
|
}
|
|
1639
1640
|
});
|
|
1640
1641
|
test("update a polyline patches vertices + line endings and is non-structural", async () => {
|
|
1641
|
-
const doc = await openFixture$
|
|
1642
|
+
const doc = await openFixture$15(engine, opts);
|
|
1642
1643
|
try {
|
|
1643
1644
|
const page = doc.page(fix.pageObjectNumber);
|
|
1644
1645
|
const created = await page.annotations.create({
|
|
@@ -1687,7 +1688,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1687
1688
|
}
|
|
1688
1689
|
});
|
|
1689
1690
|
test("update a shape annotation patches color and is non-structural", async () => {
|
|
1690
|
-
const doc = await openFixture$
|
|
1691
|
+
const doc = await openFixture$15(engine, opts);
|
|
1691
1692
|
try {
|
|
1692
1693
|
const page = doc.page(fix.pageObjectNumber);
|
|
1693
1694
|
const created = await page.annotations.create({
|
|
@@ -1736,7 +1737,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1736
1737
|
}
|
|
1737
1738
|
});
|
|
1738
1739
|
if (opts.supportsAppearanceRasters) test("creating a shape bakes an /AP appearance the reader can rasterize", async () => {
|
|
1739
|
-
const doc = await openFixture$
|
|
1740
|
+
const doc = await openFixture$15(engine, opts);
|
|
1740
1741
|
try {
|
|
1741
1742
|
const page = doc.page(fix.pageObjectNumber);
|
|
1742
1743
|
const created = await page.annotations.create({
|
|
@@ -1766,7 +1767,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1766
1767
|
}
|
|
1767
1768
|
});
|
|
1768
1769
|
test("update on a durable annotation is non-structural and never touches /NM", async () => {
|
|
1769
|
-
const doc = await openFixture$
|
|
1770
|
+
const doc = await openFixture$15(engine, opts);
|
|
1770
1771
|
try {
|
|
1771
1772
|
const page = doc.page(fix.pageObjectNumber);
|
|
1772
1773
|
const before = await page.annotations.list();
|
|
@@ -1792,7 +1793,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1792
1793
|
}
|
|
1793
1794
|
});
|
|
1794
1795
|
if (fix.expectsWeakAnnotation) test("update on a weak annotation stamps a UUID v4 /NM and upgrades the ref", async () => {
|
|
1795
|
-
const doc = await openFixture$
|
|
1796
|
+
const doc = await openFixture$15(engine, opts);
|
|
1796
1797
|
try {
|
|
1797
1798
|
const page = doc.page(fix.pageObjectNumber);
|
|
1798
1799
|
const before = await page.annotations.list();
|
|
@@ -1822,7 +1823,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1822
1823
|
}
|
|
1823
1824
|
});
|
|
1824
1825
|
test("delete by objectNumber removes the annotation and reports a stable id", async () => {
|
|
1825
|
-
const doc = await openFixture$
|
|
1826
|
+
const doc = await openFixture$15(engine, opts);
|
|
1826
1827
|
try {
|
|
1827
1828
|
const page = doc.page(fix.pageObjectNumber);
|
|
1828
1829
|
const draft = {
|
|
@@ -1852,7 +1853,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1852
1853
|
}
|
|
1853
1854
|
});
|
|
1854
1855
|
if (fix.expectsWeakAnnotation) test("delete by index of a weak annotation reports deleted: null and refetch reason", async () => {
|
|
1855
|
-
const doc = await openFixture$
|
|
1856
|
+
const doc = await openFixture$15(engine, opts);
|
|
1856
1857
|
try {
|
|
1857
1858
|
const page = doc.page(fix.pageObjectNumber);
|
|
1858
1859
|
const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
|
|
@@ -1875,7 +1876,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1875
1876
|
}
|
|
1876
1877
|
});
|
|
1877
1878
|
test("abort on create rejects with AbortError", async () => {
|
|
1878
|
-
const doc = await openFixture$
|
|
1879
|
+
const doc = await openFixture$15(engine, opts);
|
|
1879
1880
|
try {
|
|
1880
1881
|
const page = doc.page(fix.pageObjectNumber);
|
|
1881
1882
|
const draft = {
|
|
@@ -1891,7 +1892,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1891
1892
|
}
|
|
1892
1893
|
});
|
|
1893
1894
|
test("update with a stale index revision throws InvalidReference", async () => {
|
|
1894
|
-
const doc = await openFixture$
|
|
1895
|
+
const doc = await openFixture$15(engine, opts);
|
|
1895
1896
|
try {
|
|
1896
1897
|
const page = doc.page(fix.pageObjectNumber);
|
|
1897
1898
|
const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
|
|
@@ -1918,7 +1919,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1918
1919
|
}
|
|
1919
1920
|
});
|
|
1920
1921
|
test("move single durable annotation reorders within the page (single-as-batch)", async () => {
|
|
1921
|
-
const doc = await openFixture$
|
|
1922
|
+
const doc = await openFixture$15(engine, opts);
|
|
1922
1923
|
try {
|
|
1923
1924
|
const page = doc.page(fix.pageObjectNumber);
|
|
1924
1925
|
const aDraft = {
|
|
@@ -1962,7 +1963,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1962
1963
|
}
|
|
1963
1964
|
});
|
|
1964
1965
|
test("move multi-block preserves caller-supplied order at the destination", async () => {
|
|
1965
|
-
const doc = await openFixture$
|
|
1966
|
+
const doc = await openFixture$15(engine, opts);
|
|
1966
1967
|
try {
|
|
1967
1968
|
const page = doc.page(fix.pageObjectNumber);
|
|
1968
1969
|
const ids = await Promise.all([
|
|
@@ -2001,7 +2002,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2001
2002
|
}
|
|
2002
2003
|
});
|
|
2003
2004
|
if (fix.expectsWeakAnnotation) test("move on a weak annotation upgrades it to durable /NM (one rev bump for batch)", async () => {
|
|
2004
|
-
const doc = await openFixture$
|
|
2005
|
+
const doc = await openFixture$15(engine, opts);
|
|
2005
2006
|
try {
|
|
2006
2007
|
const page = doc.page(fix.pageObjectNumber);
|
|
2007
2008
|
const before = await page.annotations.list();
|
|
@@ -2026,7 +2027,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2026
2027
|
}
|
|
2027
2028
|
});
|
|
2028
2029
|
test("move with a stale index revision rejects (locked rev-token guard)", async () => {
|
|
2029
|
-
const doc = await openFixture$
|
|
2030
|
+
const doc = await openFixture$15(engine, opts);
|
|
2030
2031
|
try {
|
|
2031
2032
|
const page = doc.page(fix.pageObjectNumber);
|
|
2032
2033
|
const a = await page.annotations.create({
|
|
@@ -2064,7 +2065,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2064
2065
|
}
|
|
2065
2066
|
});
|
|
2066
2067
|
test("move with out-of-range toIndex rejects with InvalidArg", async () => {
|
|
2067
|
-
const doc = await openFixture$
|
|
2068
|
+
const doc = await openFixture$15(engine, opts);
|
|
2068
2069
|
try {
|
|
2069
2070
|
const page = doc.page(fix.pageObjectNumber);
|
|
2070
2071
|
const a = await page.annotations.create({
|
|
@@ -2088,7 +2089,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2088
2089
|
}
|
|
2089
2090
|
});
|
|
2090
2091
|
test("move with duplicate refs rejects with InvalidArg", async () => {
|
|
2091
|
-
const doc = await openFixture$
|
|
2092
|
+
const doc = await openFixture$15(engine, opts);
|
|
2092
2093
|
try {
|
|
2093
2094
|
const page = doc.page(fix.pageObjectNumber);
|
|
2094
2095
|
const a = await page.annotations.create({
|
|
@@ -2111,7 +2112,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2111
2112
|
}
|
|
2112
2113
|
});
|
|
2113
2114
|
test("abort on move rejects with AbortError", async () => {
|
|
2114
|
-
const doc = await openFixture$
|
|
2115
|
+
const doc = await openFixture$15(engine, opts);
|
|
2115
2116
|
try {
|
|
2116
2117
|
const page = doc.page(fix.pageObjectNumber);
|
|
2117
2118
|
const a = await page.annotations.create({
|
|
@@ -2132,7 +2133,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2132
2133
|
}
|
|
2133
2134
|
});
|
|
2134
2135
|
test("a page move does NOT bump per-page RevisionTokens (weak refs survive reorder)", async () => {
|
|
2135
|
-
const doc = await openFixture$
|
|
2136
|
+
const doc = await openFixture$15(engine, opts);
|
|
2136
2137
|
try {
|
|
2137
2138
|
const list = await doc.pages.list();
|
|
2138
2139
|
if (list.pages.length < 1) return;
|
|
@@ -2147,7 +2148,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2147
2148
|
}
|
|
2148
2149
|
});
|
|
2149
2150
|
test("create a reply links /IRT and defaults /RT to \"reply\", reporting the parent", async () => {
|
|
2150
|
-
const doc = await openFixture$
|
|
2151
|
+
const doc = await openFixture$15(engine, opts);
|
|
2151
2152
|
try {
|
|
2152
2153
|
const page = doc.page(fix.pageObjectNumber);
|
|
2153
2154
|
const parent = await page.annotations.create({
|
|
@@ -2181,7 +2182,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2181
2182
|
}
|
|
2182
2183
|
});
|
|
2183
2184
|
test("create a grouped subordinate writes /RT /Group", async () => {
|
|
2184
|
-
const doc = await openFixture$
|
|
2185
|
+
const doc = await openFixture$15(engine, opts);
|
|
2185
2186
|
try {
|
|
2186
2187
|
const page = doc.page(fix.pageObjectNumber);
|
|
2187
2188
|
const primary = await page.annotations.create({
|
|
@@ -2212,7 +2213,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2212
2213
|
}
|
|
2213
2214
|
});
|
|
2214
2215
|
test("create link annotations round-trip uri, goto/xyz, goto/fitH, and dead targets", async () => {
|
|
2215
|
-
const doc = await openFixture$
|
|
2216
|
+
const doc = await openFixture$15(engine, opts);
|
|
2216
2217
|
try {
|
|
2217
2218
|
const page = doc.page(fix.pageObjectNumber);
|
|
2218
2219
|
const uri = await page.annotations.create({
|
|
@@ -2289,7 +2290,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2289
2290
|
}
|
|
2290
2291
|
});
|
|
2291
2292
|
test("a link patch retargets in both directions (uri→goto, goto→uri) and moves the rect", async () => {
|
|
2292
|
-
const doc = await openFixture$
|
|
2293
|
+
const doc = await openFixture$15(engine, opts);
|
|
2293
2294
|
try {
|
|
2294
2295
|
const page = doc.page(fix.pageObjectNumber);
|
|
2295
2296
|
const created = await page.annotations.create({
|
|
@@ -2344,7 +2345,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2344
2345
|
}
|
|
2345
2346
|
});
|
|
2346
2347
|
test("a link patch clears the target with null — a dead link on re-read", async () => {
|
|
2347
|
-
const doc = await openFixture$
|
|
2348
|
+
const doc = await openFixture$15(engine, opts);
|
|
2348
2349
|
try {
|
|
2349
2350
|
const page = doc.page(fix.pageObjectNumber);
|
|
2350
2351
|
const created = await page.annotations.create({
|
|
@@ -2383,7 +2384,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2383
2384
|
}
|
|
2384
2385
|
});
|
|
2385
2386
|
test("a link grouped to a highlight round-trips /IRT + /RT /Group with its target", async () => {
|
|
2386
|
-
const doc = await openFixture$
|
|
2387
|
+
const doc = await openFixture$15(engine, opts);
|
|
2387
2388
|
try {
|
|
2388
2389
|
const page = doc.page(fix.pageObjectNumber);
|
|
2389
2390
|
const parent = await page.annotations.create({
|
|
@@ -2417,7 +2418,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2417
2418
|
}
|
|
2418
2419
|
});
|
|
2419
2420
|
test("replace-text round-trips /IT and groups StrikeOut under its Caret", async () => {
|
|
2420
|
-
const doc = await openFixture$
|
|
2421
|
+
const doc = await openFixture$15(engine, opts);
|
|
2421
2422
|
try {
|
|
2422
2423
|
const page = doc.page(fix.pageObjectNumber);
|
|
2423
2424
|
const caret = await page.annotations.create({
|
|
@@ -2469,7 +2470,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2469
2470
|
}
|
|
2470
2471
|
});
|
|
2471
2472
|
test("patch inReplyTo: null clears /IRT and /RT (back to top-level)", async () => {
|
|
2472
|
-
const doc = await openFixture$
|
|
2473
|
+
const doc = await openFixture$15(engine, opts);
|
|
2473
2474
|
try {
|
|
2474
2475
|
const page = doc.page(fix.pageObjectNumber);
|
|
2475
2476
|
const parent = await page.annotations.create({
|
|
@@ -2496,7 +2497,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2496
2497
|
}
|
|
2497
2498
|
});
|
|
2498
2499
|
test("create with a cross-page /IRT parent throws InvalidArg", async () => {
|
|
2499
|
-
const doc = await openFixture$
|
|
2500
|
+
const doc = await openFixture$15(engine, opts);
|
|
2500
2501
|
try {
|
|
2501
2502
|
const page = doc.page(fix.pageObjectNumber);
|
|
2502
2503
|
const parent = await page.annotations.create({
|
|
@@ -2526,13 +2527,136 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2526
2527
|
await doc.close();
|
|
2527
2528
|
}
|
|
2528
2529
|
});
|
|
2530
|
+
test("a review-status reply round-trips /State + /StateModel + /Subj", async () => {
|
|
2531
|
+
const doc = await openFixture$15(engine, opts);
|
|
2532
|
+
try {
|
|
2533
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2534
|
+
const target = await page.annotations.create({
|
|
2535
|
+
subtype: "highlight",
|
|
2536
|
+
contents: "status target",
|
|
2537
|
+
subject: "Pricing question",
|
|
2538
|
+
quadPoints: quad
|
|
2539
|
+
});
|
|
2540
|
+
expect(target.created.subject).toBe("Pricing question");
|
|
2541
|
+
const status = await page.annotations.create({
|
|
2542
|
+
subtype: "text",
|
|
2543
|
+
rect: shapeRect,
|
|
2544
|
+
inReplyTo: target.created.ref,
|
|
2545
|
+
state: "accepted",
|
|
2546
|
+
stateModel: "review"
|
|
2547
|
+
});
|
|
2548
|
+
expect(AnnotationCreateResultSchema.safeParse(status).success).toBe(true);
|
|
2549
|
+
expect(status.created.subtype).toBe("text");
|
|
2550
|
+
if (status.created.subtype !== "text") return;
|
|
2551
|
+
expect(status.created.state).toBe("accepted");
|
|
2552
|
+
expect(status.created.stateModel).toBe("review");
|
|
2553
|
+
expect(status.created.replyType).toBe("reply");
|
|
2554
|
+
const read = (await page.annotations.list()).annotations.find((a) => a.ref.kind === "objectNumber" && status.created.ref.kind === "objectNumber" && a.ref.annotObjectNumber === status.created.ref.annotObjectNumber);
|
|
2555
|
+
expect(read?.subtype).toBe("text");
|
|
2556
|
+
if (read?.subtype === "text") {
|
|
2557
|
+
expect(read.state).toBe("accepted");
|
|
2558
|
+
expect(read.stateModel).toBe("review");
|
|
2559
|
+
}
|
|
2560
|
+
} finally {
|
|
2561
|
+
await doc.close();
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
test("a draft /State without /StateModel is rejected with InvalidArg", async () => {
|
|
2565
|
+
const doc = await openFixture$15(engine, opts);
|
|
2566
|
+
try {
|
|
2567
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2568
|
+
let caught;
|
|
2569
|
+
try {
|
|
2570
|
+
await page.annotations.create({
|
|
2571
|
+
subtype: "text",
|
|
2572
|
+
rect: shapeRect,
|
|
2573
|
+
state: "accepted"
|
|
2574
|
+
});
|
|
2575
|
+
} catch (err) {
|
|
2576
|
+
caught = err;
|
|
2577
|
+
}
|
|
2578
|
+
expect(EngineError.is(caught, EngineErrorCode.InvalidArg)).toBe(true);
|
|
2579
|
+
} finally {
|
|
2580
|
+
await doc.close();
|
|
2581
|
+
}
|
|
2582
|
+
});
|
|
2583
|
+
test("null-clear patches truly remove /State, /StateModel, /Subj and /Contents", async () => {
|
|
2584
|
+
const doc = await openFixture$15(engine, opts);
|
|
2585
|
+
try {
|
|
2586
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2587
|
+
const note = await page.annotations.create({
|
|
2588
|
+
subtype: "text",
|
|
2589
|
+
rect: shapeRect,
|
|
2590
|
+
contents: "work in progress",
|
|
2591
|
+
subject: "Draft",
|
|
2592
|
+
state: "none",
|
|
2593
|
+
stateModel: "review"
|
|
2594
|
+
});
|
|
2595
|
+
const cycled = await page.annotations.update(note.created.ref, {
|
|
2596
|
+
subtype: "text",
|
|
2597
|
+
state: "rejected"
|
|
2598
|
+
});
|
|
2599
|
+
expect(cycled.appearance.changed).toBe(false);
|
|
2600
|
+
expect(cycled.updated.subtype).toBe("text");
|
|
2601
|
+
if (cycled.updated.subtype === "text") {
|
|
2602
|
+
expect(cycled.updated.state).toBe("rejected");
|
|
2603
|
+
expect(cycled.updated.stateModel).toBe("review");
|
|
2604
|
+
}
|
|
2605
|
+
const cleared = await page.annotations.update(note.created.ref, {
|
|
2606
|
+
subtype: "text",
|
|
2607
|
+
contents: null,
|
|
2608
|
+
subject: null,
|
|
2609
|
+
state: null,
|
|
2610
|
+
stateModel: null
|
|
2611
|
+
});
|
|
2612
|
+
expect(cleared.appearance.changed).toBe(false);
|
|
2613
|
+
expect(cleared.updated.contents).toBe(null);
|
|
2614
|
+
expect(cleared.updated.subject).toBe(null);
|
|
2615
|
+
if (cleared.updated.subtype === "text") {
|
|
2616
|
+
expect(cleared.updated.state).toBe(null);
|
|
2617
|
+
expect(cleared.updated.stateModel).toBe(null);
|
|
2618
|
+
}
|
|
2619
|
+
const read = (await page.annotations.list()).annotations.find((a) => a.ref.kind === "objectNumber" && note.created.ref.kind === "objectNumber" && a.ref.annotObjectNumber === note.created.ref.annotObjectNumber);
|
|
2620
|
+
expect(read?.contents).toBe(null);
|
|
2621
|
+
expect(read?.subject).toBe(null);
|
|
2622
|
+
if (read?.subtype === "text") {
|
|
2623
|
+
expect(read.state).toBe(null);
|
|
2624
|
+
expect(read.stateModel).toBe(null);
|
|
2625
|
+
}
|
|
2626
|
+
} finally {
|
|
2627
|
+
await doc.close();
|
|
2628
|
+
}
|
|
2629
|
+
});
|
|
2630
|
+
test("custom state models round-trip verbatim", async () => {
|
|
2631
|
+
const doc = await openFixture$15(engine, opts);
|
|
2632
|
+
try {
|
|
2633
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2634
|
+
const custom = await page.annotations.create({
|
|
2635
|
+
subtype: "text",
|
|
2636
|
+
rect: shapeRect,
|
|
2637
|
+
state: "in-progress",
|
|
2638
|
+
stateModel: "X-ReviewWorkflow"
|
|
2639
|
+
});
|
|
2640
|
+
expect(custom.created.subtype).toBe("text");
|
|
2641
|
+
if (custom.created.subtype !== "text") return;
|
|
2642
|
+
expect(custom.created.state).toBe("in-progress");
|
|
2643
|
+
expect(custom.created.stateModel).toBe("X-ReviewWorkflow");
|
|
2644
|
+
const read = (await page.annotations.list()).annotations.find((a) => a.ref.kind === "objectNumber" && custom.created.ref.kind === "objectNumber" && a.ref.annotObjectNumber === custom.created.ref.annotObjectNumber);
|
|
2645
|
+
if (read?.subtype === "text") {
|
|
2646
|
+
expect(read.state).toBe("in-progress");
|
|
2647
|
+
expect(read.stateModel).toBe("X-ReviewWorkflow");
|
|
2648
|
+
}
|
|
2649
|
+
} finally {
|
|
2650
|
+
await doc.close();
|
|
2651
|
+
}
|
|
2652
|
+
});
|
|
2529
2653
|
});
|
|
2530
2654
|
}
|
|
2531
2655
|
async function beginWeakEditIfRequired(doc, pageObjectNumber, fix) {
|
|
2532
2656
|
if (doc.capabilities.weakAnnotationEditSessions !== "required" || !fix.expectsWeakAnnotation) return null;
|
|
2533
2657
|
return doc.annotations.beginWeakEdit([pageObjectNumber]);
|
|
2534
2658
|
}
|
|
2535
|
-
async function openFixture$
|
|
2659
|
+
async function openFixture$15(engine, opts) {
|
|
2536
2660
|
if (opts.openKind === "bytes") {
|
|
2537
2661
|
const bytes = await opts.fixture.bytes();
|
|
2538
2662
|
return engine.open({
|
|
@@ -3122,7 +3246,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3122
3246
|
if (engine) await engine.destroy();
|
|
3123
3247
|
});
|
|
3124
3248
|
test("renders the expected set of appearances with valid output", async () => {
|
|
3125
|
-
const doc = await openFixture$
|
|
3249
|
+
const doc = await openFixture$14(engine, opts);
|
|
3126
3250
|
try {
|
|
3127
3251
|
const { pageState, appearances } = await collect(doc, opts, useRaw);
|
|
3128
3252
|
expect(pageState.pageObjectNumber).toBe(opts.fixture.pageObjectNumber);
|
|
@@ -3149,7 +3273,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3149
3273
|
}
|
|
3150
3274
|
});
|
|
3151
3275
|
if (opts.fixture.expectsWeakAppearance) test("weak (index-only) annotations are emitted, not dropped", async () => {
|
|
3152
|
-
const doc = await openFixture$
|
|
3276
|
+
const doc = await openFixture$14(engine, opts);
|
|
3153
3277
|
try {
|
|
3154
3278
|
const { appearances } = await collect(doc, opts, useRaw);
|
|
3155
3279
|
const weak = appearances.find((a) => a.ref.kind === "index");
|
|
@@ -3159,7 +3283,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3159
3283
|
}
|
|
3160
3284
|
});
|
|
3161
3285
|
if (useRaw) test("rendered appearances are not blank (non-zero alpha)", async () => {
|
|
3162
|
-
const doc = await openFixture$
|
|
3286
|
+
const doc = await openFixture$14(engine, opts);
|
|
3163
3287
|
try {
|
|
3164
3288
|
const { appearances } = await collect(doc, opts, useRaw);
|
|
3165
3289
|
const anyVisible = appearances.some((a) => a.raster !== null && hasOpaquePixel(a.raster));
|
|
@@ -3169,7 +3293,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3169
3293
|
}
|
|
3170
3294
|
});
|
|
3171
3295
|
test("abort() rejects with AbortError", async () => {
|
|
3172
|
-
const doc = await openFixture$
|
|
3296
|
+
const doc = await openFixture$14(engine, opts);
|
|
3173
3297
|
try {
|
|
3174
3298
|
const page = doc.page(opts.fixture.pageObjectNumber);
|
|
3175
3299
|
const p = useRaw ? page.annotations.renderAppearances({ scale: 1 }) : page.annotations.renderAppearanceImages({
|
|
@@ -3224,7 +3348,7 @@ function hasOpaquePixel(raster) {
|
|
|
3224
3348
|
}
|
|
3225
3349
|
return false;
|
|
3226
3350
|
}
|
|
3227
|
-
async function openFixture$
|
|
3351
|
+
async function openFixture$14(engine, opts) {
|
|
3228
3352
|
if (opts.openKind === "bytes") {
|
|
3229
3353
|
const bytes = await opts.fixture.bytes();
|
|
3230
3354
|
return engine.open({
|
|
@@ -3293,7 +3417,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3293
3417
|
if (engine) await engine.destroy();
|
|
3294
3418
|
});
|
|
3295
3419
|
test("pages.list() returns every page in display order, durable PONs only", async () => {
|
|
3296
|
-
const doc = await openFixture$
|
|
3420
|
+
const doc = await openFixture$13(engine, opts);
|
|
3297
3421
|
try {
|
|
3298
3422
|
const list = await doc.pages.list();
|
|
3299
3423
|
expect(PageListSnapshotSchema.safeParse(list).success).toBe(true);
|
|
@@ -3312,7 +3436,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3312
3436
|
}
|
|
3313
3437
|
});
|
|
3314
3438
|
test("pages.move() reorders pages and returns the full post-move order", async () => {
|
|
3315
|
-
const doc = await openFixture$
|
|
3439
|
+
const doc = await openFixture$13(engine, opts);
|
|
3316
3440
|
try {
|
|
3317
3441
|
const before = await doc.pages.list();
|
|
3318
3442
|
if (before.pages.length < 3) return;
|
|
@@ -3336,7 +3460,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3336
3460
|
}
|
|
3337
3461
|
});
|
|
3338
3462
|
test("weak index-based annotation refs survive a page reorder", async () => {
|
|
3339
|
-
const doc = await openFixture$
|
|
3463
|
+
const doc = await openFixture$13(engine, opts);
|
|
3340
3464
|
try {
|
|
3341
3465
|
const list = await doc.pages.list();
|
|
3342
3466
|
if (list.pages.length < 2) return;
|
|
@@ -3373,7 +3497,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3373
3497
|
}
|
|
3374
3498
|
});
|
|
3375
3499
|
test("pages.move() rejects duplicate PONs with InvalidArg", async () => {
|
|
3376
|
-
const doc = await openFixture$
|
|
3500
|
+
const doc = await openFixture$13(engine, opts);
|
|
3377
3501
|
try {
|
|
3378
3502
|
const list = await doc.pages.list();
|
|
3379
3503
|
if (list.pages.length < 1) return;
|
|
@@ -3390,7 +3514,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3390
3514
|
}
|
|
3391
3515
|
});
|
|
3392
3516
|
test("pages.move() rejects out-of-range destIndex with InvalidArg", async () => {
|
|
3393
|
-
const doc = await openFixture$
|
|
3517
|
+
const doc = await openFixture$13(engine, opts);
|
|
3394
3518
|
try {
|
|
3395
3519
|
const list = await doc.pages.list();
|
|
3396
3520
|
if (list.pages.length < 1) return;
|
|
@@ -3407,7 +3531,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3407
3531
|
}
|
|
3408
3532
|
});
|
|
3409
3533
|
test("pages.move() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
3410
|
-
const doc = await openFixture$
|
|
3534
|
+
const doc = await openFixture$13(engine, opts);
|
|
3411
3535
|
try {
|
|
3412
3536
|
const list = await doc.pages.list();
|
|
3413
3537
|
let bogus = 0;
|
|
@@ -3425,7 +3549,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3425
3549
|
}
|
|
3426
3550
|
});
|
|
3427
3551
|
test("abort on pages.move() rejects with AbortError", async () => {
|
|
3428
|
-
const doc = await openFixture$
|
|
3552
|
+
const doc = await openFixture$13(engine, opts);
|
|
3429
3553
|
try {
|
|
3430
3554
|
const list = await doc.pages.list();
|
|
3431
3555
|
if (list.pages.length < 1) return;
|
|
@@ -3439,7 +3563,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3439
3563
|
});
|
|
3440
3564
|
});
|
|
3441
3565
|
}
|
|
3442
|
-
async function openFixture$
|
|
3566
|
+
async function openFixture$13(engine, opts) {
|
|
3443
3567
|
if (opts.openKind === "bytes") {
|
|
3444
3568
|
const bytes = await opts.fixture.bytes();
|
|
3445
3569
|
return engine.open({
|
|
@@ -3513,7 +3637,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3513
3637
|
if (engine) await engine.destroy();
|
|
3514
3638
|
});
|
|
3515
3639
|
test("pages.rotate() sets the absolute rotation and returns the full layout", async () => {
|
|
3516
|
-
const doc = await openFixture$
|
|
3640
|
+
const doc = await openFixture$12(engine, opts);
|
|
3517
3641
|
try {
|
|
3518
3642
|
const before = await doc.pages.list();
|
|
3519
3643
|
const target = before.pages[0];
|
|
@@ -3532,7 +3656,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3532
3656
|
}
|
|
3533
3657
|
});
|
|
3534
3658
|
test("absolute rotation is idempotent: setting the same value twice is a no-op", async () => {
|
|
3535
|
-
const doc = await openFixture$
|
|
3659
|
+
const doc = await openFixture$12(engine, opts);
|
|
3536
3660
|
try {
|
|
3537
3661
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3538
3662
|
const first = await doc.pages.rotate([pon], 180);
|
|
@@ -3543,7 +3667,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3543
3667
|
}
|
|
3544
3668
|
});
|
|
3545
3669
|
test("one rotation applies to EVERY listed page (the multi-select gesture)", async () => {
|
|
3546
|
-
const doc = await openFixture$
|
|
3670
|
+
const doc = await openFixture$12(engine, opts);
|
|
3547
3671
|
try {
|
|
3548
3672
|
const list = await doc.pages.list();
|
|
3549
3673
|
if (list.pages.length < 2) return;
|
|
@@ -3555,7 +3679,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3555
3679
|
}
|
|
3556
3680
|
});
|
|
3557
3681
|
test("weak index-based annotation refs survive a rotate (no revision bump)", async () => {
|
|
3558
|
-
const doc = await openFixture$
|
|
3682
|
+
const doc = await openFixture$12(engine, opts);
|
|
3559
3683
|
try {
|
|
3560
3684
|
const hostPon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3561
3685
|
const hostPage = doc.page(hostPon);
|
|
@@ -3585,7 +3709,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3585
3709
|
}
|
|
3586
3710
|
});
|
|
3587
3711
|
test("pages.rotate() rejects an invalid rotation value with InvalidArg", async () => {
|
|
3588
|
-
const doc = await openFixture$
|
|
3712
|
+
const doc = await openFixture$12(engine, opts);
|
|
3589
3713
|
try {
|
|
3590
3714
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3591
3715
|
let caught;
|
|
@@ -3600,7 +3724,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3600
3724
|
}
|
|
3601
3725
|
});
|
|
3602
3726
|
test("pages.rotate() rejects duplicate PONs with InvalidArg", async () => {
|
|
3603
|
-
const doc = await openFixture$
|
|
3727
|
+
const doc = await openFixture$12(engine, opts);
|
|
3604
3728
|
try {
|
|
3605
3729
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3606
3730
|
let caught;
|
|
@@ -3615,7 +3739,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3615
3739
|
}
|
|
3616
3740
|
});
|
|
3617
3741
|
test("pages.rotate() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
3618
|
-
const doc = await openFixture$
|
|
3742
|
+
const doc = await openFixture$12(engine, opts);
|
|
3619
3743
|
try {
|
|
3620
3744
|
const list = await doc.pages.list();
|
|
3621
3745
|
let bogus = 0;
|
|
@@ -3633,7 +3757,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3633
3757
|
}
|
|
3634
3758
|
});
|
|
3635
3759
|
test("abort on pages.rotate() rejects with AbortError", async () => {
|
|
3636
|
-
const doc = await openFixture$
|
|
3760
|
+
const doc = await openFixture$12(engine, opts);
|
|
3637
3761
|
try {
|
|
3638
3762
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3639
3763
|
const p = doc.pages.rotate([pon], 90);
|
|
@@ -3645,7 +3769,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3645
3769
|
});
|
|
3646
3770
|
});
|
|
3647
3771
|
}
|
|
3648
|
-
async function openFixture$
|
|
3772
|
+
async function openFixture$12(engine, opts) {
|
|
3649
3773
|
if (opts.openKind === "bytes") {
|
|
3650
3774
|
const bytes = await opts.fixture.bytes();
|
|
3651
3775
|
return engine.open({
|
|
@@ -3708,7 +3832,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3708
3832
|
if (engine) await engine.destroy();
|
|
3709
3833
|
});
|
|
3710
3834
|
test("pages.delete() removes the page and returns the surviving layout", async () => {
|
|
3711
|
-
const doc = await openFixture$
|
|
3835
|
+
const doc = await openFixture$11(engine, opts);
|
|
3712
3836
|
try {
|
|
3713
3837
|
const before = await doc.pages.list();
|
|
3714
3838
|
if (before.pages.length < 2) return;
|
|
@@ -3729,7 +3853,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3729
3853
|
}
|
|
3730
3854
|
});
|
|
3731
3855
|
test("addressing a deleted page afterwards is a clean NotFound", async () => {
|
|
3732
|
-
const doc = await openFixture$
|
|
3856
|
+
const doc = await openFixture$11(engine, opts);
|
|
3733
3857
|
try {
|
|
3734
3858
|
const before = await doc.pages.list();
|
|
3735
3859
|
if (before.pages.length < 2) return;
|
|
@@ -3747,7 +3871,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3747
3871
|
}
|
|
3748
3872
|
});
|
|
3749
3873
|
test("surviving pages keep their weak index-based annotation refs", async () => {
|
|
3750
|
-
const doc = await openFixture$
|
|
3874
|
+
const doc = await openFixture$11(engine, opts);
|
|
3751
3875
|
try {
|
|
3752
3876
|
const list = await doc.pages.list();
|
|
3753
3877
|
if (list.pages.length < 2) return;
|
|
@@ -3780,7 +3904,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3780
3904
|
}
|
|
3781
3905
|
});
|
|
3782
3906
|
test("deleting EVERY page is rejected with InvalidArg", async () => {
|
|
3783
|
-
const doc = await openFixture$
|
|
3907
|
+
const doc = await openFixture$11(engine, opts);
|
|
3784
3908
|
try {
|
|
3785
3909
|
const list = await doc.pages.list();
|
|
3786
3910
|
let caught;
|
|
@@ -3795,7 +3919,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3795
3919
|
}
|
|
3796
3920
|
});
|
|
3797
3921
|
test("pages.delete() rejects duplicate PONs with InvalidArg", async () => {
|
|
3798
|
-
const doc = await openFixture$
|
|
3922
|
+
const doc = await openFixture$11(engine, opts);
|
|
3799
3923
|
try {
|
|
3800
3924
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3801
3925
|
let caught;
|
|
@@ -3810,7 +3934,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3810
3934
|
}
|
|
3811
3935
|
});
|
|
3812
3936
|
test("pages.delete() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
3813
|
-
const doc = await openFixture$
|
|
3937
|
+
const doc = await openFixture$11(engine, opts);
|
|
3814
3938
|
try {
|
|
3815
3939
|
const list = await doc.pages.list();
|
|
3816
3940
|
let bogus = 0;
|
|
@@ -3828,7 +3952,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3828
3952
|
}
|
|
3829
3953
|
});
|
|
3830
3954
|
test("abort on pages.delete() rejects with AbortError", async () => {
|
|
3831
|
-
const doc = await openFixture$
|
|
3955
|
+
const doc = await openFixture$11(engine, opts);
|
|
3832
3956
|
try {
|
|
3833
3957
|
const list = await doc.pages.list();
|
|
3834
3958
|
if (list.pages.length < 2) return;
|
|
@@ -3842,7 +3966,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3842
3966
|
});
|
|
3843
3967
|
});
|
|
3844
3968
|
}
|
|
3845
|
-
async function openFixture$
|
|
3969
|
+
async function openFixture$11(engine, opts) {
|
|
3846
3970
|
if (opts.openKind === "bytes") {
|
|
3847
3971
|
const bytes = await opts.fixture.bytes();
|
|
3848
3972
|
return engine.open({
|
|
@@ -3870,7 +3994,7 @@ function runPageFlattenConformance(runner, opts) {
|
|
|
3870
3994
|
if (engine) await engine.destroy();
|
|
3871
3995
|
});
|
|
3872
3996
|
test("flattens eligible appearances once without changing layout", async () => {
|
|
3873
|
-
const doc = await openFixture$
|
|
3997
|
+
const doc = await openFixture$10(engine, opts);
|
|
3874
3998
|
try {
|
|
3875
3999
|
if (!doc.pages.flatten) return;
|
|
3876
4000
|
const layoutBefore = await doc.pages.list();
|
|
@@ -3904,7 +4028,7 @@ function runPageFlattenConformance(runner, opts) {
|
|
|
3904
4028
|
});
|
|
3905
4029
|
});
|
|
3906
4030
|
}
|
|
3907
|
-
async function openFixture$
|
|
4031
|
+
async function openFixture$10(engine, opts) {
|
|
3908
4032
|
if (opts.openKind === "bytes") return engine.open({
|
|
3909
4033
|
kind: "bytes",
|
|
3910
4034
|
id: opts.fixture.id,
|
|
@@ -3959,7 +4083,7 @@ function runRedactionApplyConformance(runner, opts) {
|
|
|
3959
4083
|
if (engine) await engine.destroy();
|
|
3960
4084
|
});
|
|
3961
4085
|
test("annotations scope applies the marked redaction and counts collateral", async () => {
|
|
3962
|
-
const doc = await openFixture$
|
|
4086
|
+
const doc = await openFixture$9(engine, opts);
|
|
3963
4087
|
try {
|
|
3964
4088
|
if (!doc.redaction) return;
|
|
3965
4089
|
const layoutBefore = await doc.pages.list();
|
|
@@ -4013,7 +4137,7 @@ function runRedactionApplyConformance(runner, opts) {
|
|
|
4013
4137
|
}
|
|
4014
4138
|
});
|
|
4015
4139
|
test("pages scope applies everything once and validates its input", async () => {
|
|
4016
|
-
const doc = await openFixture$
|
|
4140
|
+
const doc = await openFixture$9(engine, opts);
|
|
4017
4141
|
try {
|
|
4018
4142
|
if (!doc.redaction) return;
|
|
4019
4143
|
const pageObjectNumber = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
@@ -4064,7 +4188,7 @@ function runRedactionApplyConformance(runner, opts) {
|
|
|
4064
4188
|
});
|
|
4065
4189
|
});
|
|
4066
4190
|
}
|
|
4067
|
-
async function openFixture$
|
|
4191
|
+
async function openFixture$9(engine, opts) {
|
|
4068
4192
|
if (opts.openKind === "bytes") return engine.open({
|
|
4069
4193
|
kind: "bytes",
|
|
4070
4194
|
id: opts.fixture.id,
|
|
@@ -4104,19 +4228,14 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4104
4228
|
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
4105
4229
|
describe(`page extract conformance: ${opts.label}`, () => {
|
|
4106
4230
|
let engine;
|
|
4107
|
-
let supported = false;
|
|
4108
4231
|
beforeAll(async () => {
|
|
4109
4232
|
engine = await opts.makeEngine();
|
|
4110
|
-
const probe = await openFixture$5(engine, opts);
|
|
4111
|
-
supported = probe.pages.extract !== void 0;
|
|
4112
|
-
await probe.close();
|
|
4113
4233
|
});
|
|
4114
4234
|
afterAll(async () => {
|
|
4115
4235
|
if (engine) await engine.destroy();
|
|
4116
4236
|
});
|
|
4117
4237
|
test("pages.extract() returns standalone PDF bytes and leaves the source untouched", async () => {
|
|
4118
|
-
|
|
4119
|
-
const doc = await openFixture$5(engine, opts);
|
|
4238
|
+
const doc = await openFixture$8(engine, opts);
|
|
4120
4239
|
try {
|
|
4121
4240
|
const before = await doc.pages.list();
|
|
4122
4241
|
const target = before.pages[before.pages.length - 1];
|
|
@@ -4131,8 +4250,8 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4131
4250
|
}
|
|
4132
4251
|
});
|
|
4133
4252
|
test("extracted bytes re-open as a document with the requested pages, in caller order", async () => {
|
|
4134
|
-
if (
|
|
4135
|
-
const doc = await openFixture$
|
|
4253
|
+
if (opts.openKind !== "bytes") return;
|
|
4254
|
+
const doc = await openFixture$8(engine, opts);
|
|
4136
4255
|
let extracted = null;
|
|
4137
4256
|
try {
|
|
4138
4257
|
const list = await doc.pages.list();
|
|
@@ -4155,8 +4274,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4155
4274
|
}
|
|
4156
4275
|
});
|
|
4157
4276
|
test("pages.extract() rejects empty input with InvalidArg", async () => {
|
|
4158
|
-
|
|
4159
|
-
const doc = await openFixture$5(engine, opts);
|
|
4277
|
+
const doc = await openFixture$8(engine, opts);
|
|
4160
4278
|
try {
|
|
4161
4279
|
let caught;
|
|
4162
4280
|
try {
|
|
@@ -4170,8 +4288,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4170
4288
|
}
|
|
4171
4289
|
});
|
|
4172
4290
|
test("pages.extract() rejects duplicate PONs with InvalidArg", async () => {
|
|
4173
|
-
|
|
4174
|
-
const doc = await openFixture$5(engine, opts);
|
|
4291
|
+
const doc = await openFixture$8(engine, opts);
|
|
4175
4292
|
try {
|
|
4176
4293
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
4177
4294
|
let caught;
|
|
@@ -4186,8 +4303,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4186
4303
|
}
|
|
4187
4304
|
});
|
|
4188
4305
|
test("pages.extract() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
4189
|
-
|
|
4190
|
-
const doc = await openFixture$5(engine, opts);
|
|
4306
|
+
const doc = await openFixture$8(engine, opts);
|
|
4191
4307
|
try {
|
|
4192
4308
|
const list = await doc.pages.list();
|
|
4193
4309
|
let bogus = 0;
|
|
@@ -4205,8 +4321,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4205
4321
|
}
|
|
4206
4322
|
});
|
|
4207
4323
|
test("abort on pages.extract() rejects with AbortError", async () => {
|
|
4208
|
-
|
|
4209
|
-
const doc = await openFixture$5(engine, opts);
|
|
4324
|
+
const doc = await openFixture$8(engine, opts);
|
|
4210
4325
|
try {
|
|
4211
4326
|
const list = await doc.pages.list();
|
|
4212
4327
|
const p = doc.pages.extract([list.pages[0].pageObjectNumber]);
|
|
@@ -4218,7 +4333,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4218
4333
|
});
|
|
4219
4334
|
});
|
|
4220
4335
|
}
|
|
4221
|
-
async function openFixture$
|
|
4336
|
+
async function openFixture$8(engine, opts) {
|
|
4222
4337
|
if (opts.openKind === "bytes") {
|
|
4223
4338
|
const bytes = await opts.fixture.bytes();
|
|
4224
4339
|
return engine.open({
|
|
@@ -4480,9 +4595,9 @@ function runAttachmentConformance(runner, opts) {
|
|
|
4480
4595
|
//#endregion
|
|
4481
4596
|
//#region src/conformance/runPageInsertConformance.ts
|
|
4482
4597
|
/**
|
|
4483
|
-
* Page insert conformance. `pages.insert
|
|
4484
|
-
*
|
|
4485
|
-
*
|
|
4598
|
+
* Page insert conformance. `pages.insert` is a REQUIRED member — this suite
|
|
4599
|
+
* runs unconditionally on every engine, so an implementation that loses the
|
|
4600
|
+
* verb fails loudly instead of being skipped past.
|
|
4486
4601
|
*
|
|
4487
4602
|
* Invariants:
|
|
4488
4603
|
* 1. Every page of the source bytes is COPIED in at `destIndex` (omitted →
|
|
@@ -4498,19 +4613,14 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4498
4613
|
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
4499
4614
|
describe(`page insert conformance: ${opts.label}`, () => {
|
|
4500
4615
|
let engine;
|
|
4501
|
-
let supported = false;
|
|
4502
4616
|
beforeAll(async () => {
|
|
4503
4617
|
engine = await opts.makeEngine();
|
|
4504
|
-
const probe = await openFixture$4(engine, opts);
|
|
4505
|
-
supported = probe.pages.insert !== void 0 && probe.pages.extract !== void 0;
|
|
4506
|
-
await probe.close();
|
|
4507
4618
|
});
|
|
4508
4619
|
afterAll(async () => {
|
|
4509
4620
|
if (engine) await engine.destroy();
|
|
4510
4621
|
});
|
|
4511
4622
|
test("appends every source page with fresh PONs; existing pages keep identity", async () => {
|
|
4512
|
-
|
|
4513
|
-
const doc = await openFixture$4(engine, opts);
|
|
4623
|
+
const doc = await openFixture$7(engine, opts);
|
|
4514
4624
|
try {
|
|
4515
4625
|
const before = await doc.pages.list();
|
|
4516
4626
|
const beforePons = before.pages.map((p) => p.pageObjectNumber);
|
|
@@ -4528,8 +4638,7 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4528
4638
|
}
|
|
4529
4639
|
});
|
|
4530
4640
|
test("destIndex places the block mid-document, in source order", async () => {
|
|
4531
|
-
|
|
4532
|
-
const doc = await openFixture$4(engine, opts);
|
|
4641
|
+
const doc = await openFixture$7(engine, opts);
|
|
4533
4642
|
try {
|
|
4534
4643
|
const before = await doc.pages.list();
|
|
4535
4644
|
if (before.pages.length < 2) return;
|
|
@@ -4546,8 +4655,8 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4546
4655
|
}
|
|
4547
4656
|
});
|
|
4548
4657
|
test("the inserted pages persist through save → re-open", async () => {
|
|
4549
|
-
if (
|
|
4550
|
-
const doc = await openFixture$
|
|
4658
|
+
if (opts.openKind !== "bytes") return;
|
|
4659
|
+
const doc = await openFixture$7(engine, opts);
|
|
4551
4660
|
let reopened = null;
|
|
4552
4661
|
try {
|
|
4553
4662
|
const before = await doc.pages.list();
|
|
@@ -4567,8 +4676,7 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4567
4676
|
}
|
|
4568
4677
|
});
|
|
4569
4678
|
test("empty bytes reject with InvalidArg; garbage rejects with MalformedPdf", async () => {
|
|
4570
|
-
|
|
4571
|
-
const doc = await openFixture$4(engine, opts);
|
|
4679
|
+
const doc = await openFixture$7(engine, opts);
|
|
4572
4680
|
try {
|
|
4573
4681
|
let caught;
|
|
4574
4682
|
try {
|
|
@@ -4591,8 +4699,7 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4591
4699
|
}
|
|
4592
4700
|
});
|
|
4593
4701
|
test("out-of-range destIndex rejects with InvalidArg", async () => {
|
|
4594
|
-
|
|
4595
|
-
const doc = await openFixture$4(engine, opts);
|
|
4702
|
+
const doc = await openFixture$7(engine, opts);
|
|
4596
4703
|
try {
|
|
4597
4704
|
const before = await doc.pages.list();
|
|
4598
4705
|
const single = await doc.pages.extract([before.pages[0].pageObjectNumber]);
|
|
@@ -4609,7 +4716,160 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4609
4716
|
});
|
|
4610
4717
|
});
|
|
4611
4718
|
}
|
|
4612
|
-
async function openFixture$
|
|
4719
|
+
async function openFixture$7(engine, opts) {
|
|
4720
|
+
if (opts.openKind === "bytes") {
|
|
4721
|
+
const bytes = await opts.fixture.bytes();
|
|
4722
|
+
return engine.open({
|
|
4723
|
+
kind: "bytes",
|
|
4724
|
+
id: opts.fixture.id,
|
|
4725
|
+
bytes
|
|
4726
|
+
});
|
|
4727
|
+
}
|
|
4728
|
+
return engine.open({
|
|
4729
|
+
kind: "id",
|
|
4730
|
+
id: opts.fixture.cloudId ?? opts.fixture.id
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
//#endregion
|
|
4734
|
+
//#region src/conformance/runPageInsertBlankConformance.ts
|
|
4735
|
+
/** An on-purpose non-default size so the assertions can't pass by accident. */
|
|
4736
|
+
const SIZE = {
|
|
4737
|
+
width: 396,
|
|
4738
|
+
height: 612
|
|
4739
|
+
};
|
|
4740
|
+
/**
|
|
4741
|
+
* Blank-page insert conformance. `pages.insertBlank` is a REQUIRED member —
|
|
4742
|
+
* the suite runs unconditionally on every engine (no fixture bytes are
|
|
4743
|
+
* needed at all, which is the point of the verb), so an implementation that
|
|
4744
|
+
* loses the verb fails loudly instead of being skipped past.
|
|
4745
|
+
*
|
|
4746
|
+
* Invariants:
|
|
4747
|
+
* 1. `count` blank pages of exactly `size` appear at `destIndex` (omitted
|
|
4748
|
+
* → append), rotation 0; the result lists their fresh PONs in insertion
|
|
4749
|
+
* order and they agree with the returned layout.
|
|
4750
|
+
* 2. Pre-existing pages keep their identity: same PONs before and after,
|
|
4751
|
+
* in the expected positions (an insert never invalidates neighbours).
|
|
4752
|
+
* 3. The mutation persists through save → re-open (bytes engines only).
|
|
4753
|
+
* 4. Non-positive size / count outside [1, PAGE_INSERT_BLANK_MAX_COUNT] /
|
|
4754
|
+
* out-of-range destIndex reject with InvalidArg, leaving the document
|
|
4755
|
+
* untouched.
|
|
4756
|
+
*/
|
|
4757
|
+
function runPageInsertBlankConformance(runner, opts) {
|
|
4758
|
+
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
4759
|
+
describe(`page insert-blank conformance: ${opts.label}`, () => {
|
|
4760
|
+
let engine;
|
|
4761
|
+
beforeAll(async () => {
|
|
4762
|
+
engine = await opts.makeEngine();
|
|
4763
|
+
});
|
|
4764
|
+
afterAll(async () => {
|
|
4765
|
+
if (engine) await engine.destroy();
|
|
4766
|
+
});
|
|
4767
|
+
test("appends one blank page of the requested size with a fresh PON", async () => {
|
|
4768
|
+
const doc = await openFixture$6(engine, opts);
|
|
4769
|
+
try {
|
|
4770
|
+
const before = await doc.pages.list();
|
|
4771
|
+
const beforePons = before.pages.map((p) => p.pageObjectNumber);
|
|
4772
|
+
const result = await doc.pages.insertBlank({ size: SIZE });
|
|
4773
|
+
expect(result.insertedPageObjectNumbers.length).toBe(1);
|
|
4774
|
+
expect(result.layout.pageCount).toBe(before.pageCount + 1);
|
|
4775
|
+
expect(result.layout.pages.slice(0, before.pageCount).map((p) => p.pageObjectNumber)).toEqual(beforePons);
|
|
4776
|
+
const newPon = result.insertedPageObjectNumbers[0];
|
|
4777
|
+
expect(beforePons.includes(newPon)).toBe(false);
|
|
4778
|
+
const appended = result.layout.pages[before.pageCount];
|
|
4779
|
+
expect(appended.pageObjectNumber).toBe(newPon);
|
|
4780
|
+
expect(appended.size).toEqual(SIZE);
|
|
4781
|
+
expect(appended.rotation).toBe(0);
|
|
4782
|
+
} finally {
|
|
4783
|
+
await doc.close();
|
|
4784
|
+
}
|
|
4785
|
+
});
|
|
4786
|
+
test("destIndex + count places the blank block mid-document", async () => {
|
|
4787
|
+
const doc = await openFixture$6(engine, opts);
|
|
4788
|
+
try {
|
|
4789
|
+
const before = await doc.pages.list();
|
|
4790
|
+
if (before.pages.length < 2) return;
|
|
4791
|
+
const beforePons = before.pages.map((p) => p.pageObjectNumber);
|
|
4792
|
+
const result = await doc.pages.insertBlank({
|
|
4793
|
+
size: SIZE,
|
|
4794
|
+
count: 2
|
|
4795
|
+
}, 1);
|
|
4796
|
+
expect(result.insertedPageObjectNumbers.length).toBe(2);
|
|
4797
|
+
expect(result.insertedPageObjectNumbers[0] === result.insertedPageObjectNumbers[1]).toBe(false);
|
|
4798
|
+
const pons = result.layout.pages.map((p) => p.pageObjectNumber);
|
|
4799
|
+
expect(pons[0]).toBe(beforePons[0]);
|
|
4800
|
+
expect(pons.slice(1, 3)).toEqual(result.insertedPageObjectNumbers);
|
|
4801
|
+
expect(pons.slice(3)).toEqual(beforePons.slice(1));
|
|
4802
|
+
expect(result.layout.pages[1].size).toEqual(SIZE);
|
|
4803
|
+
expect(result.layout.pages[2].size).toEqual(SIZE);
|
|
4804
|
+
} finally {
|
|
4805
|
+
await doc.close();
|
|
4806
|
+
}
|
|
4807
|
+
});
|
|
4808
|
+
test("the blank pages persist through save → re-open", async () => {
|
|
4809
|
+
if (opts.openKind !== "bytes") return;
|
|
4810
|
+
const doc = await openFixture$6(engine, opts);
|
|
4811
|
+
let reopened = null;
|
|
4812
|
+
try {
|
|
4813
|
+
const before = await doc.pages.list();
|
|
4814
|
+
await doc.pages.insertBlank({ size: SIZE });
|
|
4815
|
+
const bytes = await doc.download();
|
|
4816
|
+
reopened = await engine.open({
|
|
4817
|
+
kind: "bytes",
|
|
4818
|
+
id: `${opts.fixture.id}-insert-blank-reopen`,
|
|
4819
|
+
bytes
|
|
4820
|
+
});
|
|
4821
|
+
const after = await reopened.pages.list();
|
|
4822
|
+
expect(after.pageCount).toBe(before.pageCount + 1);
|
|
4823
|
+
const tail = after.pages[after.pageCount - 1];
|
|
4824
|
+
expect(Math.round(tail.size.width)).toBe(SIZE.width);
|
|
4825
|
+
expect(Math.round(tail.size.height)).toBe(SIZE.height);
|
|
4826
|
+
} finally {
|
|
4827
|
+
if (reopened) await reopened.close();
|
|
4828
|
+
await doc.close();
|
|
4829
|
+
}
|
|
4830
|
+
});
|
|
4831
|
+
test("non-positive size, bad count, and out-of-range destIndex reject with InvalidArg", async () => {
|
|
4832
|
+
const doc = await openFixture$6(engine, opts);
|
|
4833
|
+
try {
|
|
4834
|
+
const before = await doc.pages.list();
|
|
4835
|
+
const rejects = [
|
|
4836
|
+
() => doc.pages.insertBlank({ size: {
|
|
4837
|
+
width: 0,
|
|
4838
|
+
height: 612
|
|
4839
|
+
} }),
|
|
4840
|
+
() => doc.pages.insertBlank({ size: {
|
|
4841
|
+
width: 396,
|
|
4842
|
+
height: -10
|
|
4843
|
+
} }),
|
|
4844
|
+
() => doc.pages.insertBlank({
|
|
4845
|
+
size: SIZE,
|
|
4846
|
+
count: 0
|
|
4847
|
+
}),
|
|
4848
|
+
() => doc.pages.insertBlank({
|
|
4849
|
+
size: SIZE,
|
|
4850
|
+
count: 101
|
|
4851
|
+
}),
|
|
4852
|
+
() => doc.pages.insertBlank({ size: SIZE }, before.pageCount + 1)
|
|
4853
|
+
];
|
|
4854
|
+
for (const attempt of rejects) {
|
|
4855
|
+
let caught;
|
|
4856
|
+
try {
|
|
4857
|
+
await attempt();
|
|
4858
|
+
} catch (err) {
|
|
4859
|
+
caught = err;
|
|
4860
|
+
}
|
|
4861
|
+
expect(EngineError.is(caught, EngineErrorCode.InvalidArg)).toBe(true);
|
|
4862
|
+
}
|
|
4863
|
+
const list = await doc.pages.list();
|
|
4864
|
+
expect(list.pageCount).toBe(before.pageCount);
|
|
4865
|
+
expect(list.pages.map((p) => p.pageObjectNumber)).toEqual(before.pages.map((p) => p.pageObjectNumber));
|
|
4866
|
+
} finally {
|
|
4867
|
+
await doc.close();
|
|
4868
|
+
}
|
|
4869
|
+
});
|
|
4870
|
+
});
|
|
4871
|
+
}
|
|
4872
|
+
async function openFixture$6(engine, opts) {
|
|
4613
4873
|
if (opts.openKind === "bytes") {
|
|
4614
4874
|
const bytes = await opts.fixture.bytes();
|
|
4615
4875
|
return engine.open({
|
|
@@ -4650,7 +4910,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4650
4910
|
let supported = false;
|
|
4651
4911
|
beforeAll(async () => {
|
|
4652
4912
|
engine = await opts.makeEngine();
|
|
4653
|
-
const probe = await openFixture$
|
|
4913
|
+
const probe = await openFixture$5(engine, opts);
|
|
4654
4914
|
supported = probe.pieceInfo !== void 0;
|
|
4655
4915
|
await probe.close();
|
|
4656
4916
|
});
|
|
@@ -4659,7 +4919,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4659
4919
|
});
|
|
4660
4920
|
test("doc-level: the full value vocabulary round-trips with tags intact", async () => {
|
|
4661
4921
|
if (!supported) return;
|
|
4662
|
-
const doc = await openFixture$
|
|
4922
|
+
const doc = await openFixture$5(engine, opts);
|
|
4663
4923
|
try {
|
|
4664
4924
|
await doc.pieceInfo.update(APP, {
|
|
4665
4925
|
name: "Standard Stamps",
|
|
@@ -4699,7 +4959,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4699
4959
|
});
|
|
4700
4960
|
test("page-level: the stamp schema round-trips on a page", async () => {
|
|
4701
4961
|
if (!supported) return;
|
|
4702
|
-
const doc = await openFixture$
|
|
4962
|
+
const doc = await openFixture$5(engine, opts);
|
|
4703
4963
|
try {
|
|
4704
4964
|
const list = await doc.pages.list();
|
|
4705
4965
|
const page = doc.page(list.pages[0].pageObjectNumber);
|
|
@@ -4725,7 +4985,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4725
4985
|
});
|
|
4726
4986
|
test("writes persist through save → re-open", async () => {
|
|
4727
4987
|
if (!supported || opts.openKind !== "bytes") return;
|
|
4728
|
-
const doc = await openFixture$
|
|
4988
|
+
const doc = await openFixture$5(engine, opts);
|
|
4729
4989
|
let reopened = null;
|
|
4730
4990
|
try {
|
|
4731
4991
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
@@ -4758,7 +5018,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4758
5018
|
});
|
|
4759
5019
|
test("null deletes a key; clear removes an entry; siblings survive", async () => {
|
|
4760
5020
|
if (!supported) return;
|
|
4761
|
-
const doc = await openFixture$
|
|
5021
|
+
const doc = await openFixture$5(engine, opts);
|
|
4762
5022
|
try {
|
|
4763
5023
|
await doc.pieceInfo.update(APP, {
|
|
4764
5024
|
name: "A",
|
|
@@ -4784,7 +5044,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4784
5044
|
});
|
|
4785
5045
|
test("an application that was never written reads as null", async () => {
|
|
4786
5046
|
if (!supported) return;
|
|
4787
|
-
const doc = await openFixture$
|
|
5047
|
+
const doc = await openFixture$5(engine, opts);
|
|
4788
5048
|
try {
|
|
4789
5049
|
expect(await doc.pieceInfo.read("EMBD_NeverWritten")).toBe(null);
|
|
4790
5050
|
const list = await doc.pages.list();
|
|
@@ -4795,7 +5055,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4795
5055
|
});
|
|
4796
5056
|
});
|
|
4797
5057
|
}
|
|
4798
|
-
async function openFixture$
|
|
5058
|
+
async function openFixture$5(engine, opts) {
|
|
4799
5059
|
if (opts.openKind === "bytes") {
|
|
4800
5060
|
const bytes = await opts.fixture.bytes();
|
|
4801
5061
|
return engine.open({
|
|
@@ -4858,7 +5118,7 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4858
5118
|
if (engine) await engine.destroy();
|
|
4859
5119
|
});
|
|
4860
5120
|
test("every confirmed mutation publishes exactly one event, results verbatim", async () => {
|
|
4861
|
-
const doc = await openFixture$
|
|
5121
|
+
const doc = await openFixture$4(engine, opts);
|
|
4862
5122
|
try {
|
|
4863
5123
|
const events = [];
|
|
4864
5124
|
doc.events.subscribe((event) => events.push(event));
|
|
@@ -4905,19 +5165,46 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4905
5165
|
expect(evDeleted.layout).toEqual(deleted.layout);
|
|
4906
5166
|
}
|
|
4907
5167
|
if (evMeta.type === "metadata.updated") expect(evMeta.metadata).toEqual(meta.metadata);
|
|
5168
|
+
const first = events[0];
|
|
5169
|
+
expect(first !== void 0 && "origin" in first).toBe(true);
|
|
5170
|
+
if (first === void 0 || !("origin" in first)) return;
|
|
4908
5171
|
for (const event of events) {
|
|
5172
|
+
expect("origin" in event).toBe(true);
|
|
5173
|
+
if (!("origin" in event)) continue;
|
|
4909
5174
|
expect(event.origin.kind).toBe("local");
|
|
4910
5175
|
expect(typeof event.origin.sessionId).toBe("string");
|
|
4911
5176
|
expect(event.origin.sessionId.length > 0).toBe(true);
|
|
4912
|
-
expect(event.origin.sessionId).toBe(
|
|
5177
|
+
expect(event.origin.sessionId).toBe(first.origin.sessionId);
|
|
4913
5178
|
expect(typeof event.origin.ts).toBe("number");
|
|
4914
5179
|
}
|
|
4915
5180
|
} finally {
|
|
4916
5181
|
await doc.close();
|
|
4917
5182
|
}
|
|
4918
5183
|
});
|
|
5184
|
+
test("pages.inserted rides verbatim", async () => {
|
|
5185
|
+
const doc = await openFixture$4(engine, opts);
|
|
5186
|
+
try {
|
|
5187
|
+
const events = [];
|
|
5188
|
+
doc.events.subscribe((event) => events.push(event));
|
|
5189
|
+
const inserted = await doc.pages.insertBlank({ size: {
|
|
5190
|
+
width: 396,
|
|
5191
|
+
height: 612
|
|
5192
|
+
} }, 0);
|
|
5193
|
+
expect(events.map((event) => event.type)).toEqual(["pages.inserted"]);
|
|
5194
|
+
const [evInserted] = events;
|
|
5195
|
+
if (evInserted.type === "pages.inserted") {
|
|
5196
|
+
expect(evInserted.insertedPageObjectNumbers).toEqual(inserted.insertedPageObjectNumbers);
|
|
5197
|
+
expect(evInserted.layout).toEqual(inserted.layout);
|
|
5198
|
+
expect(evInserted.cache).toEqual(inserted.cache);
|
|
5199
|
+
expect(evInserted.destIndex).toBe(0);
|
|
5200
|
+
expect(evInserted.origin.kind).toBe("local");
|
|
5201
|
+
}
|
|
5202
|
+
} finally {
|
|
5203
|
+
await doc.close();
|
|
5204
|
+
}
|
|
5205
|
+
});
|
|
4919
5206
|
test("a FAILED mutation publishes nothing (events are ground truth)", async () => {
|
|
4920
|
-
const doc = await openFixture$
|
|
5207
|
+
const doc = await openFixture$4(engine, opts);
|
|
4921
5208
|
try {
|
|
4922
5209
|
const events = [];
|
|
4923
5210
|
doc.events.subscribe((event) => events.push(event));
|
|
@@ -4935,7 +5222,7 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4935
5222
|
}
|
|
4936
5223
|
});
|
|
4937
5224
|
test("unsubscribe stops delivery", async () => {
|
|
4938
|
-
const doc = await openFixture$
|
|
5225
|
+
const doc = await openFixture$4(engine, opts);
|
|
4939
5226
|
try {
|
|
4940
5227
|
const events = [];
|
|
4941
5228
|
doc.events.subscribe((event) => events.push(event))();
|
|
@@ -4948,7 +5235,7 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4948
5235
|
});
|
|
4949
5236
|
});
|
|
4950
5237
|
}
|
|
4951
|
-
async function openFixture$
|
|
5238
|
+
async function openFixture$4(engine, opts) {
|
|
4952
5239
|
if (opts.openKind === "bytes") {
|
|
4953
5240
|
const bytes = await opts.fixture.bytes();
|
|
4954
5241
|
return engine.open({
|
|
@@ -4975,7 +5262,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
4975
5262
|
if (engine) await engine.destroy();
|
|
4976
5263
|
});
|
|
4977
5264
|
test("read() returns a PageTextSnapshot with non-empty text", async () => {
|
|
4978
|
-
const doc = await openFixture$
|
|
5265
|
+
const doc = await openFixture$3(engine, opts);
|
|
4979
5266
|
try {
|
|
4980
5267
|
const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
|
|
4981
5268
|
expect(PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
|
|
@@ -4986,7 +5273,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
4986
5273
|
}
|
|
4987
5274
|
});
|
|
4988
5275
|
test("extracted text contains the expected substring", async () => {
|
|
4989
|
-
const doc = await openFixture$
|
|
5276
|
+
const doc = await openFixture$3(engine, opts);
|
|
4990
5277
|
try {
|
|
4991
5278
|
const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
|
|
4992
5279
|
expect(snap.text.includes(opts.fixture.expectedSubstring)).toBe(true);
|
|
@@ -4995,7 +5282,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
4995
5282
|
}
|
|
4996
5283
|
});
|
|
4997
5284
|
test("read() on an unknown pageObjectNumber throws NotFound", async () => {
|
|
4998
|
-
const doc = await openFixture$
|
|
5285
|
+
const doc = await openFixture$3(engine, opts);
|
|
4999
5286
|
try {
|
|
5000
5287
|
let caught;
|
|
5001
5288
|
try {
|
|
@@ -5010,7 +5297,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
5010
5297
|
}
|
|
5011
5298
|
});
|
|
5012
5299
|
test("abort() on read rejects with AbortError", async () => {
|
|
5013
|
-
const doc = await openFixture$
|
|
5300
|
+
const doc = await openFixture$3(engine, opts);
|
|
5014
5301
|
try {
|
|
5015
5302
|
const p = doc.page(opts.fixture.pageObjectNumber).text.read();
|
|
5016
5303
|
p.abort("test");
|
|
@@ -5020,7 +5307,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
5020
5307
|
}
|
|
5021
5308
|
});
|
|
5022
5309
|
test("read after close throws DocNotOpen", async () => {
|
|
5023
|
-
const doc = await openFixture$
|
|
5310
|
+
const doc = await openFixture$3(engine, opts);
|
|
5024
5311
|
const page = doc.page(opts.fixture.pageObjectNumber);
|
|
5025
5312
|
await doc.close();
|
|
5026
5313
|
let caught;
|
|
@@ -5034,6 +5321,320 @@ function runPageTextConformance(runner, opts) {
|
|
|
5034
5321
|
});
|
|
5035
5322
|
});
|
|
5036
5323
|
}
|
|
5324
|
+
async function openFixture$3(engine, opts) {
|
|
5325
|
+
if (opts.openKind === "bytes") {
|
|
5326
|
+
const bytes = await opts.fixture.bytes();
|
|
5327
|
+
return engine.open({
|
|
5328
|
+
kind: "bytes",
|
|
5329
|
+
id: opts.fixture.id,
|
|
5330
|
+
bytes
|
|
5331
|
+
});
|
|
5332
|
+
}
|
|
5333
|
+
return engine.open({
|
|
5334
|
+
kind: "id",
|
|
5335
|
+
id: opts.fixture.cloudId ?? opts.fixture.id
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
//#endregion
|
|
5339
|
+
//#region src/conformance/runTextDivergenceConformance.ts
|
|
5340
|
+
function runTextDivergenceConformance(runner, opts) {
|
|
5341
|
+
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
5342
|
+
const fixture = opts.fixture;
|
|
5343
|
+
describe(`text divergence conformance: ${opts.label} [${fixture.id}]`, () => {
|
|
5344
|
+
let engine;
|
|
5345
|
+
beforeAll(async () => {
|
|
5346
|
+
engine = await opts.makeEngine();
|
|
5347
|
+
});
|
|
5348
|
+
afterAll(async () => {
|
|
5349
|
+
if (engine) await engine.destroy();
|
|
5350
|
+
});
|
|
5351
|
+
test("snapshot is schema-valid with the exact text, charCount, and charMap", async () => {
|
|
5352
|
+
const doc = await openFixture$2(engine, opts);
|
|
5353
|
+
try {
|
|
5354
|
+
const snap = await doc.page(fixture.pageObjectNumber).text.read();
|
|
5355
|
+
expect(PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
|
|
5356
|
+
expect(snap.text).toBe(fixture.exactText);
|
|
5357
|
+
expect(snap.charCount).toBe(fixture.expectedCharCount);
|
|
5358
|
+
const anchors = snap.charMap ?? null;
|
|
5359
|
+
if (fixture.expectedCharMap === null) expect(anchors === null || anchors.length === 0).toBe(true);
|
|
5360
|
+
else expect(anchors === null ? null : anchors.map((a) => [a[0], a[1]])).toEqual(fixture.expectedCharMap.map((a) => [a[0], a[1]]));
|
|
5361
|
+
} finally {
|
|
5362
|
+
await doc.close();
|
|
5363
|
+
}
|
|
5364
|
+
});
|
|
5365
|
+
test("char-space slicing round-trips through the map", async () => {
|
|
5366
|
+
const doc = await openFixture$2(engine, opts);
|
|
5367
|
+
try {
|
|
5368
|
+
const snap = await doc.page(fixture.pageObjectNumber).text.read();
|
|
5369
|
+
expect(sliceTextByChars(snap, 0, snap.charCount)).toBe(snap.text);
|
|
5370
|
+
for (const s of fixture.slices ?? []) expect(sliceTextByChars(snap, s.from, s.to)).toBe(s.text);
|
|
5371
|
+
} finally {
|
|
5372
|
+
await doc.close();
|
|
5373
|
+
}
|
|
5374
|
+
});
|
|
5375
|
+
if (fixture.search) {
|
|
5376
|
+
const probe = fixture.search;
|
|
5377
|
+
test("search hits address character space and join the text by slicing", async () => {
|
|
5378
|
+
const doc = await openFixture$2(engine, opts);
|
|
5379
|
+
try {
|
|
5380
|
+
const hit = (await doc.search.query({
|
|
5381
|
+
query: { text: probe.query },
|
|
5382
|
+
mode: "rects"
|
|
5383
|
+
})).matches.find((m) => m.pageObjectNumber === fixture.pageObjectNumber);
|
|
5384
|
+
expect(hit === void 0).toBe(false);
|
|
5385
|
+
expect(hit.charStart).toBe(probe.charStart);
|
|
5386
|
+
expect(hit.charCount).toBe(probe.charCount);
|
|
5387
|
+
expect(hit.segments.length > 0).toBe(true);
|
|
5388
|
+
const snap = await doc.page(fixture.pageObjectNumber).text.read();
|
|
5389
|
+
expect(sliceTextByChars(snap, hit.charStart, hit.charStart + hit.charCount)).toBe(probe.matchedText);
|
|
5390
|
+
} finally {
|
|
5391
|
+
await doc.close();
|
|
5392
|
+
}
|
|
5393
|
+
});
|
|
5394
|
+
}
|
|
5395
|
+
});
|
|
5396
|
+
}
|
|
5397
|
+
async function openFixture$2(engine, opts) {
|
|
5398
|
+
if (opts.openKind === "bytes") {
|
|
5399
|
+
const bytes = await opts.fixture.bytes();
|
|
5400
|
+
return engine.open({
|
|
5401
|
+
kind: "bytes",
|
|
5402
|
+
id: opts.fixture.id,
|
|
5403
|
+
bytes
|
|
5404
|
+
});
|
|
5405
|
+
}
|
|
5406
|
+
return engine.open({
|
|
5407
|
+
kind: "id",
|
|
5408
|
+
id: opts.fixture.cloudId ?? opts.fixture.id
|
|
5409
|
+
});
|
|
5410
|
+
}
|
|
5411
|
+
/**
|
|
5412
|
+
* THE pinned divergence cases — one table, consumed by both engines' suites
|
|
5413
|
+
* so expected values cannot drift. Values verified against the wasm runtime
|
|
5414
|
+
* (see each case). All fixtures are single-page; the page's indirect object
|
|
5415
|
+
* number is 3 in each.
|
|
5416
|
+
*/
|
|
5417
|
+
const TEXT_DIVERGENCE_CASES = {
|
|
5418
|
+
/** Leading NON-PRINTING character: 31 character slots, 30 text units —
|
|
5419
|
+
* every text offset shifts by one, encoded by the single anchor [1, 0].
|
|
5420
|
+
* The searchex embeddertest for this fixture pins the same +1 shift. */
|
|
5421
|
+
bug1139: {
|
|
5422
|
+
resource: "bug_1139.pdf",
|
|
5423
|
+
pageObjectNumber: 3,
|
|
5424
|
+
exactText: "Hello, world!\r\nGoodbye, world!",
|
|
5425
|
+
expectedCharCount: 31,
|
|
5426
|
+
expectedCharMap: [[1, 0]],
|
|
5427
|
+
slices: [
|
|
5428
|
+
{
|
|
5429
|
+
from: 0,
|
|
5430
|
+
to: 1,
|
|
5431
|
+
text: ""
|
|
5432
|
+
},
|
|
5433
|
+
{
|
|
5434
|
+
from: 1,
|
|
5435
|
+
to: 7,
|
|
5436
|
+
text: "Hello,"
|
|
5437
|
+
},
|
|
5438
|
+
{
|
|
5439
|
+
from: 16,
|
|
5440
|
+
to: 23,
|
|
5441
|
+
text: "Goodbye"
|
|
5442
|
+
}
|
|
5443
|
+
],
|
|
5444
|
+
search: {
|
|
5445
|
+
query: "Goodbye",
|
|
5446
|
+
charStart: 16,
|
|
5447
|
+
charCount: 7,
|
|
5448
|
+
matchedText: "Goodbye"
|
|
5449
|
+
}
|
|
5450
|
+
},
|
|
5451
|
+
/** `/ActualText` replacement: the span's real glyphs are REPLACED by one
|
|
5452
|
+
* synthetic kPiece character per ActualText character, lockstep with the
|
|
5453
|
+
* text (identity map), with synthesized evenly-divided boxes — so search
|
|
5454
|
+
* segments exist and extraction yields the ActualText, not the glyphs. */
|
|
5455
|
+
actualText: {
|
|
5456
|
+
resource: "bug_384770169.pdf",
|
|
5457
|
+
pageObjectNumber: 3,
|
|
5458
|
+
exactText: "What is my favorite food?",
|
|
5459
|
+
expectedCharCount: 25,
|
|
5460
|
+
expectedCharMap: null,
|
|
5461
|
+
slices: [{
|
|
5462
|
+
from: 11,
|
|
5463
|
+
to: 19,
|
|
5464
|
+
text: "favorite"
|
|
5465
|
+
}],
|
|
5466
|
+
search: {
|
|
5467
|
+
query: "favorite",
|
|
5468
|
+
charStart: 11,
|
|
5469
|
+
charCount: 8,
|
|
5470
|
+
matchedText: "favorite"
|
|
5471
|
+
}
|
|
5472
|
+
},
|
|
5473
|
+
/** Supplementary-plane text via a ToUnicode surrogate pair: PDFium stores
|
|
5474
|
+
* one character-list entry PER SURROGATE HALF in lockstep with the text
|
|
5475
|
+
* (identity map, no divergence in this representation) — and the emoji
|
|
5476
|
+
* SURVIVES extraction end to end. The combined-astral representation
|
|
5477
|
+
* (font-cmap path, one entry per code point → a [+2] anchor) is covered
|
|
5478
|
+
* by the charmap unit tests and the reader's divergent walk. */
|
|
5479
|
+
astralToUnicode: {
|
|
5480
|
+
resource: "embedpdf_astral_tounicode.pdf",
|
|
5481
|
+
pageObjectNumber: 3,
|
|
5482
|
+
exactText: "😀B!",
|
|
5483
|
+
expectedCharCount: 4,
|
|
5484
|
+
expectedCharMap: null,
|
|
5485
|
+
slices: [{
|
|
5486
|
+
from: 0,
|
|
5487
|
+
to: 2,
|
|
5488
|
+
text: "😀"
|
|
5489
|
+
}, {
|
|
5490
|
+
from: 2,
|
|
5491
|
+
to: 3,
|
|
5492
|
+
text: "B"
|
|
5493
|
+
}],
|
|
5494
|
+
search: {
|
|
5495
|
+
query: "B!",
|
|
5496
|
+
charStart: 2,
|
|
5497
|
+
charCount: 2,
|
|
5498
|
+
matchedText: "B!"
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
};
|
|
5502
|
+
//#endregion
|
|
5503
|
+
//#region src/conformance/runPageGeometryOrientationConformance.ts
|
|
5504
|
+
const FLAG_EMPTY = 2;
|
|
5505
|
+
/** Absolute tolerance for coordinate assertions (PDF points). */
|
|
5506
|
+
const COORD_TOLERANCE = .001;
|
|
5507
|
+
/** Tolerance for baseline-angle assertions (radians). */
|
|
5508
|
+
const ANGLE_TOLERANCE = .001;
|
|
5509
|
+
function runPageGeometryOrientationConformance(runner, opts) {
|
|
5510
|
+
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
5511
|
+
describe(`page geometry orientation conformance: ${opts.label} [${opts.fixture.id}]`, () => {
|
|
5512
|
+
let engine;
|
|
5513
|
+
beforeAll(async () => {
|
|
5514
|
+
engine = await opts.makeEngine();
|
|
5515
|
+
});
|
|
5516
|
+
afterAll(async () => {
|
|
5517
|
+
if (engine) await engine.destroy();
|
|
5518
|
+
});
|
|
5519
|
+
const readSnapshot = async () => {
|
|
5520
|
+
const doc = await openFixture$1(engine, opts);
|
|
5521
|
+
return {
|
|
5522
|
+
snapshot: await doc.page(opts.fixture.pageObjectNumber).geometry.read(),
|
|
5523
|
+
close: () => doc.close()
|
|
5524
|
+
};
|
|
5525
|
+
};
|
|
5526
|
+
test("snapshot round-trips the wire schema", async () => {
|
|
5527
|
+
const { snapshot, close } = await readSnapshot();
|
|
5528
|
+
try {
|
|
5529
|
+
const parsed = PageGeometrySnapshotSchema.safeParse(snapshot);
|
|
5530
|
+
expect(parsed.success).toBe(true);
|
|
5531
|
+
if (parsed.success) expect(parsed.data).toEqual(snapshot);
|
|
5532
|
+
} finally {
|
|
5533
|
+
await close();
|
|
5534
|
+
}
|
|
5535
|
+
});
|
|
5536
|
+
test("run charStart indices tile the page glyph sequence", async () => {
|
|
5537
|
+
const { snapshot, close } = await readSnapshot();
|
|
5538
|
+
try {
|
|
5539
|
+
let next = 0;
|
|
5540
|
+
for (const run of snapshot.runs) {
|
|
5541
|
+
expect(run.charStart).toBe(next);
|
|
5542
|
+
next += run.glyphs.length;
|
|
5543
|
+
}
|
|
5544
|
+
} finally {
|
|
5545
|
+
await close();
|
|
5546
|
+
}
|
|
5547
|
+
});
|
|
5548
|
+
test("runs match the fixture orientation expectation", async () => {
|
|
5549
|
+
const { snapshot, close } = await readSnapshot();
|
|
5550
|
+
try {
|
|
5551
|
+
const expectation = opts.fixture.expectation;
|
|
5552
|
+
if (expectation.kind === "upright") {
|
|
5553
|
+
for (const run of snapshot.runs) {
|
|
5554
|
+
expect(isRotatedGeometryRun(run)).toBe(false);
|
|
5555
|
+
if (isRotatedGeometryRun(run)) continue;
|
|
5556
|
+
for (const glyph of run.glyphs) {
|
|
5557
|
+
expect("looseQuad" in glyph).toBe(false);
|
|
5558
|
+
if (glyph.flags & FLAG_EMPTY) expect(glyph.looseBox).toEqual({
|
|
5559
|
+
left: 0,
|
|
5560
|
+
bottom: 0,
|
|
5561
|
+
right: 0,
|
|
5562
|
+
top: 0
|
|
5563
|
+
});
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
return;
|
|
5567
|
+
}
|
|
5568
|
+
if (expectation.kind === "empty-only") {
|
|
5569
|
+
for (const run of snapshot.runs) {
|
|
5570
|
+
expect(isRotatedGeometryRun(run)).toBe(false);
|
|
5571
|
+
if (isRotatedGeometryRun(run)) continue;
|
|
5572
|
+
for (const glyph of run.glyphs) {
|
|
5573
|
+
expect(glyph.flags & FLAG_EMPTY).toBe(FLAG_EMPTY);
|
|
5574
|
+
expect(glyph.looseBox).toEqual({
|
|
5575
|
+
left: 0,
|
|
5576
|
+
bottom: 0,
|
|
5577
|
+
right: 0,
|
|
5578
|
+
top: 0
|
|
5579
|
+
});
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
return;
|
|
5583
|
+
}
|
|
5584
|
+
const rotatedRuns = snapshot.runs.filter(isRotatedGeometryRun);
|
|
5585
|
+
expect(rotatedRuns.length >= (expectation.minRotatedRuns ?? 1)).toBe(true);
|
|
5586
|
+
for (const run of rotatedRuns) {
|
|
5587
|
+
expect(expectation.rotations.some((angle) => angleClose(run.rotation, angle, ANGLE_TOLERANCE))).toBe(true);
|
|
5588
|
+
expect(run.ascentFlip).toBe(expectation.ascentFlip);
|
|
5589
|
+
assertRotatedRunGeometry(run);
|
|
5590
|
+
}
|
|
5591
|
+
if (expectation.sheared) {
|
|
5592
|
+
const shearedGlyphs = rotatedRuns.flatMap((run) => run.glyphs).filter((glyph) => (glyph.flags & FLAG_EMPTY) === 0 && Math.abs(glyph.looseQuad.p3.x - glyph.looseQuad.p1.x) > 1);
|
|
5593
|
+
expect(shearedGlyphs.length > 0).toBe(true);
|
|
5594
|
+
}
|
|
5595
|
+
} finally {
|
|
5596
|
+
await close();
|
|
5597
|
+
}
|
|
5598
|
+
});
|
|
5599
|
+
function assertRotatedRunGeometry(run) {
|
|
5600
|
+
for (const glyph of run.glyphs) {
|
|
5601
|
+
if (glyph.flags & FLAG_EMPTY) {
|
|
5602
|
+
expect(glyph.looseQuad).toEqual({
|
|
5603
|
+
p1: {
|
|
5604
|
+
x: 0,
|
|
5605
|
+
y: 0
|
|
5606
|
+
},
|
|
5607
|
+
p2: {
|
|
5608
|
+
x: 0,
|
|
5609
|
+
y: 0
|
|
5610
|
+
},
|
|
5611
|
+
p3: {
|
|
5612
|
+
x: 0,
|
|
5613
|
+
y: 0
|
|
5614
|
+
},
|
|
5615
|
+
p4: {
|
|
5616
|
+
x: 0,
|
|
5617
|
+
y: 0
|
|
5618
|
+
}
|
|
5619
|
+
});
|
|
5620
|
+
continue;
|
|
5621
|
+
}
|
|
5622
|
+
const q = glyph.looseQuad;
|
|
5623
|
+
expect(Math.abs(q.p2.x - q.p1.x - (q.p4.x - q.p3.x)) <= COORD_TOLERANCE).toBe(true);
|
|
5624
|
+
expect(Math.abs(q.p2.y - q.p1.y - (q.p4.y - q.p3.y)) <= COORD_TOLERANCE).toBe(true);
|
|
5625
|
+
const bounds = pdfQuadBounds(q);
|
|
5626
|
+
expect(bounds.left >= run.rect.left - COORD_TOLERANCE).toBe(true);
|
|
5627
|
+
expect(bounds.right <= run.rect.right + COORD_TOLERANCE).toBe(true);
|
|
5628
|
+
expect(bounds.bottom >= run.rect.bottom - COORD_TOLERANCE).toBe(true);
|
|
5629
|
+
expect(bounds.top <= run.rect.top + COORD_TOLERANCE).toBe(true);
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
});
|
|
5633
|
+
}
|
|
5634
|
+
function angleClose(a, b, tolerance) {
|
|
5635
|
+
const delta = a - b;
|
|
5636
|
+
return Math.abs(Math.atan2(Math.sin(delta), Math.cos(delta))) <= tolerance;
|
|
5637
|
+
}
|
|
5037
5638
|
async function openFixture$1(engine, opts) {
|
|
5038
5639
|
if (opts.openKind === "bytes") {
|
|
5039
5640
|
const bytes = await opts.fixture.bytes();
|
|
@@ -5089,10 +5690,27 @@ function runSearchConformance(runner, opts) {
|
|
|
5089
5690
|
expect(matches.some((m) => m.pageObjectNumber === fixture.presentPageObjectNumber)).toBe(true);
|
|
5090
5691
|
for (const m of matches) {
|
|
5091
5692
|
expect(m.charCount > 0).toBe(true);
|
|
5092
|
-
expect(m.
|
|
5093
|
-
for (const
|
|
5094
|
-
expect(
|
|
5095
|
-
expect(
|
|
5693
|
+
expect(m.segments.length > 0).toBe(true);
|
|
5694
|
+
for (const s of m.segments) {
|
|
5695
|
+
expect(s.rect.right > s.rect.left).toBe(true);
|
|
5696
|
+
expect(s.rect.top > s.rect.bottom).toBe(true);
|
|
5697
|
+
const xs = [
|
|
5698
|
+
s.quad.p1.x,
|
|
5699
|
+
s.quad.p2.x,
|
|
5700
|
+
s.quad.p3.x,
|
|
5701
|
+
s.quad.p4.x
|
|
5702
|
+
];
|
|
5703
|
+
const ys = [
|
|
5704
|
+
s.quad.p1.y,
|
|
5705
|
+
s.quad.p2.y,
|
|
5706
|
+
s.quad.p3.y,
|
|
5707
|
+
s.quad.p4.y
|
|
5708
|
+
];
|
|
5709
|
+
expect(Math.abs(s.rect.left - Math.min(...xs)) < .001).toBe(true);
|
|
5710
|
+
expect(Math.abs(s.rect.right - Math.max(...xs)) < .001).toBe(true);
|
|
5711
|
+
expect(Math.abs(s.rect.bottom - Math.min(...ys)) < .001).toBe(true);
|
|
5712
|
+
expect(Math.abs(s.rect.top - Math.max(...ys)) < .001).toBe(true);
|
|
5713
|
+
expect(s.advance === 1 || s.advance === -1).toBe(true);
|
|
5096
5714
|
}
|
|
5097
5715
|
}
|
|
5098
5716
|
} finally {
|
|
@@ -5224,7 +5842,7 @@ function runSearchConformance(runner, opts) {
|
|
|
5224
5842
|
expect(matches.length > 0).toBe(true);
|
|
5225
5843
|
for (const m of matches) {
|
|
5226
5844
|
expect(m.charCount > 0).toBe(true);
|
|
5227
|
-
expect(m.
|
|
5845
|
+
expect(m.segments.length > 0).toBe(true);
|
|
5228
5846
|
}
|
|
5229
5847
|
} finally {
|
|
5230
5848
|
await doc.close();
|
|
@@ -5351,6 +5969,8 @@ async function openFixture(engine, opts) {
|
|
|
5351
5969
|
* are disjoint. We do compare `pageObjectNumber` and `generation` (which
|
|
5352
5970
|
* should be 0 for fresh reads on both sides). Page order is no longer part
|
|
5353
5971
|
* of `PageState`; it lives in `PageLayout.index` (see `pages.list()`).
|
|
5972
|
+
* `auditHead` is likewise not compared: it is a cloud-only reconciliation
|
|
5973
|
+
* cursor, absent on local snapshots by design.
|
|
5354
5974
|
*/
|
|
5355
5975
|
function diffAnnotationListSnapshot(a, b) {
|
|
5356
5976
|
const errs = [];
|
|
@@ -5389,6 +6009,6 @@ function diffAnnotation(i, a, b, errs) {
|
|
|
5389
6009
|
if (a.nm !== b.nm) errs.push(`annotations[${i}].nm mismatch: ${a.nm} vs ${b.nm}`);
|
|
5390
6010
|
}
|
|
5391
6011
|
//#endregion
|
|
5392
|
-
export { diffAnnotationListSnapshot, diffAnnotationListSnapshotAll, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationMutationConformance, runAnnotationReadConformance, runAttachmentConformance, runDocumentEventsConformance, runFormConformance, runMetadataConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageInsertConformance, runPageReorderConformance, runPageRotateConformance, runPageTextConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance };
|
|
6012
|
+
export { TEXT_DIVERGENCE_CASES, diffAnnotationListSnapshot, diffAnnotationListSnapshotAll, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationMutationConformance, runAnnotationReadConformance, runAttachmentConformance, runDocumentEventsConformance, runFormConformance, runMetadataConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageGeometryOrientationConformance, runPageInsertBlankConformance, runPageInsertConformance, runPageReorderConformance, runPageRotateConformance, runPageTextConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance, runTextDivergenceConformance };
|
|
5393
6013
|
|
|
5394
6014
|
//# sourceMappingURL=conformance.js.map
|