@embedpdf/engine-core 3.0.0-next.1 → 3.0.0-next.11
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-C1U3XUxP.d.cts} +846 -127
- package/dist/{AnnotationListSnapshot-W0RCsyDE.d.ts → AnnotationListSnapshot-C1U3XUxP.d.ts} +846 -127
- package/dist/{DocumentSecurityService-Df79fgpe.d.cts → DocumentSecurityService-6rhztZlz.d.cts} +33 -2
- package/dist/{DocumentSecurityService-B-tULKNY.d.ts → DocumentSecurityService-7nL9aY75.d.ts} +33 -2
- package/dist/{Engine-B7Oe7f5i.d.ts → Engine-CWnAKccD.d.ts} +62 -19
- package/dist/{Engine-D9loFZc1.d.cts → Engine-D3868xIU.d.cts} +62 -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-Cdz34nbF.js} +566 -164
- package/dist/builders-Cdz34nbF.js.map +1 -0
- package/dist/{builders-BvikWRuO.cjs → builders-Do6_4KnS.cjs} +628 -220
- package/dist/builders-Do6_4KnS.cjs.map +1 -0
- package/dist/conformance.cjs +1039 -204
- package/dist/conformance.cjs.map +1 -1
- package/dist/conformance.d.cts +130 -6
- package/dist/conformance.d.ts +130 -6
- package/dist/conformance.js +994 -163
- 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 +38 -19
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/{resolver-DiPMTfcy.js → resolver-CHG4waXL.js} +2 -1
- package/dist/resolver-CHG4waXL.js.map +1 -0
- package/dist/{resolver-DAZWVoJL.cjs → resolver-D-jumvgM.cjs} +2 -1
- package/dist/resolver-D-jumvgM.cjs.map +1 -0
- package/dist/runtime.cjs +43 -24
- 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 +6 -6
- package/dist/{schemas-B_1VyFsm.cjs → schemas-CS5iPpX5.cjs} +305 -111
- package/dist/schemas-CS5iPpX5.cjs.map +1 -0
- package/dist/{schemas-9OpSSeM6.js → schemas-XGrNVMDd.js} +250 -92
- package/dist/schemas-XGrNVMDd.js.map +1 -0
- package/dist/wire.cjs +131 -4
- package/dist/wire.cjs.map +1 -1
- package/dist/wire.d.cts +277 -20
- package/dist/wire.d.ts +277 -20
- package/dist/wire.js +120 -5
- 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/resolver-DAZWVoJL.cjs.map +0 -1
- package/dist/resolver-DiPMTfcy.js.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, mt as SearchSliceSchema, nt as PageRotateResultSchema, o as AnnotationDeleteResultSchema, pr as DocumentActionsSnapshotSchema, st as RedactionApplyResultSchema, u as AnnotationMoveResultSchema } from "./schemas-XGrNVMDd.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({
|
|
@@ -368,6 +369,13 @@ function emptyFlags() {
|
|
|
368
369
|
}
|
|
369
370
|
//#endregion
|
|
370
371
|
//#region src/conformance/runActionsConformance.ts
|
|
372
|
+
/** The script payload rides only the javascript/rendition arms. */
|
|
373
|
+
function scriptOf(node) {
|
|
374
|
+
if (!node) return "";
|
|
375
|
+
if (node.type === "javascript") return node.script;
|
|
376
|
+
if (node.type === "rendition") return node.script ?? "";
|
|
377
|
+
return "";
|
|
378
|
+
}
|
|
371
379
|
/** Action extraction parity. These tests assert data only; no script executes. */
|
|
372
380
|
function runActionsConformance(runner, opts) {
|
|
373
381
|
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
@@ -387,10 +395,10 @@ function runActionsConformance(runner, opts) {
|
|
|
387
395
|
expect(DocumentActionsSnapshotSchema.safeParse(snapshot).success).toBe(true);
|
|
388
396
|
expect(snapshot.openAction).toBeNull();
|
|
389
397
|
expect(snapshot.willSave?.root?.type).toBe("javascript");
|
|
390
|
-
expect(snapshot.willSave?.root
|
|
391
|
-
expect(snapshot.didSave?.root
|
|
392
|
-
expect(snapshot.willPrint?.root
|
|
393
|
-
expect(snapshot.didPrint?.root
|
|
398
|
+
expect(scriptOf(snapshot.willSave?.root)).toMatch(/Will Save/);
|
|
399
|
+
expect(scriptOf(snapshot.didSave?.root)).toMatch(/Did Save/);
|
|
400
|
+
expect(scriptOf(snapshot.willPrint?.root)).toMatch(/Will Print/);
|
|
401
|
+
expect(scriptOf(snapshot.didPrint?.root)).toMatch(/Did Print/);
|
|
394
402
|
const form = await doc.forms.list();
|
|
395
403
|
expect(form.calculationOrder).toEqual([{
|
|
396
404
|
kind: "objectNumber",
|
|
@@ -437,7 +445,7 @@ function runActionsConformance(runner, opts) {
|
|
|
437
445
|
expect(link?.subtype).toBe("link");
|
|
438
446
|
if (link?.subtype === "link") expect(link.target).toEqual({ kind: "javascript" });
|
|
439
447
|
expect(link?.actions?.activate?.root?.type).toBe("javascript");
|
|
440
|
-
expect(link?.actions?.activate?.root
|
|
448
|
+
expect(scriptOf(link?.actions?.activate?.root)).toMatch(/app\.alert/);
|
|
441
449
|
} finally {
|
|
442
450
|
await doc.close();
|
|
443
451
|
}
|
|
@@ -448,7 +456,7 @@ function runActionsConformance(runner, opts) {
|
|
|
448
456
|
const form = await doc.forms.list();
|
|
449
457
|
expect(form.fields).toHaveLength(1);
|
|
450
458
|
expect(form.fields[0].actions?.format?.root?.type).toBe("javascript");
|
|
451
|
-
expect(form.fields[0].actions?.format?.root
|
|
459
|
+
expect(scriptOf(form.fields[0].actions?.format?.root)).toMatch(/AFDate_FormatEx/);
|
|
452
460
|
const page = (await doc.pages.list()).pages[0];
|
|
453
461
|
const widget = (await doc.page(page.pageObjectNumber).annotations.list()).annotations[0];
|
|
454
462
|
expect(widget.actions).toBe(void 0);
|
|
@@ -456,6 +464,210 @@ function runActionsConformance(runner, opts) {
|
|
|
456
464
|
await doc.close();
|
|
457
465
|
}
|
|
458
466
|
});
|
|
467
|
+
test("carries interpreter payloads on every executable node", async () => {
|
|
468
|
+
const doc = await open(engine, opts, opts.fixtures.payloads);
|
|
469
|
+
try {
|
|
470
|
+
const page = (await doc.pages.list()).pages[0];
|
|
471
|
+
const snapshot = await doc.page(page.pageObjectNumber).annotations.list();
|
|
472
|
+
const pon = page.pageObjectNumber;
|
|
473
|
+
const rootOf = (nm) => {
|
|
474
|
+
const annotation = snapshot.annotations.find((candidate) => candidate.nm === nm);
|
|
475
|
+
expect(Boolean(annotation)).toBe(true);
|
|
476
|
+
return annotation?.actions?.activate?.root ?? null;
|
|
477
|
+
};
|
|
478
|
+
const targetOf = (nm) => {
|
|
479
|
+
const annotation = snapshot.annotations.find((candidate) => candidate.nm === nm);
|
|
480
|
+
return annotation?.subtype === "link" ? annotation.target : null;
|
|
481
|
+
};
|
|
482
|
+
expect(rootOf("goto-fitr")).toMatchObject({
|
|
483
|
+
type: "goto",
|
|
484
|
+
destination: {
|
|
485
|
+
kind: "fitR",
|
|
486
|
+
pageObjectNumber: pon,
|
|
487
|
+
left: 10,
|
|
488
|
+
bottom: 20,
|
|
489
|
+
right: 300,
|
|
490
|
+
top: 400
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
expect(targetOf("goto-fitr")).toMatchObject({
|
|
494
|
+
kind: "goto",
|
|
495
|
+
destination: { kind: "fitR" }
|
|
496
|
+
});
|
|
497
|
+
expect(rootOf("uri-map")).toMatchObject({
|
|
498
|
+
type: "uri",
|
|
499
|
+
uri: "https://example.test/map",
|
|
500
|
+
isMap: true
|
|
501
|
+
});
|
|
502
|
+
expect(rootOf("named-next")).toMatchObject({
|
|
503
|
+
type: "named",
|
|
504
|
+
name: "NextPage"
|
|
505
|
+
});
|
|
506
|
+
const mixed = rootOf("hide-mixed");
|
|
507
|
+
expect(mixed).toMatchObject({
|
|
508
|
+
type: "hide",
|
|
509
|
+
hide: false
|
|
510
|
+
});
|
|
511
|
+
const mixedTargets = mixed?.type === "hide" ? mixed.targets : [];
|
|
512
|
+
expect(mixedTargets).toHaveLength(2);
|
|
513
|
+
expect(mixedTargets[0]).toEqual({
|
|
514
|
+
kind: "name",
|
|
515
|
+
name: "note1"
|
|
516
|
+
});
|
|
517
|
+
expect(mixedTargets[1]?.kind === "objectNumber" && mixedTargets[1].objectNumber > 0).toBe(true);
|
|
518
|
+
expect(rootOf("hide-scalar")).toMatchObject({
|
|
519
|
+
type: "hide",
|
|
520
|
+
targets: [{
|
|
521
|
+
kind: "name",
|
|
522
|
+
name: "fieldB"
|
|
523
|
+
}],
|
|
524
|
+
hide: true
|
|
525
|
+
});
|
|
526
|
+
expect(rootOf("reset-include")).toMatchObject({
|
|
527
|
+
type: "reset-form",
|
|
528
|
+
fields: [{
|
|
529
|
+
kind: "name",
|
|
530
|
+
name: "calc1"
|
|
531
|
+
}],
|
|
532
|
+
exclude: true
|
|
533
|
+
});
|
|
534
|
+
expect(rootOf("reset-absent")).toMatchObject({
|
|
535
|
+
type: "reset-form",
|
|
536
|
+
fields: null,
|
|
537
|
+
exclude: true
|
|
538
|
+
});
|
|
539
|
+
expect(rootOf("reset-empty")).toMatchObject({
|
|
540
|
+
type: "reset-form",
|
|
541
|
+
fields: [],
|
|
542
|
+
exclude: false
|
|
543
|
+
});
|
|
544
|
+
expect(rootOf("submit-urlspec")).toMatchObject({
|
|
545
|
+
type: "submit-form",
|
|
546
|
+
payload: {
|
|
547
|
+
url: "https://uf.example.test/submit",
|
|
548
|
+
fields: [{
|
|
549
|
+
kind: "name",
|
|
550
|
+
name: "parent"
|
|
551
|
+
}],
|
|
552
|
+
flags: {
|
|
553
|
+
raw: 0,
|
|
554
|
+
exclude: false,
|
|
555
|
+
format: "fdf",
|
|
556
|
+
method: "post"
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
expect(rootOf("submit-compat-xfdf")).toMatchObject({
|
|
561
|
+
type: "submit-form",
|
|
562
|
+
payload: {
|
|
563
|
+
url: "https://example.test/xfdf",
|
|
564
|
+
fields: [{
|
|
565
|
+
kind: "name",
|
|
566
|
+
name: "noexport"
|
|
567
|
+
}, {
|
|
568
|
+
kind: "name",
|
|
569
|
+
name: "plain"
|
|
570
|
+
}],
|
|
571
|
+
flags: {
|
|
572
|
+
raw: 35,
|
|
573
|
+
exclude: true,
|
|
574
|
+
includeNoValueFields: true,
|
|
575
|
+
format: "xfdf",
|
|
576
|
+
method: "post"
|
|
577
|
+
},
|
|
578
|
+
charSet: "utf-8"
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
expect(rootOf("submit-pdf-get")).toMatchObject({
|
|
582
|
+
type: "submit-form",
|
|
583
|
+
payload: {
|
|
584
|
+
url: "https://example.test/pdf",
|
|
585
|
+
fields: null,
|
|
586
|
+
flags: {
|
|
587
|
+
raw: 264,
|
|
588
|
+
format: "pdf",
|
|
589
|
+
method: "get"
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
expect(rootOf("launch-app")).toMatchObject({
|
|
594
|
+
type: "launch",
|
|
595
|
+
filePath: "app.exe"
|
|
596
|
+
});
|
|
597
|
+
expect(rootOf("gotor-file")).toMatchObject({
|
|
598
|
+
type: "goto-remote",
|
|
599
|
+
filePath: "other.pdf"
|
|
600
|
+
});
|
|
601
|
+
const chain = rootOf("chain-js-goto-hide");
|
|
602
|
+
expect(chain).toMatchObject({
|
|
603
|
+
type: "javascript",
|
|
604
|
+
script: "app.alert('chain');"
|
|
605
|
+
});
|
|
606
|
+
expect(chain?.next[0]).toMatchObject({
|
|
607
|
+
type: "goto",
|
|
608
|
+
destination: {
|
|
609
|
+
kind: "xyz",
|
|
610
|
+
pageObjectNumber: pon,
|
|
611
|
+
left: 5,
|
|
612
|
+
top: 10,
|
|
613
|
+
zoom: 1.25
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
expect(chain?.next[0]?.next[0]).toMatchObject({
|
|
617
|
+
type: "hide",
|
|
618
|
+
targets: [{
|
|
619
|
+
kind: "name",
|
|
620
|
+
name: "note1"
|
|
621
|
+
}],
|
|
622
|
+
hide: true
|
|
623
|
+
});
|
|
624
|
+
} finally {
|
|
625
|
+
await doc.close();
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
test("degrades unreadable payloads to unknown with payload-dropped", async () => {
|
|
629
|
+
const doc = await open(engine, opts, opts.fixtures.payloads);
|
|
630
|
+
try {
|
|
631
|
+
const page = (await doc.pages.list()).pages[0];
|
|
632
|
+
const snapshot = await doc.page(page.pageObjectNumber).annotations.list();
|
|
633
|
+
for (const [nm, subtype] of [
|
|
634
|
+
["goto-malformed", "GoTo"],
|
|
635
|
+
["hide-partial", "Hide"],
|
|
636
|
+
["submit-not-url", "SubmitForm"],
|
|
637
|
+
["submit-no-f", "SubmitForm"]
|
|
638
|
+
]) {
|
|
639
|
+
const annotation = snapshot.annotations.find((candidate) => candidate.nm === nm);
|
|
640
|
+
const tree = annotation?.actions?.activate;
|
|
641
|
+
expect(tree?.root).toMatchObject({
|
|
642
|
+
type: "unknown",
|
|
643
|
+
subtype
|
|
644
|
+
});
|
|
645
|
+
expect(tree?.warnings ?? []).toContain("payload-dropped");
|
|
646
|
+
if (annotation?.subtype === "link") expect(annotation.target).toEqual({ kind: "unsupported" });
|
|
647
|
+
}
|
|
648
|
+
} finally {
|
|
649
|
+
await doc.close();
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
test("reads a destination-form /OpenAction as openDestination", async () => {
|
|
653
|
+
const doc = await open(engine, opts, opts.fixtures.openDestination);
|
|
654
|
+
try {
|
|
655
|
+
expect(Boolean(doc.actions)).toBe(true);
|
|
656
|
+
const snapshot = await doc.actions.read();
|
|
657
|
+
expect(DocumentActionsSnapshotSchema.safeParse(snapshot).success).toBe(true);
|
|
658
|
+
expect(snapshot.openAction).toBeNull();
|
|
659
|
+
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
660
|
+
expect(snapshot.openDestination).toEqual({
|
|
661
|
+
kind: "xyz",
|
|
662
|
+
pageObjectNumber: pon,
|
|
663
|
+
left: 10,
|
|
664
|
+
top: 700,
|
|
665
|
+
zoom: 1.5
|
|
666
|
+
});
|
|
667
|
+
} finally {
|
|
668
|
+
await doc.close();
|
|
669
|
+
}
|
|
670
|
+
});
|
|
459
671
|
});
|
|
460
672
|
}
|
|
461
673
|
async function open(engine, opts, fixture) {
|
|
@@ -596,7 +808,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
596
808
|
if (engine) await engine.destroy();
|
|
597
809
|
});
|
|
598
810
|
test("create appends without shifting indices and leaves weak refs valid", async () => {
|
|
599
|
-
const doc = await openFixture$
|
|
811
|
+
const doc = await openFixture$15(engine, opts);
|
|
600
812
|
try {
|
|
601
813
|
const page = doc.page(fix.pageObjectNumber);
|
|
602
814
|
const before = await page.annotations.list();
|
|
@@ -632,7 +844,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
632
844
|
}
|
|
633
845
|
});
|
|
634
846
|
test("create + update honor annotation flags (set on create, merge on patch)", async () => {
|
|
635
|
-
const doc = await openFixture$
|
|
847
|
+
const doc = await openFixture$15(engine, opts);
|
|
636
848
|
try {
|
|
637
849
|
const page = doc.page(fix.pageObjectNumber);
|
|
638
850
|
const draft = {
|
|
@@ -667,7 +879,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
667
879
|
}
|
|
668
880
|
});
|
|
669
881
|
test("create shape annotations (circle + square) round-trip shape fields", async () => {
|
|
670
|
-
const doc = await openFixture$
|
|
882
|
+
const doc = await openFixture$15(engine, opts);
|
|
671
883
|
try {
|
|
672
884
|
const page = doc.page(fix.pageObjectNumber);
|
|
673
885
|
const circleDraft = {
|
|
@@ -748,7 +960,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
748
960
|
}
|
|
749
961
|
});
|
|
750
962
|
test("cloudy border (/BE) + rect differences (/RD) are tri-state", async () => {
|
|
751
|
-
const doc = await openFixture$
|
|
963
|
+
const doc = await openFixture$15(engine, opts);
|
|
752
964
|
try {
|
|
753
965
|
const page = doc.page(fix.pageObjectNumber);
|
|
754
966
|
const plainDraft = {
|
|
@@ -827,7 +1039,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
827
1039
|
}
|
|
828
1040
|
});
|
|
829
1041
|
test("update echoes the appearance verdict: moves preserve /AP, restyles re-bake", async () => {
|
|
830
|
-
const doc = await openFixture$
|
|
1042
|
+
const doc = await openFixture$15(engine, opts);
|
|
831
1043
|
try {
|
|
832
1044
|
const page = doc.page(fix.pageObjectNumber);
|
|
833
1045
|
const draft = {
|
|
@@ -902,7 +1114,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
902
1114
|
}
|
|
903
1115
|
});
|
|
904
1116
|
test("a partial /DA patch preserves the unpatched font members", async () => {
|
|
905
|
-
const doc = await openFixture$
|
|
1117
|
+
const doc = await openFixture$15(engine, opts);
|
|
906
1118
|
try {
|
|
907
1119
|
const page = doc.page(fix.pageObjectNumber);
|
|
908
1120
|
const draft = {
|
|
@@ -958,7 +1170,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
958
1170
|
}
|
|
959
1171
|
});
|
|
960
1172
|
test("box transform is tri-state: rect-only moves keep rotation, null flattens", async () => {
|
|
961
|
-
const doc = await openFixture$
|
|
1173
|
+
const doc = await openFixture$15(engine, opts);
|
|
962
1174
|
try {
|
|
963
1175
|
const page = doc.page(fix.pageObjectNumber);
|
|
964
1176
|
const draft = {
|
|
@@ -1019,7 +1231,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1019
1231
|
}
|
|
1020
1232
|
});
|
|
1021
1233
|
test("create vertex + line annotations (polygon/polyline/line) round-trip geometry", async () => {
|
|
1022
|
-
const doc = await openFixture$
|
|
1234
|
+
const doc = await openFixture$15(engine, opts);
|
|
1023
1235
|
try {
|
|
1024
1236
|
const page = doc.page(fix.pageObjectNumber);
|
|
1025
1237
|
const polygonDraft = {
|
|
@@ -1150,7 +1362,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1150
1362
|
}
|
|
1151
1363
|
});
|
|
1152
1364
|
test("create free-text + callout round-trip text/colour/intent fields", async () => {
|
|
1153
|
-
const doc = await openFixture$
|
|
1365
|
+
const doc = await openFixture$15(engine, opts);
|
|
1154
1366
|
try {
|
|
1155
1367
|
const page = doc.page(fix.pageObjectNumber);
|
|
1156
1368
|
const freeTextDraft = {
|
|
@@ -1246,7 +1458,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1246
1458
|
}
|
|
1247
1459
|
});
|
|
1248
1460
|
test("update a free-text patches alignment + colours and is non-structural", async () => {
|
|
1249
|
-
const doc = await openFixture$
|
|
1461
|
+
const doc = await openFixture$15(engine, opts);
|
|
1250
1462
|
try {
|
|
1251
1463
|
const page = doc.page(fix.pageObjectNumber);
|
|
1252
1464
|
const created = await page.annotations.create({
|
|
@@ -1307,7 +1519,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1307
1519
|
}
|
|
1308
1520
|
});
|
|
1309
1521
|
test("create redact (area + text) round-trips label + colour fields", async () => {
|
|
1310
|
-
const doc = await openFixture$
|
|
1522
|
+
const doc = await openFixture$15(engine, opts);
|
|
1311
1523
|
try {
|
|
1312
1524
|
const page = doc.page(fix.pageObjectNumber);
|
|
1313
1525
|
const areaDraft = {
|
|
@@ -1387,7 +1599,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1387
1599
|
}
|
|
1388
1600
|
});
|
|
1389
1601
|
test("update a redact patches the label and clears it, non-structurally", async () => {
|
|
1390
|
-
const doc = await openFixture$
|
|
1602
|
+
const doc = await openFixture$15(engine, opts);
|
|
1391
1603
|
try {
|
|
1392
1604
|
const page = doc.page(fix.pageObjectNumber);
|
|
1393
1605
|
const created = await page.annotations.create({
|
|
@@ -1464,7 +1676,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1464
1676
|
}
|
|
1465
1677
|
});
|
|
1466
1678
|
test("create + update a caret round-trips color/opacity/rect differences", async () => {
|
|
1467
|
-
const doc = await openFixture$
|
|
1679
|
+
const doc = await openFixture$15(engine, opts);
|
|
1468
1680
|
try {
|
|
1469
1681
|
const page = doc.page(fix.pageObjectNumber);
|
|
1470
1682
|
const caretDraft = {
|
|
@@ -1544,7 +1756,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1544
1756
|
}
|
|
1545
1757
|
});
|
|
1546
1758
|
test("update an ink annotation patches strokes + color and is non-structural", async () => {
|
|
1547
|
-
const doc = await openFixture$
|
|
1759
|
+
const doc = await openFixture$15(engine, opts);
|
|
1548
1760
|
try {
|
|
1549
1761
|
const page = doc.page(fix.pageObjectNumber);
|
|
1550
1762
|
const created = await page.annotations.create({
|
|
@@ -1595,7 +1807,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1595
1807
|
}
|
|
1596
1808
|
});
|
|
1597
1809
|
test("ink highlight intent + blend round-trip and unrelated patches preserve blend", async () => {
|
|
1598
|
-
const doc = await openFixture$
|
|
1810
|
+
const doc = await openFixture$15(engine, opts);
|
|
1599
1811
|
try {
|
|
1600
1812
|
const page = doc.page(fix.pageObjectNumber);
|
|
1601
1813
|
const created = await page.annotations.create({
|
|
@@ -1638,7 +1850,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1638
1850
|
}
|
|
1639
1851
|
});
|
|
1640
1852
|
test("update a polyline patches vertices + line endings and is non-structural", async () => {
|
|
1641
|
-
const doc = await openFixture$
|
|
1853
|
+
const doc = await openFixture$15(engine, opts);
|
|
1642
1854
|
try {
|
|
1643
1855
|
const page = doc.page(fix.pageObjectNumber);
|
|
1644
1856
|
const created = await page.annotations.create({
|
|
@@ -1687,7 +1899,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1687
1899
|
}
|
|
1688
1900
|
});
|
|
1689
1901
|
test("update a shape annotation patches color and is non-structural", async () => {
|
|
1690
|
-
const doc = await openFixture$
|
|
1902
|
+
const doc = await openFixture$15(engine, opts);
|
|
1691
1903
|
try {
|
|
1692
1904
|
const page = doc.page(fix.pageObjectNumber);
|
|
1693
1905
|
const created = await page.annotations.create({
|
|
@@ -1736,7 +1948,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1736
1948
|
}
|
|
1737
1949
|
});
|
|
1738
1950
|
if (opts.supportsAppearanceRasters) test("creating a shape bakes an /AP appearance the reader can rasterize", async () => {
|
|
1739
|
-
const doc = await openFixture$
|
|
1951
|
+
const doc = await openFixture$15(engine, opts);
|
|
1740
1952
|
try {
|
|
1741
1953
|
const page = doc.page(fix.pageObjectNumber);
|
|
1742
1954
|
const created = await page.annotations.create({
|
|
@@ -1766,7 +1978,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1766
1978
|
}
|
|
1767
1979
|
});
|
|
1768
1980
|
test("update on a durable annotation is non-structural and never touches /NM", async () => {
|
|
1769
|
-
const doc = await openFixture$
|
|
1981
|
+
const doc = await openFixture$15(engine, opts);
|
|
1770
1982
|
try {
|
|
1771
1983
|
const page = doc.page(fix.pageObjectNumber);
|
|
1772
1984
|
const before = await page.annotations.list();
|
|
@@ -1792,7 +2004,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1792
2004
|
}
|
|
1793
2005
|
});
|
|
1794
2006
|
if (fix.expectsWeakAnnotation) test("update on a weak annotation stamps a UUID v4 /NM and upgrades the ref", async () => {
|
|
1795
|
-
const doc = await openFixture$
|
|
2007
|
+
const doc = await openFixture$15(engine, opts);
|
|
1796
2008
|
try {
|
|
1797
2009
|
const page = doc.page(fix.pageObjectNumber);
|
|
1798
2010
|
const before = await page.annotations.list();
|
|
@@ -1822,7 +2034,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1822
2034
|
}
|
|
1823
2035
|
});
|
|
1824
2036
|
test("delete by objectNumber removes the annotation and reports a stable id", async () => {
|
|
1825
|
-
const doc = await openFixture$
|
|
2037
|
+
const doc = await openFixture$15(engine, opts);
|
|
1826
2038
|
try {
|
|
1827
2039
|
const page = doc.page(fix.pageObjectNumber);
|
|
1828
2040
|
const draft = {
|
|
@@ -1852,7 +2064,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1852
2064
|
}
|
|
1853
2065
|
});
|
|
1854
2066
|
if (fix.expectsWeakAnnotation) test("delete by index of a weak annotation reports deleted: null and refetch reason", async () => {
|
|
1855
|
-
const doc = await openFixture$
|
|
2067
|
+
const doc = await openFixture$15(engine, opts);
|
|
1856
2068
|
try {
|
|
1857
2069
|
const page = doc.page(fix.pageObjectNumber);
|
|
1858
2070
|
const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
|
|
@@ -1875,7 +2087,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1875
2087
|
}
|
|
1876
2088
|
});
|
|
1877
2089
|
test("abort on create rejects with AbortError", async () => {
|
|
1878
|
-
const doc = await openFixture$
|
|
2090
|
+
const doc = await openFixture$15(engine, opts);
|
|
1879
2091
|
try {
|
|
1880
2092
|
const page = doc.page(fix.pageObjectNumber);
|
|
1881
2093
|
const draft = {
|
|
@@ -1891,7 +2103,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1891
2103
|
}
|
|
1892
2104
|
});
|
|
1893
2105
|
test("update with a stale index revision throws InvalidReference", async () => {
|
|
1894
|
-
const doc = await openFixture$
|
|
2106
|
+
const doc = await openFixture$15(engine, opts);
|
|
1895
2107
|
try {
|
|
1896
2108
|
const page = doc.page(fix.pageObjectNumber);
|
|
1897
2109
|
const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
|
|
@@ -1918,7 +2130,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1918
2130
|
}
|
|
1919
2131
|
});
|
|
1920
2132
|
test("move single durable annotation reorders within the page (single-as-batch)", async () => {
|
|
1921
|
-
const doc = await openFixture$
|
|
2133
|
+
const doc = await openFixture$15(engine, opts);
|
|
1922
2134
|
try {
|
|
1923
2135
|
const page = doc.page(fix.pageObjectNumber);
|
|
1924
2136
|
const aDraft = {
|
|
@@ -1962,7 +2174,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
1962
2174
|
}
|
|
1963
2175
|
});
|
|
1964
2176
|
test("move multi-block preserves caller-supplied order at the destination", async () => {
|
|
1965
|
-
const doc = await openFixture$
|
|
2177
|
+
const doc = await openFixture$15(engine, opts);
|
|
1966
2178
|
try {
|
|
1967
2179
|
const page = doc.page(fix.pageObjectNumber);
|
|
1968
2180
|
const ids = await Promise.all([
|
|
@@ -2001,7 +2213,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2001
2213
|
}
|
|
2002
2214
|
});
|
|
2003
2215
|
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$
|
|
2216
|
+
const doc = await openFixture$15(engine, opts);
|
|
2005
2217
|
try {
|
|
2006
2218
|
const page = doc.page(fix.pageObjectNumber);
|
|
2007
2219
|
const before = await page.annotations.list();
|
|
@@ -2026,7 +2238,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2026
2238
|
}
|
|
2027
2239
|
});
|
|
2028
2240
|
test("move with a stale index revision rejects (locked rev-token guard)", async () => {
|
|
2029
|
-
const doc = await openFixture$
|
|
2241
|
+
const doc = await openFixture$15(engine, opts);
|
|
2030
2242
|
try {
|
|
2031
2243
|
const page = doc.page(fix.pageObjectNumber);
|
|
2032
2244
|
const a = await page.annotations.create({
|
|
@@ -2064,7 +2276,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2064
2276
|
}
|
|
2065
2277
|
});
|
|
2066
2278
|
test("move with out-of-range toIndex rejects with InvalidArg", async () => {
|
|
2067
|
-
const doc = await openFixture$
|
|
2279
|
+
const doc = await openFixture$15(engine, opts);
|
|
2068
2280
|
try {
|
|
2069
2281
|
const page = doc.page(fix.pageObjectNumber);
|
|
2070
2282
|
const a = await page.annotations.create({
|
|
@@ -2088,7 +2300,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2088
2300
|
}
|
|
2089
2301
|
});
|
|
2090
2302
|
test("move with duplicate refs rejects with InvalidArg", async () => {
|
|
2091
|
-
const doc = await openFixture$
|
|
2303
|
+
const doc = await openFixture$15(engine, opts);
|
|
2092
2304
|
try {
|
|
2093
2305
|
const page = doc.page(fix.pageObjectNumber);
|
|
2094
2306
|
const a = await page.annotations.create({
|
|
@@ -2111,7 +2323,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2111
2323
|
}
|
|
2112
2324
|
});
|
|
2113
2325
|
test("abort on move rejects with AbortError", async () => {
|
|
2114
|
-
const doc = await openFixture$
|
|
2326
|
+
const doc = await openFixture$15(engine, opts);
|
|
2115
2327
|
try {
|
|
2116
2328
|
const page = doc.page(fix.pageObjectNumber);
|
|
2117
2329
|
const a = await page.annotations.create({
|
|
@@ -2132,7 +2344,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2132
2344
|
}
|
|
2133
2345
|
});
|
|
2134
2346
|
test("a page move does NOT bump per-page RevisionTokens (weak refs survive reorder)", async () => {
|
|
2135
|
-
const doc = await openFixture$
|
|
2347
|
+
const doc = await openFixture$15(engine, opts);
|
|
2136
2348
|
try {
|
|
2137
2349
|
const list = await doc.pages.list();
|
|
2138
2350
|
if (list.pages.length < 1) return;
|
|
@@ -2147,7 +2359,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2147
2359
|
}
|
|
2148
2360
|
});
|
|
2149
2361
|
test("create a reply links /IRT and defaults /RT to \"reply\", reporting the parent", async () => {
|
|
2150
|
-
const doc = await openFixture$
|
|
2362
|
+
const doc = await openFixture$15(engine, opts);
|
|
2151
2363
|
try {
|
|
2152
2364
|
const page = doc.page(fix.pageObjectNumber);
|
|
2153
2365
|
const parent = await page.annotations.create({
|
|
@@ -2181,7 +2393,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2181
2393
|
}
|
|
2182
2394
|
});
|
|
2183
2395
|
test("create a grouped subordinate writes /RT /Group", async () => {
|
|
2184
|
-
const doc = await openFixture$
|
|
2396
|
+
const doc = await openFixture$15(engine, opts);
|
|
2185
2397
|
try {
|
|
2186
2398
|
const page = doc.page(fix.pageObjectNumber);
|
|
2187
2399
|
const primary = await page.annotations.create({
|
|
@@ -2212,7 +2424,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2212
2424
|
}
|
|
2213
2425
|
});
|
|
2214
2426
|
test("create link annotations round-trip uri, goto/xyz, goto/fitH, and dead targets", async () => {
|
|
2215
|
-
const doc = await openFixture$
|
|
2427
|
+
const doc = await openFixture$15(engine, opts);
|
|
2216
2428
|
try {
|
|
2217
2429
|
const page = doc.page(fix.pageObjectNumber);
|
|
2218
2430
|
const uri = await page.annotations.create({
|
|
@@ -2289,7 +2501,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2289
2501
|
}
|
|
2290
2502
|
});
|
|
2291
2503
|
test("a link patch retargets in both directions (uri→goto, goto→uri) and moves the rect", async () => {
|
|
2292
|
-
const doc = await openFixture$
|
|
2504
|
+
const doc = await openFixture$15(engine, opts);
|
|
2293
2505
|
try {
|
|
2294
2506
|
const page = doc.page(fix.pageObjectNumber);
|
|
2295
2507
|
const created = await page.annotations.create({
|
|
@@ -2344,7 +2556,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2344
2556
|
}
|
|
2345
2557
|
});
|
|
2346
2558
|
test("a link patch clears the target with null — a dead link on re-read", async () => {
|
|
2347
|
-
const doc = await openFixture$
|
|
2559
|
+
const doc = await openFixture$15(engine, opts);
|
|
2348
2560
|
try {
|
|
2349
2561
|
const page = doc.page(fix.pageObjectNumber);
|
|
2350
2562
|
const created = await page.annotations.create({
|
|
@@ -2383,7 +2595,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2383
2595
|
}
|
|
2384
2596
|
});
|
|
2385
2597
|
test("a link grouped to a highlight round-trips /IRT + /RT /Group with its target", async () => {
|
|
2386
|
-
const doc = await openFixture$
|
|
2598
|
+
const doc = await openFixture$15(engine, opts);
|
|
2387
2599
|
try {
|
|
2388
2600
|
const page = doc.page(fix.pageObjectNumber);
|
|
2389
2601
|
const parent = await page.annotations.create({
|
|
@@ -2417,7 +2629,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2417
2629
|
}
|
|
2418
2630
|
});
|
|
2419
2631
|
test("replace-text round-trips /IT and groups StrikeOut under its Caret", async () => {
|
|
2420
|
-
const doc = await openFixture$
|
|
2632
|
+
const doc = await openFixture$15(engine, opts);
|
|
2421
2633
|
try {
|
|
2422
2634
|
const page = doc.page(fix.pageObjectNumber);
|
|
2423
2635
|
const caret = await page.annotations.create({
|
|
@@ -2469,7 +2681,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2469
2681
|
}
|
|
2470
2682
|
});
|
|
2471
2683
|
test("patch inReplyTo: null clears /IRT and /RT (back to top-level)", async () => {
|
|
2472
|
-
const doc = await openFixture$
|
|
2684
|
+
const doc = await openFixture$15(engine, opts);
|
|
2473
2685
|
try {
|
|
2474
2686
|
const page = doc.page(fix.pageObjectNumber);
|
|
2475
2687
|
const parent = await page.annotations.create({
|
|
@@ -2496,7 +2708,7 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2496
2708
|
}
|
|
2497
2709
|
});
|
|
2498
2710
|
test("create with a cross-page /IRT parent throws InvalidArg", async () => {
|
|
2499
|
-
const doc = await openFixture$
|
|
2711
|
+
const doc = await openFixture$15(engine, opts);
|
|
2500
2712
|
try {
|
|
2501
2713
|
const page = doc.page(fix.pageObjectNumber);
|
|
2502
2714
|
const parent = await page.annotations.create({
|
|
@@ -2526,13 +2738,136 @@ function runAnnotationMutationConformance(runner, opts) {
|
|
|
2526
2738
|
await doc.close();
|
|
2527
2739
|
}
|
|
2528
2740
|
});
|
|
2741
|
+
test("a review-status reply round-trips /State + /StateModel + /Subj", async () => {
|
|
2742
|
+
const doc = await openFixture$15(engine, opts);
|
|
2743
|
+
try {
|
|
2744
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2745
|
+
const target = await page.annotations.create({
|
|
2746
|
+
subtype: "highlight",
|
|
2747
|
+
contents: "status target",
|
|
2748
|
+
subject: "Pricing question",
|
|
2749
|
+
quadPoints: quad
|
|
2750
|
+
});
|
|
2751
|
+
expect(target.created.subject).toBe("Pricing question");
|
|
2752
|
+
const status = await page.annotations.create({
|
|
2753
|
+
subtype: "text",
|
|
2754
|
+
rect: shapeRect,
|
|
2755
|
+
inReplyTo: target.created.ref,
|
|
2756
|
+
state: "accepted",
|
|
2757
|
+
stateModel: "review"
|
|
2758
|
+
});
|
|
2759
|
+
expect(AnnotationCreateResultSchema.safeParse(status).success).toBe(true);
|
|
2760
|
+
expect(status.created.subtype).toBe("text");
|
|
2761
|
+
if (status.created.subtype !== "text") return;
|
|
2762
|
+
expect(status.created.state).toBe("accepted");
|
|
2763
|
+
expect(status.created.stateModel).toBe("review");
|
|
2764
|
+
expect(status.created.replyType).toBe("reply");
|
|
2765
|
+
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);
|
|
2766
|
+
expect(read?.subtype).toBe("text");
|
|
2767
|
+
if (read?.subtype === "text") {
|
|
2768
|
+
expect(read.state).toBe("accepted");
|
|
2769
|
+
expect(read.stateModel).toBe("review");
|
|
2770
|
+
}
|
|
2771
|
+
} finally {
|
|
2772
|
+
await doc.close();
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2775
|
+
test("a draft /State without /StateModel is rejected with InvalidArg", async () => {
|
|
2776
|
+
const doc = await openFixture$15(engine, opts);
|
|
2777
|
+
try {
|
|
2778
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2779
|
+
let caught;
|
|
2780
|
+
try {
|
|
2781
|
+
await page.annotations.create({
|
|
2782
|
+
subtype: "text",
|
|
2783
|
+
rect: shapeRect,
|
|
2784
|
+
state: "accepted"
|
|
2785
|
+
});
|
|
2786
|
+
} catch (err) {
|
|
2787
|
+
caught = err;
|
|
2788
|
+
}
|
|
2789
|
+
expect(EngineError.is(caught, EngineErrorCode.InvalidArg)).toBe(true);
|
|
2790
|
+
} finally {
|
|
2791
|
+
await doc.close();
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2794
|
+
test("null-clear patches truly remove /State, /StateModel, /Subj and /Contents", async () => {
|
|
2795
|
+
const doc = await openFixture$15(engine, opts);
|
|
2796
|
+
try {
|
|
2797
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2798
|
+
const note = await page.annotations.create({
|
|
2799
|
+
subtype: "text",
|
|
2800
|
+
rect: shapeRect,
|
|
2801
|
+
contents: "work in progress",
|
|
2802
|
+
subject: "Draft",
|
|
2803
|
+
state: "none",
|
|
2804
|
+
stateModel: "review"
|
|
2805
|
+
});
|
|
2806
|
+
const cycled = await page.annotations.update(note.created.ref, {
|
|
2807
|
+
subtype: "text",
|
|
2808
|
+
state: "rejected"
|
|
2809
|
+
});
|
|
2810
|
+
expect(cycled.appearance.changed).toBe(false);
|
|
2811
|
+
expect(cycled.updated.subtype).toBe("text");
|
|
2812
|
+
if (cycled.updated.subtype === "text") {
|
|
2813
|
+
expect(cycled.updated.state).toBe("rejected");
|
|
2814
|
+
expect(cycled.updated.stateModel).toBe("review");
|
|
2815
|
+
}
|
|
2816
|
+
const cleared = await page.annotations.update(note.created.ref, {
|
|
2817
|
+
subtype: "text",
|
|
2818
|
+
contents: null,
|
|
2819
|
+
subject: null,
|
|
2820
|
+
state: null,
|
|
2821
|
+
stateModel: null
|
|
2822
|
+
});
|
|
2823
|
+
expect(cleared.appearance.changed).toBe(false);
|
|
2824
|
+
expect(cleared.updated.contents).toBe(null);
|
|
2825
|
+
expect(cleared.updated.subject).toBe(null);
|
|
2826
|
+
if (cleared.updated.subtype === "text") {
|
|
2827
|
+
expect(cleared.updated.state).toBe(null);
|
|
2828
|
+
expect(cleared.updated.stateModel).toBe(null);
|
|
2829
|
+
}
|
|
2830
|
+
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);
|
|
2831
|
+
expect(read?.contents).toBe(null);
|
|
2832
|
+
expect(read?.subject).toBe(null);
|
|
2833
|
+
if (read?.subtype === "text") {
|
|
2834
|
+
expect(read.state).toBe(null);
|
|
2835
|
+
expect(read.stateModel).toBe(null);
|
|
2836
|
+
}
|
|
2837
|
+
} finally {
|
|
2838
|
+
await doc.close();
|
|
2839
|
+
}
|
|
2840
|
+
});
|
|
2841
|
+
test("custom state models round-trip verbatim", async () => {
|
|
2842
|
+
const doc = await openFixture$15(engine, opts);
|
|
2843
|
+
try {
|
|
2844
|
+
const page = doc.page(fix.pageObjectNumber);
|
|
2845
|
+
const custom = await page.annotations.create({
|
|
2846
|
+
subtype: "text",
|
|
2847
|
+
rect: shapeRect,
|
|
2848
|
+
state: "in-progress",
|
|
2849
|
+
stateModel: "X-ReviewWorkflow"
|
|
2850
|
+
});
|
|
2851
|
+
expect(custom.created.subtype).toBe("text");
|
|
2852
|
+
if (custom.created.subtype !== "text") return;
|
|
2853
|
+
expect(custom.created.state).toBe("in-progress");
|
|
2854
|
+
expect(custom.created.stateModel).toBe("X-ReviewWorkflow");
|
|
2855
|
+
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);
|
|
2856
|
+
if (read?.subtype === "text") {
|
|
2857
|
+
expect(read.state).toBe("in-progress");
|
|
2858
|
+
expect(read.stateModel).toBe("X-ReviewWorkflow");
|
|
2859
|
+
}
|
|
2860
|
+
} finally {
|
|
2861
|
+
await doc.close();
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2529
2864
|
});
|
|
2530
2865
|
}
|
|
2531
2866
|
async function beginWeakEditIfRequired(doc, pageObjectNumber, fix) {
|
|
2532
2867
|
if (doc.capabilities.weakAnnotationEditSessions !== "required" || !fix.expectsWeakAnnotation) return null;
|
|
2533
2868
|
return doc.annotations.beginWeakEdit([pageObjectNumber]);
|
|
2534
2869
|
}
|
|
2535
|
-
async function openFixture$
|
|
2870
|
+
async function openFixture$15(engine, opts) {
|
|
2536
2871
|
if (opts.openKind === "bytes") {
|
|
2537
2872
|
const bytes = await opts.fixture.bytes();
|
|
2538
2873
|
return engine.open({
|
|
@@ -3122,7 +3457,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3122
3457
|
if (engine) await engine.destroy();
|
|
3123
3458
|
});
|
|
3124
3459
|
test("renders the expected set of appearances with valid output", async () => {
|
|
3125
|
-
const doc = await openFixture$
|
|
3460
|
+
const doc = await openFixture$14(engine, opts);
|
|
3126
3461
|
try {
|
|
3127
3462
|
const { pageState, appearances } = await collect(doc, opts, useRaw);
|
|
3128
3463
|
expect(pageState.pageObjectNumber).toBe(opts.fixture.pageObjectNumber);
|
|
@@ -3149,7 +3484,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3149
3484
|
}
|
|
3150
3485
|
});
|
|
3151
3486
|
if (opts.fixture.expectsWeakAppearance) test("weak (index-only) annotations are emitted, not dropped", async () => {
|
|
3152
|
-
const doc = await openFixture$
|
|
3487
|
+
const doc = await openFixture$14(engine, opts);
|
|
3153
3488
|
try {
|
|
3154
3489
|
const { appearances } = await collect(doc, opts, useRaw);
|
|
3155
3490
|
const weak = appearances.find((a) => a.ref.kind === "index");
|
|
@@ -3159,7 +3494,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3159
3494
|
}
|
|
3160
3495
|
});
|
|
3161
3496
|
if (useRaw) test("rendered appearances are not blank (non-zero alpha)", async () => {
|
|
3162
|
-
const doc = await openFixture$
|
|
3497
|
+
const doc = await openFixture$14(engine, opts);
|
|
3163
3498
|
try {
|
|
3164
3499
|
const { appearances } = await collect(doc, opts, useRaw);
|
|
3165
3500
|
const anyVisible = appearances.some((a) => a.raster !== null && hasOpaquePixel(a.raster));
|
|
@@ -3169,7 +3504,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
|
|
|
3169
3504
|
}
|
|
3170
3505
|
});
|
|
3171
3506
|
test("abort() rejects with AbortError", async () => {
|
|
3172
|
-
const doc = await openFixture$
|
|
3507
|
+
const doc = await openFixture$14(engine, opts);
|
|
3173
3508
|
try {
|
|
3174
3509
|
const page = doc.page(opts.fixture.pageObjectNumber);
|
|
3175
3510
|
const p = useRaw ? page.annotations.renderAppearances({ scale: 1 }) : page.annotations.renderAppearanceImages({
|
|
@@ -3224,7 +3559,7 @@ function hasOpaquePixel(raster) {
|
|
|
3224
3559
|
}
|
|
3225
3560
|
return false;
|
|
3226
3561
|
}
|
|
3227
|
-
async function openFixture$
|
|
3562
|
+
async function openFixture$14(engine, opts) {
|
|
3228
3563
|
if (opts.openKind === "bytes") {
|
|
3229
3564
|
const bytes = await opts.fixture.bytes();
|
|
3230
3565
|
return engine.open({
|
|
@@ -3293,7 +3628,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3293
3628
|
if (engine) await engine.destroy();
|
|
3294
3629
|
});
|
|
3295
3630
|
test("pages.list() returns every page in display order, durable PONs only", async () => {
|
|
3296
|
-
const doc = await openFixture$
|
|
3631
|
+
const doc = await openFixture$13(engine, opts);
|
|
3297
3632
|
try {
|
|
3298
3633
|
const list = await doc.pages.list();
|
|
3299
3634
|
expect(PageListSnapshotSchema.safeParse(list).success).toBe(true);
|
|
@@ -3312,7 +3647,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3312
3647
|
}
|
|
3313
3648
|
});
|
|
3314
3649
|
test("pages.move() reorders pages and returns the full post-move order", async () => {
|
|
3315
|
-
const doc = await openFixture$
|
|
3650
|
+
const doc = await openFixture$13(engine, opts);
|
|
3316
3651
|
try {
|
|
3317
3652
|
const before = await doc.pages.list();
|
|
3318
3653
|
if (before.pages.length < 3) return;
|
|
@@ -3336,7 +3671,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3336
3671
|
}
|
|
3337
3672
|
});
|
|
3338
3673
|
test("weak index-based annotation refs survive a page reorder", async () => {
|
|
3339
|
-
const doc = await openFixture$
|
|
3674
|
+
const doc = await openFixture$13(engine, opts);
|
|
3340
3675
|
try {
|
|
3341
3676
|
const list = await doc.pages.list();
|
|
3342
3677
|
if (list.pages.length < 2) return;
|
|
@@ -3373,7 +3708,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3373
3708
|
}
|
|
3374
3709
|
});
|
|
3375
3710
|
test("pages.move() rejects duplicate PONs with InvalidArg", async () => {
|
|
3376
|
-
const doc = await openFixture$
|
|
3711
|
+
const doc = await openFixture$13(engine, opts);
|
|
3377
3712
|
try {
|
|
3378
3713
|
const list = await doc.pages.list();
|
|
3379
3714
|
if (list.pages.length < 1) return;
|
|
@@ -3390,7 +3725,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3390
3725
|
}
|
|
3391
3726
|
});
|
|
3392
3727
|
test("pages.move() rejects out-of-range destIndex with InvalidArg", async () => {
|
|
3393
|
-
const doc = await openFixture$
|
|
3728
|
+
const doc = await openFixture$13(engine, opts);
|
|
3394
3729
|
try {
|
|
3395
3730
|
const list = await doc.pages.list();
|
|
3396
3731
|
if (list.pages.length < 1) return;
|
|
@@ -3407,7 +3742,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3407
3742
|
}
|
|
3408
3743
|
});
|
|
3409
3744
|
test("pages.move() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
3410
|
-
const doc = await openFixture$
|
|
3745
|
+
const doc = await openFixture$13(engine, opts);
|
|
3411
3746
|
try {
|
|
3412
3747
|
const list = await doc.pages.list();
|
|
3413
3748
|
let bogus = 0;
|
|
@@ -3425,7 +3760,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3425
3760
|
}
|
|
3426
3761
|
});
|
|
3427
3762
|
test("abort on pages.move() rejects with AbortError", async () => {
|
|
3428
|
-
const doc = await openFixture$
|
|
3763
|
+
const doc = await openFixture$13(engine, opts);
|
|
3429
3764
|
try {
|
|
3430
3765
|
const list = await doc.pages.list();
|
|
3431
3766
|
if (list.pages.length < 1) return;
|
|
@@ -3439,7 +3774,7 @@ function runPageReorderConformance(runner, opts) {
|
|
|
3439
3774
|
});
|
|
3440
3775
|
});
|
|
3441
3776
|
}
|
|
3442
|
-
async function openFixture$
|
|
3777
|
+
async function openFixture$13(engine, opts) {
|
|
3443
3778
|
if (opts.openKind === "bytes") {
|
|
3444
3779
|
const bytes = await opts.fixture.bytes();
|
|
3445
3780
|
return engine.open({
|
|
@@ -3513,7 +3848,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3513
3848
|
if (engine) await engine.destroy();
|
|
3514
3849
|
});
|
|
3515
3850
|
test("pages.rotate() sets the absolute rotation and returns the full layout", async () => {
|
|
3516
|
-
const doc = await openFixture$
|
|
3851
|
+
const doc = await openFixture$12(engine, opts);
|
|
3517
3852
|
try {
|
|
3518
3853
|
const before = await doc.pages.list();
|
|
3519
3854
|
const target = before.pages[0];
|
|
@@ -3532,7 +3867,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3532
3867
|
}
|
|
3533
3868
|
});
|
|
3534
3869
|
test("absolute rotation is idempotent: setting the same value twice is a no-op", async () => {
|
|
3535
|
-
const doc = await openFixture$
|
|
3870
|
+
const doc = await openFixture$12(engine, opts);
|
|
3536
3871
|
try {
|
|
3537
3872
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3538
3873
|
const first = await doc.pages.rotate([pon], 180);
|
|
@@ -3543,7 +3878,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3543
3878
|
}
|
|
3544
3879
|
});
|
|
3545
3880
|
test("one rotation applies to EVERY listed page (the multi-select gesture)", async () => {
|
|
3546
|
-
const doc = await openFixture$
|
|
3881
|
+
const doc = await openFixture$12(engine, opts);
|
|
3547
3882
|
try {
|
|
3548
3883
|
const list = await doc.pages.list();
|
|
3549
3884
|
if (list.pages.length < 2) return;
|
|
@@ -3555,7 +3890,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3555
3890
|
}
|
|
3556
3891
|
});
|
|
3557
3892
|
test("weak index-based annotation refs survive a rotate (no revision bump)", async () => {
|
|
3558
|
-
const doc = await openFixture$
|
|
3893
|
+
const doc = await openFixture$12(engine, opts);
|
|
3559
3894
|
try {
|
|
3560
3895
|
const hostPon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3561
3896
|
const hostPage = doc.page(hostPon);
|
|
@@ -3585,7 +3920,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3585
3920
|
}
|
|
3586
3921
|
});
|
|
3587
3922
|
test("pages.rotate() rejects an invalid rotation value with InvalidArg", async () => {
|
|
3588
|
-
const doc = await openFixture$
|
|
3923
|
+
const doc = await openFixture$12(engine, opts);
|
|
3589
3924
|
try {
|
|
3590
3925
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3591
3926
|
let caught;
|
|
@@ -3600,7 +3935,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3600
3935
|
}
|
|
3601
3936
|
});
|
|
3602
3937
|
test("pages.rotate() rejects duplicate PONs with InvalidArg", async () => {
|
|
3603
|
-
const doc = await openFixture$
|
|
3938
|
+
const doc = await openFixture$12(engine, opts);
|
|
3604
3939
|
try {
|
|
3605
3940
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3606
3941
|
let caught;
|
|
@@ -3615,7 +3950,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3615
3950
|
}
|
|
3616
3951
|
});
|
|
3617
3952
|
test("pages.rotate() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
3618
|
-
const doc = await openFixture$
|
|
3953
|
+
const doc = await openFixture$12(engine, opts);
|
|
3619
3954
|
try {
|
|
3620
3955
|
const list = await doc.pages.list();
|
|
3621
3956
|
let bogus = 0;
|
|
@@ -3633,7 +3968,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3633
3968
|
}
|
|
3634
3969
|
});
|
|
3635
3970
|
test("abort on pages.rotate() rejects with AbortError", async () => {
|
|
3636
|
-
const doc = await openFixture$
|
|
3971
|
+
const doc = await openFixture$12(engine, opts);
|
|
3637
3972
|
try {
|
|
3638
3973
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3639
3974
|
const p = doc.pages.rotate([pon], 90);
|
|
@@ -3645,7 +3980,7 @@ function runPageRotateConformance(runner, opts) {
|
|
|
3645
3980
|
});
|
|
3646
3981
|
});
|
|
3647
3982
|
}
|
|
3648
|
-
async function openFixture$
|
|
3983
|
+
async function openFixture$12(engine, opts) {
|
|
3649
3984
|
if (opts.openKind === "bytes") {
|
|
3650
3985
|
const bytes = await opts.fixture.bytes();
|
|
3651
3986
|
return engine.open({
|
|
@@ -3708,7 +4043,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3708
4043
|
if (engine) await engine.destroy();
|
|
3709
4044
|
});
|
|
3710
4045
|
test("pages.delete() removes the page and returns the surviving layout", async () => {
|
|
3711
|
-
const doc = await openFixture$
|
|
4046
|
+
const doc = await openFixture$11(engine, opts);
|
|
3712
4047
|
try {
|
|
3713
4048
|
const before = await doc.pages.list();
|
|
3714
4049
|
if (before.pages.length < 2) return;
|
|
@@ -3729,7 +4064,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3729
4064
|
}
|
|
3730
4065
|
});
|
|
3731
4066
|
test("addressing a deleted page afterwards is a clean NotFound", async () => {
|
|
3732
|
-
const doc = await openFixture$
|
|
4067
|
+
const doc = await openFixture$11(engine, opts);
|
|
3733
4068
|
try {
|
|
3734
4069
|
const before = await doc.pages.list();
|
|
3735
4070
|
if (before.pages.length < 2) return;
|
|
@@ -3747,7 +4082,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3747
4082
|
}
|
|
3748
4083
|
});
|
|
3749
4084
|
test("surviving pages keep their weak index-based annotation refs", async () => {
|
|
3750
|
-
const doc = await openFixture$
|
|
4085
|
+
const doc = await openFixture$11(engine, opts);
|
|
3751
4086
|
try {
|
|
3752
4087
|
const list = await doc.pages.list();
|
|
3753
4088
|
if (list.pages.length < 2) return;
|
|
@@ -3780,7 +4115,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3780
4115
|
}
|
|
3781
4116
|
});
|
|
3782
4117
|
test("deleting EVERY page is rejected with InvalidArg", async () => {
|
|
3783
|
-
const doc = await openFixture$
|
|
4118
|
+
const doc = await openFixture$11(engine, opts);
|
|
3784
4119
|
try {
|
|
3785
4120
|
const list = await doc.pages.list();
|
|
3786
4121
|
let caught;
|
|
@@ -3795,7 +4130,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3795
4130
|
}
|
|
3796
4131
|
});
|
|
3797
4132
|
test("pages.delete() rejects duplicate PONs with InvalidArg", async () => {
|
|
3798
|
-
const doc = await openFixture$
|
|
4133
|
+
const doc = await openFixture$11(engine, opts);
|
|
3799
4134
|
try {
|
|
3800
4135
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
3801
4136
|
let caught;
|
|
@@ -3810,7 +4145,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3810
4145
|
}
|
|
3811
4146
|
});
|
|
3812
4147
|
test("pages.delete() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
3813
|
-
const doc = await openFixture$
|
|
4148
|
+
const doc = await openFixture$11(engine, opts);
|
|
3814
4149
|
try {
|
|
3815
4150
|
const list = await doc.pages.list();
|
|
3816
4151
|
let bogus = 0;
|
|
@@ -3828,7 +4163,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3828
4163
|
}
|
|
3829
4164
|
});
|
|
3830
4165
|
test("abort on pages.delete() rejects with AbortError", async () => {
|
|
3831
|
-
const doc = await openFixture$
|
|
4166
|
+
const doc = await openFixture$11(engine, opts);
|
|
3832
4167
|
try {
|
|
3833
4168
|
const list = await doc.pages.list();
|
|
3834
4169
|
if (list.pages.length < 2) return;
|
|
@@ -3842,7 +4177,7 @@ function runPageDeleteConformance(runner, opts) {
|
|
|
3842
4177
|
});
|
|
3843
4178
|
});
|
|
3844
4179
|
}
|
|
3845
|
-
async function openFixture$
|
|
4180
|
+
async function openFixture$11(engine, opts) {
|
|
3846
4181
|
if (opts.openKind === "bytes") {
|
|
3847
4182
|
const bytes = await opts.fixture.bytes();
|
|
3848
4183
|
return engine.open({
|
|
@@ -3870,7 +4205,7 @@ function runPageFlattenConformance(runner, opts) {
|
|
|
3870
4205
|
if (engine) await engine.destroy();
|
|
3871
4206
|
});
|
|
3872
4207
|
test("flattens eligible appearances once without changing layout", async () => {
|
|
3873
|
-
const doc = await openFixture$
|
|
4208
|
+
const doc = await openFixture$10(engine, opts);
|
|
3874
4209
|
try {
|
|
3875
4210
|
if (!doc.pages.flatten) return;
|
|
3876
4211
|
const layoutBefore = await doc.pages.list();
|
|
@@ -3904,7 +4239,7 @@ function runPageFlattenConformance(runner, opts) {
|
|
|
3904
4239
|
});
|
|
3905
4240
|
});
|
|
3906
4241
|
}
|
|
3907
|
-
async function openFixture$
|
|
4242
|
+
async function openFixture$10(engine, opts) {
|
|
3908
4243
|
if (opts.openKind === "bytes") return engine.open({
|
|
3909
4244
|
kind: "bytes",
|
|
3910
4245
|
id: opts.fixture.id,
|
|
@@ -3959,7 +4294,7 @@ function runRedactionApplyConformance(runner, opts) {
|
|
|
3959
4294
|
if (engine) await engine.destroy();
|
|
3960
4295
|
});
|
|
3961
4296
|
test("annotations scope applies the marked redaction and counts collateral", async () => {
|
|
3962
|
-
const doc = await openFixture$
|
|
4297
|
+
const doc = await openFixture$9(engine, opts);
|
|
3963
4298
|
try {
|
|
3964
4299
|
if (!doc.redaction) return;
|
|
3965
4300
|
const layoutBefore = await doc.pages.list();
|
|
@@ -4013,7 +4348,7 @@ function runRedactionApplyConformance(runner, opts) {
|
|
|
4013
4348
|
}
|
|
4014
4349
|
});
|
|
4015
4350
|
test("pages scope applies everything once and validates its input", async () => {
|
|
4016
|
-
const doc = await openFixture$
|
|
4351
|
+
const doc = await openFixture$9(engine, opts);
|
|
4017
4352
|
try {
|
|
4018
4353
|
if (!doc.redaction) return;
|
|
4019
4354
|
const pageObjectNumber = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
@@ -4064,7 +4399,7 @@ function runRedactionApplyConformance(runner, opts) {
|
|
|
4064
4399
|
});
|
|
4065
4400
|
});
|
|
4066
4401
|
}
|
|
4067
|
-
async function openFixture$
|
|
4402
|
+
async function openFixture$9(engine, opts) {
|
|
4068
4403
|
if (opts.openKind === "bytes") return engine.open({
|
|
4069
4404
|
kind: "bytes",
|
|
4070
4405
|
id: opts.fixture.id,
|
|
@@ -4104,19 +4439,14 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4104
4439
|
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
4105
4440
|
describe(`page extract conformance: ${opts.label}`, () => {
|
|
4106
4441
|
let engine;
|
|
4107
|
-
let supported = false;
|
|
4108
4442
|
beforeAll(async () => {
|
|
4109
4443
|
engine = await opts.makeEngine();
|
|
4110
|
-
const probe = await openFixture$5(engine, opts);
|
|
4111
|
-
supported = probe.pages.extract !== void 0;
|
|
4112
|
-
await probe.close();
|
|
4113
4444
|
});
|
|
4114
4445
|
afterAll(async () => {
|
|
4115
4446
|
if (engine) await engine.destroy();
|
|
4116
4447
|
});
|
|
4117
4448
|
test("pages.extract() returns standalone PDF bytes and leaves the source untouched", async () => {
|
|
4118
|
-
|
|
4119
|
-
const doc = await openFixture$5(engine, opts);
|
|
4449
|
+
const doc = await openFixture$8(engine, opts);
|
|
4120
4450
|
try {
|
|
4121
4451
|
const before = await doc.pages.list();
|
|
4122
4452
|
const target = before.pages[before.pages.length - 1];
|
|
@@ -4131,8 +4461,8 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4131
4461
|
}
|
|
4132
4462
|
});
|
|
4133
4463
|
test("extracted bytes re-open as a document with the requested pages, in caller order", async () => {
|
|
4134
|
-
if (
|
|
4135
|
-
const doc = await openFixture$
|
|
4464
|
+
if (opts.openKind !== "bytes") return;
|
|
4465
|
+
const doc = await openFixture$8(engine, opts);
|
|
4136
4466
|
let extracted = null;
|
|
4137
4467
|
try {
|
|
4138
4468
|
const list = await doc.pages.list();
|
|
@@ -4155,8 +4485,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4155
4485
|
}
|
|
4156
4486
|
});
|
|
4157
4487
|
test("pages.extract() rejects empty input with InvalidArg", async () => {
|
|
4158
|
-
|
|
4159
|
-
const doc = await openFixture$5(engine, opts);
|
|
4488
|
+
const doc = await openFixture$8(engine, opts);
|
|
4160
4489
|
try {
|
|
4161
4490
|
let caught;
|
|
4162
4491
|
try {
|
|
@@ -4170,8 +4499,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4170
4499
|
}
|
|
4171
4500
|
});
|
|
4172
4501
|
test("pages.extract() rejects duplicate PONs with InvalidArg", async () => {
|
|
4173
|
-
|
|
4174
|
-
const doc = await openFixture$5(engine, opts);
|
|
4502
|
+
const doc = await openFixture$8(engine, opts);
|
|
4175
4503
|
try {
|
|
4176
4504
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
4177
4505
|
let caught;
|
|
@@ -4186,8 +4514,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4186
4514
|
}
|
|
4187
4515
|
});
|
|
4188
4516
|
test("pages.extract() rejects unknown PON with NotFound or InvalidArg", async () => {
|
|
4189
|
-
|
|
4190
|
-
const doc = await openFixture$5(engine, opts);
|
|
4517
|
+
const doc = await openFixture$8(engine, opts);
|
|
4191
4518
|
try {
|
|
4192
4519
|
const list = await doc.pages.list();
|
|
4193
4520
|
let bogus = 0;
|
|
@@ -4205,8 +4532,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4205
4532
|
}
|
|
4206
4533
|
});
|
|
4207
4534
|
test("abort on pages.extract() rejects with AbortError", async () => {
|
|
4208
|
-
|
|
4209
|
-
const doc = await openFixture$5(engine, opts);
|
|
4535
|
+
const doc = await openFixture$8(engine, opts);
|
|
4210
4536
|
try {
|
|
4211
4537
|
const list = await doc.pages.list();
|
|
4212
4538
|
const p = doc.pages.extract([list.pages[0].pageObjectNumber]);
|
|
@@ -4218,7 +4544,7 @@ function runPageExtractConformance(runner, opts) {
|
|
|
4218
4544
|
});
|
|
4219
4545
|
});
|
|
4220
4546
|
}
|
|
4221
|
-
async function openFixture$
|
|
4547
|
+
async function openFixture$8(engine, opts) {
|
|
4222
4548
|
if (opts.openKind === "bytes") {
|
|
4223
4549
|
const bytes = await opts.fixture.bytes();
|
|
4224
4550
|
return engine.open({
|
|
@@ -4480,9 +4806,9 @@ function runAttachmentConformance(runner, opts) {
|
|
|
4480
4806
|
//#endregion
|
|
4481
4807
|
//#region src/conformance/runPageInsertConformance.ts
|
|
4482
4808
|
/**
|
|
4483
|
-
* Page insert conformance. `pages.insert
|
|
4484
|
-
*
|
|
4485
|
-
*
|
|
4809
|
+
* Page insert conformance. `pages.insert` is a REQUIRED member — this suite
|
|
4810
|
+
* runs unconditionally on every engine, so an implementation that loses the
|
|
4811
|
+
* verb fails loudly instead of being skipped past.
|
|
4486
4812
|
*
|
|
4487
4813
|
* Invariants:
|
|
4488
4814
|
* 1. Every page of the source bytes is COPIED in at `destIndex` (omitted →
|
|
@@ -4498,19 +4824,14 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4498
4824
|
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
4499
4825
|
describe(`page insert conformance: ${opts.label}`, () => {
|
|
4500
4826
|
let engine;
|
|
4501
|
-
let supported = false;
|
|
4502
4827
|
beforeAll(async () => {
|
|
4503
4828
|
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
4829
|
});
|
|
4508
4830
|
afterAll(async () => {
|
|
4509
4831
|
if (engine) await engine.destroy();
|
|
4510
4832
|
});
|
|
4511
4833
|
test("appends every source page with fresh PONs; existing pages keep identity", async () => {
|
|
4512
|
-
|
|
4513
|
-
const doc = await openFixture$4(engine, opts);
|
|
4834
|
+
const doc = await openFixture$7(engine, opts);
|
|
4514
4835
|
try {
|
|
4515
4836
|
const before = await doc.pages.list();
|
|
4516
4837
|
const beforePons = before.pages.map((p) => p.pageObjectNumber);
|
|
@@ -4528,8 +4849,7 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4528
4849
|
}
|
|
4529
4850
|
});
|
|
4530
4851
|
test("destIndex places the block mid-document, in source order", async () => {
|
|
4531
|
-
|
|
4532
|
-
const doc = await openFixture$4(engine, opts);
|
|
4852
|
+
const doc = await openFixture$7(engine, opts);
|
|
4533
4853
|
try {
|
|
4534
4854
|
const before = await doc.pages.list();
|
|
4535
4855
|
if (before.pages.length < 2) return;
|
|
@@ -4546,8 +4866,8 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4546
4866
|
}
|
|
4547
4867
|
});
|
|
4548
4868
|
test("the inserted pages persist through save → re-open", async () => {
|
|
4549
|
-
if (
|
|
4550
|
-
const doc = await openFixture$
|
|
4869
|
+
if (opts.openKind !== "bytes") return;
|
|
4870
|
+
const doc = await openFixture$7(engine, opts);
|
|
4551
4871
|
let reopened = null;
|
|
4552
4872
|
try {
|
|
4553
4873
|
const before = await doc.pages.list();
|
|
@@ -4567,8 +4887,7 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4567
4887
|
}
|
|
4568
4888
|
});
|
|
4569
4889
|
test("empty bytes reject with InvalidArg; garbage rejects with MalformedPdf", async () => {
|
|
4570
|
-
|
|
4571
|
-
const doc = await openFixture$4(engine, opts);
|
|
4890
|
+
const doc = await openFixture$7(engine, opts);
|
|
4572
4891
|
try {
|
|
4573
4892
|
let caught;
|
|
4574
4893
|
try {
|
|
@@ -4591,8 +4910,7 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4591
4910
|
}
|
|
4592
4911
|
});
|
|
4593
4912
|
test("out-of-range destIndex rejects with InvalidArg", async () => {
|
|
4594
|
-
|
|
4595
|
-
const doc = await openFixture$4(engine, opts);
|
|
4913
|
+
const doc = await openFixture$7(engine, opts);
|
|
4596
4914
|
try {
|
|
4597
4915
|
const before = await doc.pages.list();
|
|
4598
4916
|
const single = await doc.pages.extract([before.pages[0].pageObjectNumber]);
|
|
@@ -4609,7 +4927,160 @@ function runPageInsertConformance(runner, opts) {
|
|
|
4609
4927
|
});
|
|
4610
4928
|
});
|
|
4611
4929
|
}
|
|
4612
|
-
async function openFixture$
|
|
4930
|
+
async function openFixture$7(engine, opts) {
|
|
4931
|
+
if (opts.openKind === "bytes") {
|
|
4932
|
+
const bytes = await opts.fixture.bytes();
|
|
4933
|
+
return engine.open({
|
|
4934
|
+
kind: "bytes",
|
|
4935
|
+
id: opts.fixture.id,
|
|
4936
|
+
bytes
|
|
4937
|
+
});
|
|
4938
|
+
}
|
|
4939
|
+
return engine.open({
|
|
4940
|
+
kind: "id",
|
|
4941
|
+
id: opts.fixture.cloudId ?? opts.fixture.id
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
4944
|
+
//#endregion
|
|
4945
|
+
//#region src/conformance/runPageInsertBlankConformance.ts
|
|
4946
|
+
/** An on-purpose non-default size so the assertions can't pass by accident. */
|
|
4947
|
+
const SIZE = {
|
|
4948
|
+
width: 396,
|
|
4949
|
+
height: 612
|
|
4950
|
+
};
|
|
4951
|
+
/**
|
|
4952
|
+
* Blank-page insert conformance. `pages.insertBlank` is a REQUIRED member —
|
|
4953
|
+
* the suite runs unconditionally on every engine (no fixture bytes are
|
|
4954
|
+
* needed at all, which is the point of the verb), so an implementation that
|
|
4955
|
+
* loses the verb fails loudly instead of being skipped past.
|
|
4956
|
+
*
|
|
4957
|
+
* Invariants:
|
|
4958
|
+
* 1. `count` blank pages of exactly `size` appear at `destIndex` (omitted
|
|
4959
|
+
* → append), rotation 0; the result lists their fresh PONs in insertion
|
|
4960
|
+
* order and they agree with the returned layout.
|
|
4961
|
+
* 2. Pre-existing pages keep their identity: same PONs before and after,
|
|
4962
|
+
* in the expected positions (an insert never invalidates neighbours).
|
|
4963
|
+
* 3. The mutation persists through save → re-open (bytes engines only).
|
|
4964
|
+
* 4. Non-positive size / count outside [1, PAGE_INSERT_BLANK_MAX_COUNT] /
|
|
4965
|
+
* out-of-range destIndex reject with InvalidArg, leaving the document
|
|
4966
|
+
* untouched.
|
|
4967
|
+
*/
|
|
4968
|
+
function runPageInsertBlankConformance(runner, opts) {
|
|
4969
|
+
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
4970
|
+
describe(`page insert-blank conformance: ${opts.label}`, () => {
|
|
4971
|
+
let engine;
|
|
4972
|
+
beforeAll(async () => {
|
|
4973
|
+
engine = await opts.makeEngine();
|
|
4974
|
+
});
|
|
4975
|
+
afterAll(async () => {
|
|
4976
|
+
if (engine) await engine.destroy();
|
|
4977
|
+
});
|
|
4978
|
+
test("appends one blank page of the requested size with a fresh PON", async () => {
|
|
4979
|
+
const doc = await openFixture$6(engine, opts);
|
|
4980
|
+
try {
|
|
4981
|
+
const before = await doc.pages.list();
|
|
4982
|
+
const beforePons = before.pages.map((p) => p.pageObjectNumber);
|
|
4983
|
+
const result = await doc.pages.insertBlank({ size: SIZE });
|
|
4984
|
+
expect(result.insertedPageObjectNumbers.length).toBe(1);
|
|
4985
|
+
expect(result.layout.pageCount).toBe(before.pageCount + 1);
|
|
4986
|
+
expect(result.layout.pages.slice(0, before.pageCount).map((p) => p.pageObjectNumber)).toEqual(beforePons);
|
|
4987
|
+
const newPon = result.insertedPageObjectNumbers[0];
|
|
4988
|
+
expect(beforePons.includes(newPon)).toBe(false);
|
|
4989
|
+
const appended = result.layout.pages[before.pageCount];
|
|
4990
|
+
expect(appended.pageObjectNumber).toBe(newPon);
|
|
4991
|
+
expect(appended.size).toEqual(SIZE);
|
|
4992
|
+
expect(appended.rotation).toBe(0);
|
|
4993
|
+
} finally {
|
|
4994
|
+
await doc.close();
|
|
4995
|
+
}
|
|
4996
|
+
});
|
|
4997
|
+
test("destIndex + count places the blank block mid-document", async () => {
|
|
4998
|
+
const doc = await openFixture$6(engine, opts);
|
|
4999
|
+
try {
|
|
5000
|
+
const before = await doc.pages.list();
|
|
5001
|
+
if (before.pages.length < 2) return;
|
|
5002
|
+
const beforePons = before.pages.map((p) => p.pageObjectNumber);
|
|
5003
|
+
const result = await doc.pages.insertBlank({
|
|
5004
|
+
size: SIZE,
|
|
5005
|
+
count: 2
|
|
5006
|
+
}, 1);
|
|
5007
|
+
expect(result.insertedPageObjectNumbers.length).toBe(2);
|
|
5008
|
+
expect(result.insertedPageObjectNumbers[0] === result.insertedPageObjectNumbers[1]).toBe(false);
|
|
5009
|
+
const pons = result.layout.pages.map((p) => p.pageObjectNumber);
|
|
5010
|
+
expect(pons[0]).toBe(beforePons[0]);
|
|
5011
|
+
expect(pons.slice(1, 3)).toEqual(result.insertedPageObjectNumbers);
|
|
5012
|
+
expect(pons.slice(3)).toEqual(beforePons.slice(1));
|
|
5013
|
+
expect(result.layout.pages[1].size).toEqual(SIZE);
|
|
5014
|
+
expect(result.layout.pages[2].size).toEqual(SIZE);
|
|
5015
|
+
} finally {
|
|
5016
|
+
await doc.close();
|
|
5017
|
+
}
|
|
5018
|
+
});
|
|
5019
|
+
test("the blank pages persist through save → re-open", async () => {
|
|
5020
|
+
if (opts.openKind !== "bytes") return;
|
|
5021
|
+
const doc = await openFixture$6(engine, opts);
|
|
5022
|
+
let reopened = null;
|
|
5023
|
+
try {
|
|
5024
|
+
const before = await doc.pages.list();
|
|
5025
|
+
await doc.pages.insertBlank({ size: SIZE });
|
|
5026
|
+
const bytes = await doc.download();
|
|
5027
|
+
reopened = await engine.open({
|
|
5028
|
+
kind: "bytes",
|
|
5029
|
+
id: `${opts.fixture.id}-insert-blank-reopen`,
|
|
5030
|
+
bytes
|
|
5031
|
+
});
|
|
5032
|
+
const after = await reopened.pages.list();
|
|
5033
|
+
expect(after.pageCount).toBe(before.pageCount + 1);
|
|
5034
|
+
const tail = after.pages[after.pageCount - 1];
|
|
5035
|
+
expect(Math.round(tail.size.width)).toBe(SIZE.width);
|
|
5036
|
+
expect(Math.round(tail.size.height)).toBe(SIZE.height);
|
|
5037
|
+
} finally {
|
|
5038
|
+
if (reopened) await reopened.close();
|
|
5039
|
+
await doc.close();
|
|
5040
|
+
}
|
|
5041
|
+
});
|
|
5042
|
+
test("non-positive size, bad count, and out-of-range destIndex reject with InvalidArg", async () => {
|
|
5043
|
+
const doc = await openFixture$6(engine, opts);
|
|
5044
|
+
try {
|
|
5045
|
+
const before = await doc.pages.list();
|
|
5046
|
+
const rejects = [
|
|
5047
|
+
() => doc.pages.insertBlank({ size: {
|
|
5048
|
+
width: 0,
|
|
5049
|
+
height: 612
|
|
5050
|
+
} }),
|
|
5051
|
+
() => doc.pages.insertBlank({ size: {
|
|
5052
|
+
width: 396,
|
|
5053
|
+
height: -10
|
|
5054
|
+
} }),
|
|
5055
|
+
() => doc.pages.insertBlank({
|
|
5056
|
+
size: SIZE,
|
|
5057
|
+
count: 0
|
|
5058
|
+
}),
|
|
5059
|
+
() => doc.pages.insertBlank({
|
|
5060
|
+
size: SIZE,
|
|
5061
|
+
count: 101
|
|
5062
|
+
}),
|
|
5063
|
+
() => doc.pages.insertBlank({ size: SIZE }, before.pageCount + 1)
|
|
5064
|
+
];
|
|
5065
|
+
for (const attempt of rejects) {
|
|
5066
|
+
let caught;
|
|
5067
|
+
try {
|
|
5068
|
+
await attempt();
|
|
5069
|
+
} catch (err) {
|
|
5070
|
+
caught = err;
|
|
5071
|
+
}
|
|
5072
|
+
expect(EngineError.is(caught, EngineErrorCode.InvalidArg)).toBe(true);
|
|
5073
|
+
}
|
|
5074
|
+
const list = await doc.pages.list();
|
|
5075
|
+
expect(list.pageCount).toBe(before.pageCount);
|
|
5076
|
+
expect(list.pages.map((p) => p.pageObjectNumber)).toEqual(before.pages.map((p) => p.pageObjectNumber));
|
|
5077
|
+
} finally {
|
|
5078
|
+
await doc.close();
|
|
5079
|
+
}
|
|
5080
|
+
});
|
|
5081
|
+
});
|
|
5082
|
+
}
|
|
5083
|
+
async function openFixture$6(engine, opts) {
|
|
4613
5084
|
if (opts.openKind === "bytes") {
|
|
4614
5085
|
const bytes = await opts.fixture.bytes();
|
|
4615
5086
|
return engine.open({
|
|
@@ -4650,7 +5121,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4650
5121
|
let supported = false;
|
|
4651
5122
|
beforeAll(async () => {
|
|
4652
5123
|
engine = await opts.makeEngine();
|
|
4653
|
-
const probe = await openFixture$
|
|
5124
|
+
const probe = await openFixture$5(engine, opts);
|
|
4654
5125
|
supported = probe.pieceInfo !== void 0;
|
|
4655
5126
|
await probe.close();
|
|
4656
5127
|
});
|
|
@@ -4659,7 +5130,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4659
5130
|
});
|
|
4660
5131
|
test("doc-level: the full value vocabulary round-trips with tags intact", async () => {
|
|
4661
5132
|
if (!supported) return;
|
|
4662
|
-
const doc = await openFixture$
|
|
5133
|
+
const doc = await openFixture$5(engine, opts);
|
|
4663
5134
|
try {
|
|
4664
5135
|
await doc.pieceInfo.update(APP, {
|
|
4665
5136
|
name: "Standard Stamps",
|
|
@@ -4699,7 +5170,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4699
5170
|
});
|
|
4700
5171
|
test("page-level: the stamp schema round-trips on a page", async () => {
|
|
4701
5172
|
if (!supported) return;
|
|
4702
|
-
const doc = await openFixture$
|
|
5173
|
+
const doc = await openFixture$5(engine, opts);
|
|
4703
5174
|
try {
|
|
4704
5175
|
const list = await doc.pages.list();
|
|
4705
5176
|
const page = doc.page(list.pages[0].pageObjectNumber);
|
|
@@ -4725,7 +5196,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4725
5196
|
});
|
|
4726
5197
|
test("writes persist through save → re-open", async () => {
|
|
4727
5198
|
if (!supported || opts.openKind !== "bytes") return;
|
|
4728
|
-
const doc = await openFixture$
|
|
5199
|
+
const doc = await openFixture$5(engine, opts);
|
|
4729
5200
|
let reopened = null;
|
|
4730
5201
|
try {
|
|
4731
5202
|
const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
|
|
@@ -4758,7 +5229,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4758
5229
|
});
|
|
4759
5230
|
test("null deletes a key; clear removes an entry; siblings survive", async () => {
|
|
4760
5231
|
if (!supported) return;
|
|
4761
|
-
const doc = await openFixture$
|
|
5232
|
+
const doc = await openFixture$5(engine, opts);
|
|
4762
5233
|
try {
|
|
4763
5234
|
await doc.pieceInfo.update(APP, {
|
|
4764
5235
|
name: "A",
|
|
@@ -4784,7 +5255,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4784
5255
|
});
|
|
4785
5256
|
test("an application that was never written reads as null", async () => {
|
|
4786
5257
|
if (!supported) return;
|
|
4787
|
-
const doc = await openFixture$
|
|
5258
|
+
const doc = await openFixture$5(engine, opts);
|
|
4788
5259
|
try {
|
|
4789
5260
|
expect(await doc.pieceInfo.read("EMBD_NeverWritten")).toBe(null);
|
|
4790
5261
|
const list = await doc.pages.list();
|
|
@@ -4795,7 +5266,7 @@ function runPieceInfoConformance(runner, opts) {
|
|
|
4795
5266
|
});
|
|
4796
5267
|
});
|
|
4797
5268
|
}
|
|
4798
|
-
async function openFixture$
|
|
5269
|
+
async function openFixture$5(engine, opts) {
|
|
4799
5270
|
if (opts.openKind === "bytes") {
|
|
4800
5271
|
const bytes = await opts.fixture.bytes();
|
|
4801
5272
|
return engine.open({
|
|
@@ -4858,7 +5329,7 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4858
5329
|
if (engine) await engine.destroy();
|
|
4859
5330
|
});
|
|
4860
5331
|
test("every confirmed mutation publishes exactly one event, results verbatim", async () => {
|
|
4861
|
-
const doc = await openFixture$
|
|
5332
|
+
const doc = await openFixture$4(engine, opts);
|
|
4862
5333
|
try {
|
|
4863
5334
|
const events = [];
|
|
4864
5335
|
doc.events.subscribe((event) => events.push(event));
|
|
@@ -4905,19 +5376,46 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4905
5376
|
expect(evDeleted.layout).toEqual(deleted.layout);
|
|
4906
5377
|
}
|
|
4907
5378
|
if (evMeta.type === "metadata.updated") expect(evMeta.metadata).toEqual(meta.metadata);
|
|
5379
|
+
const first = events[0];
|
|
5380
|
+
expect(first !== void 0 && "origin" in first).toBe(true);
|
|
5381
|
+
if (first === void 0 || !("origin" in first)) return;
|
|
4908
5382
|
for (const event of events) {
|
|
5383
|
+
expect("origin" in event).toBe(true);
|
|
5384
|
+
if (!("origin" in event)) continue;
|
|
4909
5385
|
expect(event.origin.kind).toBe("local");
|
|
4910
5386
|
expect(typeof event.origin.sessionId).toBe("string");
|
|
4911
5387
|
expect(event.origin.sessionId.length > 0).toBe(true);
|
|
4912
|
-
expect(event.origin.sessionId).toBe(
|
|
5388
|
+
expect(event.origin.sessionId).toBe(first.origin.sessionId);
|
|
4913
5389
|
expect(typeof event.origin.ts).toBe("number");
|
|
4914
5390
|
}
|
|
4915
5391
|
} finally {
|
|
4916
5392
|
await doc.close();
|
|
4917
5393
|
}
|
|
4918
5394
|
});
|
|
5395
|
+
test("pages.inserted rides verbatim", async () => {
|
|
5396
|
+
const doc = await openFixture$4(engine, opts);
|
|
5397
|
+
try {
|
|
5398
|
+
const events = [];
|
|
5399
|
+
doc.events.subscribe((event) => events.push(event));
|
|
5400
|
+
const inserted = await doc.pages.insertBlank({ size: {
|
|
5401
|
+
width: 396,
|
|
5402
|
+
height: 612
|
|
5403
|
+
} }, 0);
|
|
5404
|
+
expect(events.map((event) => event.type)).toEqual(["pages.inserted"]);
|
|
5405
|
+
const [evInserted] = events;
|
|
5406
|
+
if (evInserted.type === "pages.inserted") {
|
|
5407
|
+
expect(evInserted.insertedPageObjectNumbers).toEqual(inserted.insertedPageObjectNumbers);
|
|
5408
|
+
expect(evInserted.layout).toEqual(inserted.layout);
|
|
5409
|
+
expect(evInserted.cache).toEqual(inserted.cache);
|
|
5410
|
+
expect(evInserted.destIndex).toBe(0);
|
|
5411
|
+
expect(evInserted.origin.kind).toBe("local");
|
|
5412
|
+
}
|
|
5413
|
+
} finally {
|
|
5414
|
+
await doc.close();
|
|
5415
|
+
}
|
|
5416
|
+
});
|
|
4919
5417
|
test("a FAILED mutation publishes nothing (events are ground truth)", async () => {
|
|
4920
|
-
const doc = await openFixture$
|
|
5418
|
+
const doc = await openFixture$4(engine, opts);
|
|
4921
5419
|
try {
|
|
4922
5420
|
const events = [];
|
|
4923
5421
|
doc.events.subscribe((event) => events.push(event));
|
|
@@ -4935,7 +5433,7 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4935
5433
|
}
|
|
4936
5434
|
});
|
|
4937
5435
|
test("unsubscribe stops delivery", async () => {
|
|
4938
|
-
const doc = await openFixture$
|
|
5436
|
+
const doc = await openFixture$4(engine, opts);
|
|
4939
5437
|
try {
|
|
4940
5438
|
const events = [];
|
|
4941
5439
|
doc.events.subscribe((event) => events.push(event))();
|
|
@@ -4948,7 +5446,7 @@ function runDocumentEventsConformance(runner, opts) {
|
|
|
4948
5446
|
});
|
|
4949
5447
|
});
|
|
4950
5448
|
}
|
|
4951
|
-
async function openFixture$
|
|
5449
|
+
async function openFixture$4(engine, opts) {
|
|
4952
5450
|
if (opts.openKind === "bytes") {
|
|
4953
5451
|
const bytes = await opts.fixture.bytes();
|
|
4954
5452
|
return engine.open({
|
|
@@ -4975,7 +5473,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
4975
5473
|
if (engine) await engine.destroy();
|
|
4976
5474
|
});
|
|
4977
5475
|
test("read() returns a PageTextSnapshot with non-empty text", async () => {
|
|
4978
|
-
const doc = await openFixture$
|
|
5476
|
+
const doc = await openFixture$3(engine, opts);
|
|
4979
5477
|
try {
|
|
4980
5478
|
const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
|
|
4981
5479
|
expect(PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
|
|
@@ -4986,7 +5484,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
4986
5484
|
}
|
|
4987
5485
|
});
|
|
4988
5486
|
test("extracted text contains the expected substring", async () => {
|
|
4989
|
-
const doc = await openFixture$
|
|
5487
|
+
const doc = await openFixture$3(engine, opts);
|
|
4990
5488
|
try {
|
|
4991
5489
|
const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
|
|
4992
5490
|
expect(snap.text.includes(opts.fixture.expectedSubstring)).toBe(true);
|
|
@@ -4995,7 +5493,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
4995
5493
|
}
|
|
4996
5494
|
});
|
|
4997
5495
|
test("read() on an unknown pageObjectNumber throws NotFound", async () => {
|
|
4998
|
-
const doc = await openFixture$
|
|
5496
|
+
const doc = await openFixture$3(engine, opts);
|
|
4999
5497
|
try {
|
|
5000
5498
|
let caught;
|
|
5001
5499
|
try {
|
|
@@ -5010,7 +5508,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
5010
5508
|
}
|
|
5011
5509
|
});
|
|
5012
5510
|
test("abort() on read rejects with AbortError", async () => {
|
|
5013
|
-
const doc = await openFixture$
|
|
5511
|
+
const doc = await openFixture$3(engine, opts);
|
|
5014
5512
|
try {
|
|
5015
5513
|
const p = doc.page(opts.fixture.pageObjectNumber).text.read();
|
|
5016
5514
|
p.abort("test");
|
|
@@ -5020,7 +5518,7 @@ function runPageTextConformance(runner, opts) {
|
|
|
5020
5518
|
}
|
|
5021
5519
|
});
|
|
5022
5520
|
test("read after close throws DocNotOpen", async () => {
|
|
5023
|
-
const doc = await openFixture$
|
|
5521
|
+
const doc = await openFixture$3(engine, opts);
|
|
5024
5522
|
const page = doc.page(opts.fixture.pageObjectNumber);
|
|
5025
5523
|
await doc.close();
|
|
5026
5524
|
let caught;
|
|
@@ -5034,6 +5532,320 @@ function runPageTextConformance(runner, opts) {
|
|
|
5034
5532
|
});
|
|
5035
5533
|
});
|
|
5036
5534
|
}
|
|
5535
|
+
async function openFixture$3(engine, opts) {
|
|
5536
|
+
if (opts.openKind === "bytes") {
|
|
5537
|
+
const bytes = await opts.fixture.bytes();
|
|
5538
|
+
return engine.open({
|
|
5539
|
+
kind: "bytes",
|
|
5540
|
+
id: opts.fixture.id,
|
|
5541
|
+
bytes
|
|
5542
|
+
});
|
|
5543
|
+
}
|
|
5544
|
+
return engine.open({
|
|
5545
|
+
kind: "id",
|
|
5546
|
+
id: opts.fixture.cloudId ?? opts.fixture.id
|
|
5547
|
+
});
|
|
5548
|
+
}
|
|
5549
|
+
//#endregion
|
|
5550
|
+
//#region src/conformance/runTextDivergenceConformance.ts
|
|
5551
|
+
function runTextDivergenceConformance(runner, opts) {
|
|
5552
|
+
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
5553
|
+
const fixture = opts.fixture;
|
|
5554
|
+
describe(`text divergence conformance: ${opts.label} [${fixture.id}]`, () => {
|
|
5555
|
+
let engine;
|
|
5556
|
+
beforeAll(async () => {
|
|
5557
|
+
engine = await opts.makeEngine();
|
|
5558
|
+
});
|
|
5559
|
+
afterAll(async () => {
|
|
5560
|
+
if (engine) await engine.destroy();
|
|
5561
|
+
});
|
|
5562
|
+
test("snapshot is schema-valid with the exact text, charCount, and charMap", async () => {
|
|
5563
|
+
const doc = await openFixture$2(engine, opts);
|
|
5564
|
+
try {
|
|
5565
|
+
const snap = await doc.page(fixture.pageObjectNumber).text.read();
|
|
5566
|
+
expect(PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
|
|
5567
|
+
expect(snap.text).toBe(fixture.exactText);
|
|
5568
|
+
expect(snap.charCount).toBe(fixture.expectedCharCount);
|
|
5569
|
+
const anchors = snap.charMap ?? null;
|
|
5570
|
+
if (fixture.expectedCharMap === null) expect(anchors === null || anchors.length === 0).toBe(true);
|
|
5571
|
+
else expect(anchors === null ? null : anchors.map((a) => [a[0], a[1]])).toEqual(fixture.expectedCharMap.map((a) => [a[0], a[1]]));
|
|
5572
|
+
} finally {
|
|
5573
|
+
await doc.close();
|
|
5574
|
+
}
|
|
5575
|
+
});
|
|
5576
|
+
test("char-space slicing round-trips through the map", async () => {
|
|
5577
|
+
const doc = await openFixture$2(engine, opts);
|
|
5578
|
+
try {
|
|
5579
|
+
const snap = await doc.page(fixture.pageObjectNumber).text.read();
|
|
5580
|
+
expect(sliceTextByChars(snap, 0, snap.charCount)).toBe(snap.text);
|
|
5581
|
+
for (const s of fixture.slices ?? []) expect(sliceTextByChars(snap, s.from, s.to)).toBe(s.text);
|
|
5582
|
+
} finally {
|
|
5583
|
+
await doc.close();
|
|
5584
|
+
}
|
|
5585
|
+
});
|
|
5586
|
+
if (fixture.search) {
|
|
5587
|
+
const probe = fixture.search;
|
|
5588
|
+
test("search hits address character space and join the text by slicing", async () => {
|
|
5589
|
+
const doc = await openFixture$2(engine, opts);
|
|
5590
|
+
try {
|
|
5591
|
+
const hit = (await doc.search.query({
|
|
5592
|
+
query: { text: probe.query },
|
|
5593
|
+
mode: "rects"
|
|
5594
|
+
})).matches.find((m) => m.pageObjectNumber === fixture.pageObjectNumber);
|
|
5595
|
+
expect(hit === void 0).toBe(false);
|
|
5596
|
+
expect(hit.charStart).toBe(probe.charStart);
|
|
5597
|
+
expect(hit.charCount).toBe(probe.charCount);
|
|
5598
|
+
expect(hit.segments.length > 0).toBe(true);
|
|
5599
|
+
const snap = await doc.page(fixture.pageObjectNumber).text.read();
|
|
5600
|
+
expect(sliceTextByChars(snap, hit.charStart, hit.charStart + hit.charCount)).toBe(probe.matchedText);
|
|
5601
|
+
} finally {
|
|
5602
|
+
await doc.close();
|
|
5603
|
+
}
|
|
5604
|
+
});
|
|
5605
|
+
}
|
|
5606
|
+
});
|
|
5607
|
+
}
|
|
5608
|
+
async function openFixture$2(engine, opts) {
|
|
5609
|
+
if (opts.openKind === "bytes") {
|
|
5610
|
+
const bytes = await opts.fixture.bytes();
|
|
5611
|
+
return engine.open({
|
|
5612
|
+
kind: "bytes",
|
|
5613
|
+
id: opts.fixture.id,
|
|
5614
|
+
bytes
|
|
5615
|
+
});
|
|
5616
|
+
}
|
|
5617
|
+
return engine.open({
|
|
5618
|
+
kind: "id",
|
|
5619
|
+
id: opts.fixture.cloudId ?? opts.fixture.id
|
|
5620
|
+
});
|
|
5621
|
+
}
|
|
5622
|
+
/**
|
|
5623
|
+
* THE pinned divergence cases — one table, consumed by both engines' suites
|
|
5624
|
+
* so expected values cannot drift. Values verified against the wasm runtime
|
|
5625
|
+
* (see each case). All fixtures are single-page; the page's indirect object
|
|
5626
|
+
* number is 3 in each.
|
|
5627
|
+
*/
|
|
5628
|
+
const TEXT_DIVERGENCE_CASES = {
|
|
5629
|
+
/** Leading NON-PRINTING character: 31 character slots, 30 text units —
|
|
5630
|
+
* every text offset shifts by one, encoded by the single anchor [1, 0].
|
|
5631
|
+
* The searchex embeddertest for this fixture pins the same +1 shift. */
|
|
5632
|
+
bug1139: {
|
|
5633
|
+
resource: "bug_1139.pdf",
|
|
5634
|
+
pageObjectNumber: 3,
|
|
5635
|
+
exactText: "Hello, world!\r\nGoodbye, world!",
|
|
5636
|
+
expectedCharCount: 31,
|
|
5637
|
+
expectedCharMap: [[1, 0]],
|
|
5638
|
+
slices: [
|
|
5639
|
+
{
|
|
5640
|
+
from: 0,
|
|
5641
|
+
to: 1,
|
|
5642
|
+
text: ""
|
|
5643
|
+
},
|
|
5644
|
+
{
|
|
5645
|
+
from: 1,
|
|
5646
|
+
to: 7,
|
|
5647
|
+
text: "Hello,"
|
|
5648
|
+
},
|
|
5649
|
+
{
|
|
5650
|
+
from: 16,
|
|
5651
|
+
to: 23,
|
|
5652
|
+
text: "Goodbye"
|
|
5653
|
+
}
|
|
5654
|
+
],
|
|
5655
|
+
search: {
|
|
5656
|
+
query: "Goodbye",
|
|
5657
|
+
charStart: 16,
|
|
5658
|
+
charCount: 7,
|
|
5659
|
+
matchedText: "Goodbye"
|
|
5660
|
+
}
|
|
5661
|
+
},
|
|
5662
|
+
/** `/ActualText` replacement: the span's real glyphs are REPLACED by one
|
|
5663
|
+
* synthetic kPiece character per ActualText character, lockstep with the
|
|
5664
|
+
* text (identity map), with synthesized evenly-divided boxes — so search
|
|
5665
|
+
* segments exist and extraction yields the ActualText, not the glyphs. */
|
|
5666
|
+
actualText: {
|
|
5667
|
+
resource: "bug_384770169.pdf",
|
|
5668
|
+
pageObjectNumber: 3,
|
|
5669
|
+
exactText: "What is my favorite food?",
|
|
5670
|
+
expectedCharCount: 25,
|
|
5671
|
+
expectedCharMap: null,
|
|
5672
|
+
slices: [{
|
|
5673
|
+
from: 11,
|
|
5674
|
+
to: 19,
|
|
5675
|
+
text: "favorite"
|
|
5676
|
+
}],
|
|
5677
|
+
search: {
|
|
5678
|
+
query: "favorite",
|
|
5679
|
+
charStart: 11,
|
|
5680
|
+
charCount: 8,
|
|
5681
|
+
matchedText: "favorite"
|
|
5682
|
+
}
|
|
5683
|
+
},
|
|
5684
|
+
/** Supplementary-plane text via a ToUnicode surrogate pair: PDFium stores
|
|
5685
|
+
* one character-list entry PER SURROGATE HALF in lockstep with the text
|
|
5686
|
+
* (identity map, no divergence in this representation) — and the emoji
|
|
5687
|
+
* SURVIVES extraction end to end. The combined-astral representation
|
|
5688
|
+
* (font-cmap path, one entry per code point → a [+2] anchor) is covered
|
|
5689
|
+
* by the charmap unit tests and the reader's divergent walk. */
|
|
5690
|
+
astralToUnicode: {
|
|
5691
|
+
resource: "embedpdf_astral_tounicode.pdf",
|
|
5692
|
+
pageObjectNumber: 3,
|
|
5693
|
+
exactText: "😀B!",
|
|
5694
|
+
expectedCharCount: 4,
|
|
5695
|
+
expectedCharMap: null,
|
|
5696
|
+
slices: [{
|
|
5697
|
+
from: 0,
|
|
5698
|
+
to: 2,
|
|
5699
|
+
text: "😀"
|
|
5700
|
+
}, {
|
|
5701
|
+
from: 2,
|
|
5702
|
+
to: 3,
|
|
5703
|
+
text: "B"
|
|
5704
|
+
}],
|
|
5705
|
+
search: {
|
|
5706
|
+
query: "B!",
|
|
5707
|
+
charStart: 2,
|
|
5708
|
+
charCount: 2,
|
|
5709
|
+
matchedText: "B!"
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5712
|
+
};
|
|
5713
|
+
//#endregion
|
|
5714
|
+
//#region src/conformance/runPageGeometryOrientationConformance.ts
|
|
5715
|
+
const FLAG_EMPTY = 2;
|
|
5716
|
+
/** Absolute tolerance for coordinate assertions (PDF points). */
|
|
5717
|
+
const COORD_TOLERANCE = .001;
|
|
5718
|
+
/** Tolerance for baseline-angle assertions (radians). */
|
|
5719
|
+
const ANGLE_TOLERANCE = .001;
|
|
5720
|
+
function runPageGeometryOrientationConformance(runner, opts) {
|
|
5721
|
+
const { describe, test, beforeAll, afterAll, expect } = runner;
|
|
5722
|
+
describe(`page geometry orientation conformance: ${opts.label} [${opts.fixture.id}]`, () => {
|
|
5723
|
+
let engine;
|
|
5724
|
+
beforeAll(async () => {
|
|
5725
|
+
engine = await opts.makeEngine();
|
|
5726
|
+
});
|
|
5727
|
+
afterAll(async () => {
|
|
5728
|
+
if (engine) await engine.destroy();
|
|
5729
|
+
});
|
|
5730
|
+
const readSnapshot = async () => {
|
|
5731
|
+
const doc = await openFixture$1(engine, opts);
|
|
5732
|
+
return {
|
|
5733
|
+
snapshot: await doc.page(opts.fixture.pageObjectNumber).geometry.read(),
|
|
5734
|
+
close: () => doc.close()
|
|
5735
|
+
};
|
|
5736
|
+
};
|
|
5737
|
+
test("snapshot round-trips the wire schema", async () => {
|
|
5738
|
+
const { snapshot, close } = await readSnapshot();
|
|
5739
|
+
try {
|
|
5740
|
+
const parsed = PageGeometrySnapshotSchema.safeParse(snapshot);
|
|
5741
|
+
expect(parsed.success).toBe(true);
|
|
5742
|
+
if (parsed.success) expect(parsed.data).toEqual(snapshot);
|
|
5743
|
+
} finally {
|
|
5744
|
+
await close();
|
|
5745
|
+
}
|
|
5746
|
+
});
|
|
5747
|
+
test("run charStart indices tile the page glyph sequence", async () => {
|
|
5748
|
+
const { snapshot, close } = await readSnapshot();
|
|
5749
|
+
try {
|
|
5750
|
+
let next = 0;
|
|
5751
|
+
for (const run of snapshot.runs) {
|
|
5752
|
+
expect(run.charStart).toBe(next);
|
|
5753
|
+
next += run.glyphs.length;
|
|
5754
|
+
}
|
|
5755
|
+
} finally {
|
|
5756
|
+
await close();
|
|
5757
|
+
}
|
|
5758
|
+
});
|
|
5759
|
+
test("runs match the fixture orientation expectation", async () => {
|
|
5760
|
+
const { snapshot, close } = await readSnapshot();
|
|
5761
|
+
try {
|
|
5762
|
+
const expectation = opts.fixture.expectation;
|
|
5763
|
+
if (expectation.kind === "upright") {
|
|
5764
|
+
for (const run of snapshot.runs) {
|
|
5765
|
+
expect(isRotatedGeometryRun(run)).toBe(false);
|
|
5766
|
+
if (isRotatedGeometryRun(run)) continue;
|
|
5767
|
+
for (const glyph of run.glyphs) {
|
|
5768
|
+
expect("looseQuad" in glyph).toBe(false);
|
|
5769
|
+
if (glyph.flags & FLAG_EMPTY) expect(glyph.looseBox).toEqual({
|
|
5770
|
+
left: 0,
|
|
5771
|
+
bottom: 0,
|
|
5772
|
+
right: 0,
|
|
5773
|
+
top: 0
|
|
5774
|
+
});
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
return;
|
|
5778
|
+
}
|
|
5779
|
+
if (expectation.kind === "empty-only") {
|
|
5780
|
+
for (const run of snapshot.runs) {
|
|
5781
|
+
expect(isRotatedGeometryRun(run)).toBe(false);
|
|
5782
|
+
if (isRotatedGeometryRun(run)) continue;
|
|
5783
|
+
for (const glyph of run.glyphs) {
|
|
5784
|
+
expect(glyph.flags & FLAG_EMPTY).toBe(FLAG_EMPTY);
|
|
5785
|
+
expect(glyph.looseBox).toEqual({
|
|
5786
|
+
left: 0,
|
|
5787
|
+
bottom: 0,
|
|
5788
|
+
right: 0,
|
|
5789
|
+
top: 0
|
|
5790
|
+
});
|
|
5791
|
+
}
|
|
5792
|
+
}
|
|
5793
|
+
return;
|
|
5794
|
+
}
|
|
5795
|
+
const rotatedRuns = snapshot.runs.filter(isRotatedGeometryRun);
|
|
5796
|
+
expect(rotatedRuns.length >= (expectation.minRotatedRuns ?? 1)).toBe(true);
|
|
5797
|
+
for (const run of rotatedRuns) {
|
|
5798
|
+
expect(expectation.rotations.some((angle) => angleClose(run.rotation, angle, ANGLE_TOLERANCE))).toBe(true);
|
|
5799
|
+
expect(run.ascentFlip).toBe(expectation.ascentFlip);
|
|
5800
|
+
assertRotatedRunGeometry(run);
|
|
5801
|
+
}
|
|
5802
|
+
if (expectation.sheared) {
|
|
5803
|
+
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);
|
|
5804
|
+
expect(shearedGlyphs.length > 0).toBe(true);
|
|
5805
|
+
}
|
|
5806
|
+
} finally {
|
|
5807
|
+
await close();
|
|
5808
|
+
}
|
|
5809
|
+
});
|
|
5810
|
+
function assertRotatedRunGeometry(run) {
|
|
5811
|
+
for (const glyph of run.glyphs) {
|
|
5812
|
+
if (glyph.flags & FLAG_EMPTY) {
|
|
5813
|
+
expect(glyph.looseQuad).toEqual({
|
|
5814
|
+
p1: {
|
|
5815
|
+
x: 0,
|
|
5816
|
+
y: 0
|
|
5817
|
+
},
|
|
5818
|
+
p2: {
|
|
5819
|
+
x: 0,
|
|
5820
|
+
y: 0
|
|
5821
|
+
},
|
|
5822
|
+
p3: {
|
|
5823
|
+
x: 0,
|
|
5824
|
+
y: 0
|
|
5825
|
+
},
|
|
5826
|
+
p4: {
|
|
5827
|
+
x: 0,
|
|
5828
|
+
y: 0
|
|
5829
|
+
}
|
|
5830
|
+
});
|
|
5831
|
+
continue;
|
|
5832
|
+
}
|
|
5833
|
+
const q = glyph.looseQuad;
|
|
5834
|
+
expect(Math.abs(q.p2.x - q.p1.x - (q.p4.x - q.p3.x)) <= COORD_TOLERANCE).toBe(true);
|
|
5835
|
+
expect(Math.abs(q.p2.y - q.p1.y - (q.p4.y - q.p3.y)) <= COORD_TOLERANCE).toBe(true);
|
|
5836
|
+
const bounds = pdfQuadBounds(q);
|
|
5837
|
+
expect(bounds.left >= run.rect.left - COORD_TOLERANCE).toBe(true);
|
|
5838
|
+
expect(bounds.right <= run.rect.right + COORD_TOLERANCE).toBe(true);
|
|
5839
|
+
expect(bounds.bottom >= run.rect.bottom - COORD_TOLERANCE).toBe(true);
|
|
5840
|
+
expect(bounds.top <= run.rect.top + COORD_TOLERANCE).toBe(true);
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5843
|
+
});
|
|
5844
|
+
}
|
|
5845
|
+
function angleClose(a, b, tolerance) {
|
|
5846
|
+
const delta = a - b;
|
|
5847
|
+
return Math.abs(Math.atan2(Math.sin(delta), Math.cos(delta))) <= tolerance;
|
|
5848
|
+
}
|
|
5037
5849
|
async function openFixture$1(engine, opts) {
|
|
5038
5850
|
if (opts.openKind === "bytes") {
|
|
5039
5851
|
const bytes = await opts.fixture.bytes();
|
|
@@ -5089,10 +5901,27 @@ function runSearchConformance(runner, opts) {
|
|
|
5089
5901
|
expect(matches.some((m) => m.pageObjectNumber === fixture.presentPageObjectNumber)).toBe(true);
|
|
5090
5902
|
for (const m of matches) {
|
|
5091
5903
|
expect(m.charCount > 0).toBe(true);
|
|
5092
|
-
expect(m.
|
|
5093
|
-
for (const
|
|
5094
|
-
expect(
|
|
5095
|
-
expect(
|
|
5904
|
+
expect(m.segments.length > 0).toBe(true);
|
|
5905
|
+
for (const s of m.segments) {
|
|
5906
|
+
expect(s.rect.right > s.rect.left).toBe(true);
|
|
5907
|
+
expect(s.rect.top > s.rect.bottom).toBe(true);
|
|
5908
|
+
const xs = [
|
|
5909
|
+
s.quad.p1.x,
|
|
5910
|
+
s.quad.p2.x,
|
|
5911
|
+
s.quad.p3.x,
|
|
5912
|
+
s.quad.p4.x
|
|
5913
|
+
];
|
|
5914
|
+
const ys = [
|
|
5915
|
+
s.quad.p1.y,
|
|
5916
|
+
s.quad.p2.y,
|
|
5917
|
+
s.quad.p3.y,
|
|
5918
|
+
s.quad.p4.y
|
|
5919
|
+
];
|
|
5920
|
+
expect(Math.abs(s.rect.left - Math.min(...xs)) < .001).toBe(true);
|
|
5921
|
+
expect(Math.abs(s.rect.right - Math.max(...xs)) < .001).toBe(true);
|
|
5922
|
+
expect(Math.abs(s.rect.bottom - Math.min(...ys)) < .001).toBe(true);
|
|
5923
|
+
expect(Math.abs(s.rect.top - Math.max(...ys)) < .001).toBe(true);
|
|
5924
|
+
expect(s.advance === 1 || s.advance === -1).toBe(true);
|
|
5096
5925
|
}
|
|
5097
5926
|
}
|
|
5098
5927
|
} finally {
|
|
@@ -5224,7 +6053,7 @@ function runSearchConformance(runner, opts) {
|
|
|
5224
6053
|
expect(matches.length > 0).toBe(true);
|
|
5225
6054
|
for (const m of matches) {
|
|
5226
6055
|
expect(m.charCount > 0).toBe(true);
|
|
5227
|
-
expect(m.
|
|
6056
|
+
expect(m.segments.length > 0).toBe(true);
|
|
5228
6057
|
}
|
|
5229
6058
|
} finally {
|
|
5230
6059
|
await doc.close();
|
|
@@ -5351,6 +6180,8 @@ async function openFixture(engine, opts) {
|
|
|
5351
6180
|
* are disjoint. We do compare `pageObjectNumber` and `generation` (which
|
|
5352
6181
|
* should be 0 for fresh reads on both sides). Page order is no longer part
|
|
5353
6182
|
* of `PageState`; it lives in `PageLayout.index` (see `pages.list()`).
|
|
6183
|
+
* `auditHead` is likewise not compared: it is a cloud-only reconciliation
|
|
6184
|
+
* cursor, absent on local snapshots by design.
|
|
5354
6185
|
*/
|
|
5355
6186
|
function diffAnnotationListSnapshot(a, b) {
|
|
5356
6187
|
const errs = [];
|
|
@@ -5389,6 +6220,6 @@ function diffAnnotation(i, a, b, errs) {
|
|
|
5389
6220
|
if (a.nm !== b.nm) errs.push(`annotations[${i}].nm mismatch: ${a.nm} vs ${b.nm}`);
|
|
5390
6221
|
}
|
|
5391
6222
|
//#endregion
|
|
5392
|
-
export { diffAnnotationListSnapshot, diffAnnotationListSnapshotAll, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationMutationConformance, runAnnotationReadConformance, runAttachmentConformance, runDocumentEventsConformance, runFormConformance, runMetadataConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageInsertConformance, runPageReorderConformance, runPageRotateConformance, runPageTextConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance };
|
|
6223
|
+
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
6224
|
|
|
5394
6225
|
//# sourceMappingURL=conformance.js.map
|