@beyondwork/docx-react-component 1.0.104 → 1.0.106
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/package.json +1 -1
- package/src/api/public-types.ts +3 -0
- package/src/api/v3/_create.ts +9 -2
- package/src/api/v3/ai/_audit-reference.ts +28 -0
- package/src/api/v3/ai/_pe2-evidence.ts +419 -0
- package/src/api/v3/ai/attach.ts +22 -2
- package/src/api/v3/ai/bundle.ts +18 -6
- package/src/api/v3/ai/inspect.ts +12 -2
- package/src/api/v3/ai/replacement.ts +124 -0
- package/src/api/v3/index.ts +7 -0
- package/src/api/v3/ui/_types.ts +139 -0
- package/src/api/v3/ui/index.ts +9 -0
- package/src/api/v3/ui/overlays.ts +104 -0
- package/src/api/v3/ui/viewport.ts +97 -0
- package/src/model/layout/index.ts +3 -0
- package/src/model/layout/page-graph-types.ts +118 -0
- package/src/model/layout/runtime-page-graph-types.ts +13 -0
- package/src/runtime/document-runtime.ts +39 -18
- package/src/runtime/event-refresh-hints.ts +33 -6
- package/src/runtime/geometry/geometry-facet.ts +9 -1
- package/src/runtime/geometry/geometry-index.ts +461 -10
- package/src/runtime/geometry/geometry-types.ts +6 -0
- package/src/runtime/geometry/object-handles.ts +7 -4
- package/src/runtime/layout/layout-engine-instance.ts +2 -0
- package/src/runtime/layout/layout-engine-version.ts +36 -1
- package/src/runtime/layout/page-graph.ts +697 -10
- package/src/runtime/layout/paginated-layout-engine.ts +10 -0
- package/src/runtime/layout/project-block-fragments.ts +187 -8
- package/src/runtime/layout/public-facet.ts +236 -0
- package/src/runtime/prerender/graph-canonicalize.ts +14 -0
- package/src/runtime/workflow/index.ts +1 -0
- package/src/runtime/workflow/overlay-lanes.ts +228 -0
- package/src/ui/presence-overlay-lane.ts +131 -0
- package/src/ui/ui-controller-factory.ts +21 -0
|
@@ -21,6 +21,17 @@ import type {
|
|
|
21
21
|
EditorStoryTarget,
|
|
22
22
|
PageLayoutSnapshot,
|
|
23
23
|
} from "../../api/public-types";
|
|
24
|
+
import type {
|
|
25
|
+
BlockNode,
|
|
26
|
+
FooterDocument,
|
|
27
|
+
HeaderDocument,
|
|
28
|
+
InlineNode,
|
|
29
|
+
PreserveOnlyObjectSizing,
|
|
30
|
+
} from "../../model/canonical-document.ts";
|
|
31
|
+
import {
|
|
32
|
+
formatPageNumber,
|
|
33
|
+
formatPageNumberWithChapter,
|
|
34
|
+
} from "../formatting/field/page-number-format.ts";
|
|
24
35
|
import type {
|
|
25
36
|
ResolvedPageStories,
|
|
26
37
|
} from "./page-story-resolver.ts";
|
|
@@ -54,6 +65,15 @@ export type {
|
|
|
54
65
|
RuntimeLayoutDivergenceKind,
|
|
55
66
|
RuntimeLayoutDivergence,
|
|
56
67
|
RuntimePageFrame,
|
|
68
|
+
RuntimePageLocalStoryInstance,
|
|
69
|
+
RuntimeResolvedStoryField,
|
|
70
|
+
RuntimeStoryAnchoredObject,
|
|
71
|
+
RuntimeLayoutContinuationCursor,
|
|
72
|
+
RuntimeParagraphContinuationCursor,
|
|
73
|
+
RuntimeTableContinuationCursor,
|
|
74
|
+
RuntimeTableVerticalMergeCarry,
|
|
75
|
+
RuntimeFragmentLayoutObjectKind,
|
|
76
|
+
RuntimeFragmentLayoutObject,
|
|
57
77
|
RuntimeBlockFragment,
|
|
58
78
|
RuntimeLineBox,
|
|
59
79
|
RuntimeNoteAllocation,
|
|
@@ -73,9 +93,11 @@ import type {
|
|
|
73
93
|
RuntimeNoteAllocation,
|
|
74
94
|
RuntimePageAnchor,
|
|
75
95
|
RuntimePageFrame,
|
|
96
|
+
RuntimePageLocalStoryInstance,
|
|
76
97
|
RuntimePageRegion,
|
|
77
98
|
RuntimePageRegions,
|
|
78
99
|
RuntimeResolvedRegions,
|
|
100
|
+
RuntimeStoryAnchoredObject,
|
|
79
101
|
RuntimeTwipsRect,
|
|
80
102
|
} from "../../model/layout/page-graph-types.ts";
|
|
81
103
|
import type {
|
|
@@ -90,6 +112,19 @@ import type {
|
|
|
90
112
|
|
|
91
113
|
let graphRevision = 0;
|
|
92
114
|
|
|
115
|
+
const PAGE_INSTANCE_FIELD_FAMILIES = new Set([
|
|
116
|
+
"PAGE",
|
|
117
|
+
"NUMPAGES",
|
|
118
|
+
"SECTIONPAGES",
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
const EMUS_PER_TWIP = 635;
|
|
122
|
+
|
|
123
|
+
type HeaderFooterStoryTarget = Extract<
|
|
124
|
+
EditorStoryTarget,
|
|
125
|
+
{ kind: "header" | "footer" }
|
|
126
|
+
>;
|
|
127
|
+
|
|
93
128
|
export function buildPageGraph(input: BuildPageGraphInput): RuntimePageGraph;
|
|
94
129
|
export function buildPageGraph(
|
|
95
130
|
pages: readonly DocumentPageSnapshot[],
|
|
@@ -114,6 +149,7 @@ export function buildPageGraph(
|
|
|
114
149
|
const pages: RuntimePageNode[] = [];
|
|
115
150
|
const aggregatedFragments: RuntimeBlockFragment[] = [...(input.fragments ?? [])];
|
|
116
151
|
const pageIdByGlobalPageIndex = new Map<number, string>();
|
|
152
|
+
const pageFieldCounts = buildPageFieldCounts(input.pages);
|
|
117
153
|
for (let index = 0; index < input.pages.length; index += 1) {
|
|
118
154
|
const page = input.pages[index]!;
|
|
119
155
|
pageIdByGlobalPageIndex.set(page.pageIndex, `page-${graphRevision}-${index}`);
|
|
@@ -180,17 +216,22 @@ export function buildPageGraph(
|
|
|
180
216
|
stories.displayPageNumber,
|
|
181
217
|
);
|
|
182
218
|
const regions = buildRegions(page.layout, bodyPageFragments, stories, pageNoteAllocations);
|
|
183
|
-
const
|
|
184
|
-
const
|
|
219
|
+
const frameDivergences = detectFrameDivergences(frameId, regions);
|
|
220
|
+
const builtFrame = buildPageFrame({
|
|
185
221
|
frameId,
|
|
186
222
|
pageId,
|
|
187
223
|
pageIndex: page.pageIndex,
|
|
188
224
|
sectionIndex: page.sectionIndex,
|
|
189
225
|
displayPageNumber: stories.displayPageNumber,
|
|
190
226
|
layout: page.layout,
|
|
227
|
+
stories,
|
|
191
228
|
regions,
|
|
192
|
-
divergences,
|
|
229
|
+
divergences: frameDivergences,
|
|
230
|
+
subParts: input.subParts,
|
|
231
|
+
pageFieldCounts,
|
|
193
232
|
});
|
|
233
|
+
const divergences = builtFrame.divergences;
|
|
234
|
+
const frame = builtFrame.frame;
|
|
194
235
|
|
|
195
236
|
const node: RuntimePageNode = {
|
|
196
237
|
pageId,
|
|
@@ -383,9 +424,12 @@ function buildPageFrame(input: {
|
|
|
383
424
|
sectionIndex: number;
|
|
384
425
|
displayPageNumber: number;
|
|
385
426
|
layout: PageLayoutSnapshot;
|
|
427
|
+
stories: ResolvedPageStories;
|
|
386
428
|
regions: RuntimePageRegions;
|
|
387
429
|
divergences: readonly RuntimeLayoutDivergence[];
|
|
388
|
-
|
|
430
|
+
subParts?: BuildPageGraphInput["subParts"];
|
|
431
|
+
pageFieldCounts: PageFieldCounts;
|
|
432
|
+
}): { frame: RuntimePageFrame; divergences: RuntimeLayoutDivergence[] } {
|
|
389
433
|
const regions: RuntimeResolvedRegions = {
|
|
390
434
|
body: input.regions.body,
|
|
391
435
|
exclusionZones: [],
|
|
@@ -394,18 +438,587 @@ function buildPageFrame(input: {
|
|
|
394
438
|
...(input.regions.columns ? { columns: input.regions.columns } : {}),
|
|
395
439
|
...(input.regions.footnotes ? { footnotes: input.regions.footnotes } : {}),
|
|
396
440
|
};
|
|
397
|
-
const
|
|
398
|
-
return {
|
|
441
|
+
const pageLocalStoryResult = buildPageLocalStoryInstances({
|
|
399
442
|
frameId: input.frameId,
|
|
400
443
|
pageId: input.pageId,
|
|
401
444
|
pageIndex: input.pageIndex,
|
|
402
445
|
sectionIndex: input.sectionIndex,
|
|
403
446
|
displayPageNumber: input.displayPageNumber,
|
|
404
|
-
|
|
447
|
+
layout: input.layout,
|
|
448
|
+
stories: input.stories,
|
|
405
449
|
regions,
|
|
406
|
-
|
|
407
|
-
|
|
450
|
+
subParts: input.subParts,
|
|
451
|
+
pageFieldCounts: input.pageFieldCounts,
|
|
452
|
+
});
|
|
453
|
+
const divergences = [...input.divergences, ...pageLocalStoryResult.divergences];
|
|
454
|
+
const pageLocalStories = pageLocalStoryResult.instances;
|
|
455
|
+
const divergenceIds = divergences.map((d) => d.divergenceId);
|
|
456
|
+
return {
|
|
457
|
+
frame: {
|
|
458
|
+
frameId: input.frameId,
|
|
459
|
+
pageId: input.pageId,
|
|
460
|
+
pageIndex: input.pageIndex,
|
|
461
|
+
sectionIndex: input.sectionIndex,
|
|
462
|
+
displayPageNumber: input.displayPageNumber,
|
|
463
|
+
physicalBoundsTwips: rect(0, 0, input.layout.pageWidth, input.layout.pageHeight),
|
|
464
|
+
regions,
|
|
465
|
+
pageLocalStories,
|
|
466
|
+
divergenceIds,
|
|
467
|
+
signature: buildPageFrameSignature(input, pageLocalStories, divergenceIds),
|
|
468
|
+
},
|
|
469
|
+
divergences,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function buildPageLocalStoryInstances(input: {
|
|
474
|
+
frameId: string;
|
|
475
|
+
pageId: string;
|
|
476
|
+
pageIndex: number;
|
|
477
|
+
sectionIndex: number;
|
|
478
|
+
displayPageNumber: number;
|
|
479
|
+
layout: PageLayoutSnapshot;
|
|
480
|
+
stories: ResolvedPageStories;
|
|
481
|
+
regions: RuntimeResolvedRegions;
|
|
482
|
+
subParts?: BuildPageGraphInput["subParts"];
|
|
483
|
+
pageFieldCounts: PageFieldCounts;
|
|
484
|
+
}): {
|
|
485
|
+
instances: RuntimePageLocalStoryInstance[];
|
|
486
|
+
divergences: RuntimeLayoutDivergence[];
|
|
487
|
+
} {
|
|
488
|
+
const instances: RuntimePageLocalStoryInstance[] = [];
|
|
489
|
+
const divergences: RuntimeLayoutDivergence[] = [];
|
|
490
|
+
if (isHeaderFooterStoryTarget(input.stories.header)) {
|
|
491
|
+
const built = buildPageLocalStoryInstance(
|
|
492
|
+
input.frameId,
|
|
493
|
+
input.pageId,
|
|
494
|
+
input.pageIndex,
|
|
495
|
+
input.sectionIndex,
|
|
496
|
+
input.displayPageNumber,
|
|
497
|
+
input.layout,
|
|
498
|
+
input.stories.header,
|
|
499
|
+
input.regions.header,
|
|
500
|
+
findHeaderFooterPart(input.subParts?.headers, input.stories.header),
|
|
501
|
+
input.pageFieldCounts,
|
|
502
|
+
);
|
|
503
|
+
instances.push(built.instance);
|
|
504
|
+
divergences.push(...built.divergences);
|
|
505
|
+
}
|
|
506
|
+
if (isHeaderFooterStoryTarget(input.stories.footer)) {
|
|
507
|
+
const built = buildPageLocalStoryInstance(
|
|
508
|
+
input.frameId,
|
|
509
|
+
input.pageId,
|
|
510
|
+
input.pageIndex,
|
|
511
|
+
input.sectionIndex,
|
|
512
|
+
input.displayPageNumber,
|
|
513
|
+
input.layout,
|
|
514
|
+
input.stories.footer,
|
|
515
|
+
input.regions.footer,
|
|
516
|
+
findHeaderFooterPart(input.subParts?.footers, input.stories.footer),
|
|
517
|
+
input.pageFieldCounts,
|
|
518
|
+
);
|
|
519
|
+
instances.push(built.instance);
|
|
520
|
+
divergences.push(...built.divergences);
|
|
521
|
+
}
|
|
522
|
+
return { instances, divergences };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function buildPageLocalStoryInstance(
|
|
526
|
+
frameId: string,
|
|
527
|
+
pageId: string,
|
|
528
|
+
pageIndex: number,
|
|
529
|
+
sectionIndex: number,
|
|
530
|
+
displayPageNumber: number,
|
|
531
|
+
layout: PageLayoutSnapshot,
|
|
532
|
+
target: HeaderFooterStoryTarget,
|
|
533
|
+
region: RuntimePageRegion | undefined,
|
|
534
|
+
source: HeaderDocument | FooterDocument | undefined,
|
|
535
|
+
pageFieldCounts: PageFieldCounts,
|
|
536
|
+
): {
|
|
537
|
+
instance: RuntimePageLocalStoryInstance;
|
|
538
|
+
divergences: RuntimeLayoutDivergence[];
|
|
539
|
+
} {
|
|
540
|
+
const measuredFrameHeightTwips = region?.heightTwips ?? 0;
|
|
541
|
+
const sectionPart =
|
|
542
|
+
target.sectionIndex === undefined ? "section-unknown" : `section-${target.sectionIndex}`;
|
|
543
|
+
const instanceId = `${frameId}:${target.kind}:${target.variant}:${target.relationshipId}`;
|
|
544
|
+
const storyKey = `${target.kind}:${target.relationshipId}`;
|
|
545
|
+
const resolvedFields = source
|
|
546
|
+
? collectResolvedStoryFields(source.blocks, {
|
|
547
|
+
storyKey,
|
|
548
|
+
pageIndex,
|
|
549
|
+
sectionIndex,
|
|
550
|
+
displayPageNumber,
|
|
551
|
+
layout,
|
|
552
|
+
pageFieldCounts,
|
|
553
|
+
})
|
|
554
|
+
: [];
|
|
555
|
+
const objectLedger = source
|
|
556
|
+
? collectStoryAnchoredObjects(source.blocks, {
|
|
557
|
+
frameId,
|
|
558
|
+
storyKey,
|
|
559
|
+
kind: target.kind,
|
|
560
|
+
variant: target.variant,
|
|
561
|
+
relationshipId: target.relationshipId,
|
|
562
|
+
})
|
|
563
|
+
: { objects: [], divergences: [] };
|
|
564
|
+
const signature = buildPageLocalStorySignature({
|
|
565
|
+
kind: target.kind,
|
|
566
|
+
variant: target.variant,
|
|
567
|
+
relationshipId: target.relationshipId,
|
|
568
|
+
sectionPart,
|
|
569
|
+
measuredFrameHeightTwips,
|
|
570
|
+
resolvedFields,
|
|
571
|
+
anchoredObjects: objectLedger.objects,
|
|
572
|
+
});
|
|
573
|
+
return {
|
|
574
|
+
instance: {
|
|
575
|
+
instanceId,
|
|
576
|
+
storyKey,
|
|
577
|
+
pageId,
|
|
578
|
+
kind: target.kind,
|
|
579
|
+
variant: target.variant,
|
|
580
|
+
relationshipId: target.relationshipId,
|
|
581
|
+
...(target.sectionIndex === undefined ? {} : { sectionIndex: target.sectionIndex }),
|
|
582
|
+
resolvedFields,
|
|
583
|
+
anchoredObjects: objectLedger.objects,
|
|
584
|
+
measuredFrameHeightTwips,
|
|
585
|
+
signature,
|
|
586
|
+
},
|
|
587
|
+
divergences: objectLedger.divergences,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function buildPageLocalStorySignature(input: {
|
|
592
|
+
kind: RuntimePageLocalStoryInstance["kind"];
|
|
593
|
+
variant: RuntimePageLocalStoryInstance["variant"];
|
|
594
|
+
relationshipId: string;
|
|
595
|
+
sectionPart: string;
|
|
596
|
+
measuredFrameHeightTwips: number;
|
|
597
|
+
resolvedFields: readonly RuntimePageLocalStoryInstance["resolvedFields"][number][];
|
|
598
|
+
anchoredObjects: readonly RuntimeStoryAnchoredObject[];
|
|
599
|
+
}): string {
|
|
600
|
+
return [
|
|
601
|
+
"page-local-story",
|
|
602
|
+
input.kind,
|
|
603
|
+
input.variant,
|
|
604
|
+
input.relationshipId,
|
|
605
|
+
input.sectionPart,
|
|
606
|
+
input.measuredFrameHeightTwips,
|
|
607
|
+
...input.resolvedFields.map((field) =>
|
|
608
|
+
[field.fieldId, field.family, field.displayText].join(":"),
|
|
609
|
+
),
|
|
610
|
+
...input.anchoredObjects.map((object) =>
|
|
611
|
+
[
|
|
612
|
+
object.objectId,
|
|
613
|
+
object.sourceType,
|
|
614
|
+
object.display,
|
|
615
|
+
object.extentTwips?.widthTwips ?? "",
|
|
616
|
+
object.extentTwips?.heightTwips ?? "",
|
|
617
|
+
object.relationshipIds?.join(",") ?? "",
|
|
618
|
+
object.preserveOnly ? "preserve-only" : "renderable",
|
|
619
|
+
object.divergenceIds.join(","),
|
|
620
|
+
].join(":"),
|
|
621
|
+
),
|
|
622
|
+
].join("|");
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
interface PageFieldCounts {
|
|
626
|
+
contentPageCount: number;
|
|
627
|
+
sectionContentPageCountByIndex: ReadonlyMap<number, number>;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
interface PageFieldResolutionInput {
|
|
631
|
+
storyKey: string;
|
|
632
|
+
pageIndex: number;
|
|
633
|
+
sectionIndex: number;
|
|
634
|
+
displayPageNumber: number;
|
|
635
|
+
layout: PageLayoutSnapshot;
|
|
636
|
+
pageFieldCounts: PageFieldCounts;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function buildPageFieldCounts(
|
|
640
|
+
pages: readonly Pick<DocumentPageSnapshot, "pageInSection" | "sectionIndex">[],
|
|
641
|
+
): PageFieldCounts {
|
|
642
|
+
const sectionContentPageCountByIndex = new Map<number, number>();
|
|
643
|
+
let contentPageCount = 0;
|
|
644
|
+
for (const page of pages) {
|
|
645
|
+
if (page.pageInSection === -1) continue;
|
|
646
|
+
contentPageCount += 1;
|
|
647
|
+
sectionContentPageCountByIndex.set(
|
|
648
|
+
page.sectionIndex,
|
|
649
|
+
(sectionContentPageCountByIndex.get(page.sectionIndex) ?? 0) + 1,
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
return { contentPageCount, sectionContentPageCountByIndex };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function findHeaderFooterPart<T extends HeaderDocument | FooterDocument>(
|
|
656
|
+
parts: ReadonlyArray<T> | undefined,
|
|
657
|
+
target: HeaderFooterStoryTarget,
|
|
658
|
+
): T | undefined {
|
|
659
|
+
return parts?.find((part) => part.relationshipId === target.relationshipId);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function collectResolvedStoryFields(
|
|
663
|
+
blocks: readonly BlockNode[],
|
|
664
|
+
context: PageFieldResolutionInput,
|
|
665
|
+
): RuntimePageLocalStoryInstance["resolvedFields"] {
|
|
666
|
+
const fields: RuntimePageLocalStoryInstance["resolvedFields"] = [];
|
|
667
|
+
let ordinal = 0;
|
|
668
|
+
|
|
669
|
+
const visitBlock = (block: BlockNode): void => {
|
|
670
|
+
switch (block.type) {
|
|
671
|
+
case "paragraph":
|
|
672
|
+
for (const child of block.children) visitInline(child);
|
|
673
|
+
break;
|
|
674
|
+
case "table":
|
|
675
|
+
for (const row of block.rows) {
|
|
676
|
+
for (const cell of row.cells) {
|
|
677
|
+
for (const child of cell.children) visitBlock(child);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
break;
|
|
681
|
+
case "sdt":
|
|
682
|
+
for (const child of block.children) visitBlock(child);
|
|
683
|
+
break;
|
|
684
|
+
default:
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
408
687
|
};
|
|
688
|
+
|
|
689
|
+
const visitInline = (inline: InlineNode): void => {
|
|
690
|
+
switch (inline.type) {
|
|
691
|
+
case "field": {
|
|
692
|
+
const family = inline.fieldFamily ?? classifyFieldInstructionLocal(inline.instruction);
|
|
693
|
+
if (PAGE_INSTANCE_FIELD_FAMILIES.has(family)) {
|
|
694
|
+
const fieldId = `${context.storyKey}:field-${ordinal}:${family}`;
|
|
695
|
+
fields.push({
|
|
696
|
+
fieldId,
|
|
697
|
+
family,
|
|
698
|
+
displayText: resolvePageInstanceFieldDisplayText(
|
|
699
|
+
family,
|
|
700
|
+
flattenInline(inline.children),
|
|
701
|
+
context,
|
|
702
|
+
),
|
|
703
|
+
});
|
|
704
|
+
ordinal += 1;
|
|
705
|
+
}
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
case "hyperlink":
|
|
709
|
+
for (const child of inline.children) visitInline(child);
|
|
710
|
+
break;
|
|
711
|
+
default:
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
for (const block of blocks) visitBlock(block);
|
|
717
|
+
return fields;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
interface StoryObjectContext {
|
|
721
|
+
frameId: string;
|
|
722
|
+
storyKey: string;
|
|
723
|
+
kind: "header" | "footer";
|
|
724
|
+
variant: RuntimePageLocalStoryInstance["variant"];
|
|
725
|
+
relationshipId: string;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function collectStoryAnchoredObjects(
|
|
729
|
+
blocks: readonly BlockNode[],
|
|
730
|
+
context: StoryObjectContext,
|
|
731
|
+
): {
|
|
732
|
+
objects: RuntimeStoryAnchoredObject[];
|
|
733
|
+
divergences: RuntimeLayoutDivergence[];
|
|
734
|
+
} {
|
|
735
|
+
const objects: RuntimeStoryAnchoredObject[] = [];
|
|
736
|
+
const divergences: RuntimeLayoutDivergence[] = [];
|
|
737
|
+
let ordinal = 0;
|
|
738
|
+
|
|
739
|
+
const pushObject = (
|
|
740
|
+
object: Omit<RuntimeStoryAnchoredObject, "objectId" | "divergenceIds"> & {
|
|
741
|
+
objectId?: string;
|
|
742
|
+
preserveHint?: PreserveOnlyObjectSizing;
|
|
743
|
+
wrapMode?: string;
|
|
744
|
+
},
|
|
745
|
+
): void => {
|
|
746
|
+
const objectId =
|
|
747
|
+
object.objectId ?? `${context.storyKey}:object-${ordinal}:${object.sourceType}`;
|
|
748
|
+
const objectDivergenceIds: string[] = [];
|
|
749
|
+
const objectDisplay = object.display;
|
|
750
|
+
|
|
751
|
+
if (object.preserveOnly || object.preserveHint) {
|
|
752
|
+
const divergenceId = `${context.frameId}:preserve-only-placeholder:${objectId}`;
|
|
753
|
+
objectDivergenceIds.push(divergenceId);
|
|
754
|
+
divergences.push({
|
|
755
|
+
divergenceId,
|
|
756
|
+
kind: "preserve-only-placeholder",
|
|
757
|
+
source: "runtime",
|
|
758
|
+
severity: "info",
|
|
759
|
+
message: `Page-local ${context.kind} story contains preserve-only object ${objectId}`,
|
|
760
|
+
regionKinds: [context.kind],
|
|
761
|
+
objectIds: [objectId],
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const wrapMode = object.wrapMode;
|
|
766
|
+
if (
|
|
767
|
+
objectDisplay === "floating" &&
|
|
768
|
+
wrapMode !== undefined &&
|
|
769
|
+
wrapMode !== "none" &&
|
|
770
|
+
wrapMode !== "topAndBottom"
|
|
771
|
+
) {
|
|
772
|
+
const divergenceId = `${context.frameId}:unsupported-wrap:${objectId}:${wrapMode}`;
|
|
773
|
+
objectDivergenceIds.push(divergenceId);
|
|
774
|
+
divergences.push({
|
|
775
|
+
divergenceId,
|
|
776
|
+
kind: "unsupported-wrap",
|
|
777
|
+
source: "runtime",
|
|
778
|
+
severity: "warning",
|
|
779
|
+
message: `Page-local ${context.kind} story object ${objectId} uses unsupported wrap mode ${wrapMode}`,
|
|
780
|
+
regionKinds: [context.kind],
|
|
781
|
+
objectIds: [objectId],
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const { preserveHint: _preserveHint, wrapMode: _wrapMode, ...ledger } = object;
|
|
786
|
+
objects.push({
|
|
787
|
+
...ledger,
|
|
788
|
+
objectId,
|
|
789
|
+
divergenceIds: objectDivergenceIds,
|
|
790
|
+
});
|
|
791
|
+
ordinal += 1;
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
const visitBlock = (block: BlockNode): void => {
|
|
795
|
+
switch (block.type) {
|
|
796
|
+
case "paragraph":
|
|
797
|
+
for (const child of block.children) visitInline(child);
|
|
798
|
+
break;
|
|
799
|
+
case "table":
|
|
800
|
+
for (const row of block.rows) {
|
|
801
|
+
for (const cell of row.cells) {
|
|
802
|
+
for (const child of cell.children) visitBlock(child);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
break;
|
|
806
|
+
case "sdt":
|
|
807
|
+
for (const child of block.children) visitBlock(child);
|
|
808
|
+
break;
|
|
809
|
+
default:
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
const visitInline = (inline: InlineNode): void => {
|
|
815
|
+
switch (inline.type) {
|
|
816
|
+
case "image": {
|
|
817
|
+
pushObject({
|
|
818
|
+
objectId: inline.mediaId,
|
|
819
|
+
sourceType: "image",
|
|
820
|
+
display: inline.display === "floating" ? "floating" : "inline",
|
|
821
|
+
preserveOnly: false,
|
|
822
|
+
wrapMode: inline.floating?.wrap,
|
|
823
|
+
});
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
case "drawing_frame": {
|
|
827
|
+
const preserveHint = getDrawingFramePreserveHint(inline);
|
|
828
|
+
const relationshipIds = collectDrawingRelationshipIds(inline);
|
|
829
|
+
const display = inline.anchor.display;
|
|
830
|
+
pushObject({
|
|
831
|
+
objectId: getDrawingFrameObjectId(inline, context.storyKey, ordinal),
|
|
832
|
+
sourceType: "drawing-frame",
|
|
833
|
+
display,
|
|
834
|
+
extentTwips: extentTwipsFromEmu(
|
|
835
|
+
inline.anchor.extent.widthEmu,
|
|
836
|
+
inline.anchor.extent.heightEmu,
|
|
837
|
+
),
|
|
838
|
+
...(relationshipIds.length > 0 ? { relationshipIds } : {}),
|
|
839
|
+
preserveOnly: Boolean(preserveHint),
|
|
840
|
+
...(preserveHint ? { preserveHint } : {}),
|
|
841
|
+
wrapMode: inline.anchor.wrapMode,
|
|
842
|
+
});
|
|
843
|
+
if (inline.content.type === "shape") {
|
|
844
|
+
for (const child of inline.content.txbxBlocks ?? []) visitBlock(child);
|
|
845
|
+
}
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
case "chart_preview":
|
|
849
|
+
case "smartart_preview":
|
|
850
|
+
case "shape":
|
|
851
|
+
case "wordart":
|
|
852
|
+
case "vml_shape": {
|
|
853
|
+
const preserveHint = inline.preserveOnlyObject;
|
|
854
|
+
pushObject({
|
|
855
|
+
objectId: getPreserveOnlyObjectId(inline, context.storyKey, ordinal),
|
|
856
|
+
sourceType: sourceTypeForInlineObject(inline.type),
|
|
857
|
+
display: preserveHint?.display ?? "unknown",
|
|
858
|
+
...(preserveHint?.extentEmu
|
|
859
|
+
? {
|
|
860
|
+
extentTwips: extentTwipsFromEmu(
|
|
861
|
+
preserveHint.extentEmu.widthEmu,
|
|
862
|
+
preserveHint.extentEmu.heightEmu,
|
|
863
|
+
),
|
|
864
|
+
}
|
|
865
|
+
: {}),
|
|
866
|
+
...(preserveHint?.relationshipIds
|
|
867
|
+
? { relationshipIds: [...preserveHint.relationshipIds] }
|
|
868
|
+
: {}),
|
|
869
|
+
preserveOnly: Boolean(preserveHint),
|
|
870
|
+
...(preserveHint ? { preserveHint } : {}),
|
|
871
|
+
});
|
|
872
|
+
if (inline.type === "shape") {
|
|
873
|
+
for (const child of inline.txbxBlocks ?? []) visitBlock(child);
|
|
874
|
+
}
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
case "ole_embed":
|
|
878
|
+
pushObject({
|
|
879
|
+
objectId: inline.id,
|
|
880
|
+
sourceType: "ole-embed",
|
|
881
|
+
display: "unknown",
|
|
882
|
+
relationshipIds: [inline.relationshipId],
|
|
883
|
+
preserveOnly: true,
|
|
884
|
+
});
|
|
885
|
+
break;
|
|
886
|
+
case "opaque_inline":
|
|
887
|
+
pushObject({
|
|
888
|
+
objectId: inline.fragmentId,
|
|
889
|
+
sourceType: "opaque-inline",
|
|
890
|
+
display: "unknown",
|
|
891
|
+
preserveOnly: true,
|
|
892
|
+
});
|
|
893
|
+
break;
|
|
894
|
+
case "field":
|
|
895
|
+
for (const child of inline.children) visitInline(child);
|
|
896
|
+
break;
|
|
897
|
+
case "hyperlink":
|
|
898
|
+
for (const child of inline.children) visitInline(child);
|
|
899
|
+
break;
|
|
900
|
+
default:
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
|
|
905
|
+
for (const block of blocks) visitBlock(block);
|
|
906
|
+
return { objects, divergences };
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
function getDrawingFramePreserveHint(
|
|
910
|
+
inline: Extract<InlineNode, { type: "drawing_frame" }>,
|
|
911
|
+
): PreserveOnlyObjectSizing | undefined {
|
|
912
|
+
const content = inline.content;
|
|
913
|
+
if (content.type === "picture") return undefined;
|
|
914
|
+
return content.preserveOnlyObject;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function collectDrawingRelationshipIds(
|
|
918
|
+
inline: Extract<InlineNode, { type: "drawing_frame" }>,
|
|
919
|
+
): string[] {
|
|
920
|
+
const content = inline.content;
|
|
921
|
+
if (content.type === "picture") return [content.blipRef];
|
|
922
|
+
const preserveIds = content.preserveOnlyObject?.relationshipIds ?? [];
|
|
923
|
+
return [...preserveIds];
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function getDrawingFrameObjectId(
|
|
927
|
+
inline: Extract<InlineNode, { type: "drawing_frame" }>,
|
|
928
|
+
storyKey: string,
|
|
929
|
+
ordinal: number,
|
|
930
|
+
): string {
|
|
931
|
+
if (inline.anchor.docPr?.id) return `${storyKey}:drawing-${inline.anchor.docPr.id}`;
|
|
932
|
+
if (inline.content.type === "picture") return `${storyKey}:picture-${inline.content.blipRef}`;
|
|
933
|
+
return `${storyKey}:drawing-${ordinal}`;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function getPreserveOnlyObjectId(
|
|
937
|
+
inline: Extract<
|
|
938
|
+
InlineNode,
|
|
939
|
+
{ type: "chart_preview" | "smartart_preview" | "shape" | "wordart" | "vml_shape" }
|
|
940
|
+
>,
|
|
941
|
+
storyKey: string,
|
|
942
|
+
ordinal: number,
|
|
943
|
+
): string {
|
|
944
|
+
return inline.preserveOnlyObject?.sourceId ?? `${storyKey}:${inline.type}-${ordinal}`;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function sourceTypeForInlineObject(
|
|
948
|
+
type: Extract<
|
|
949
|
+
InlineNode,
|
|
950
|
+
{ type: "chart_preview" | "smartart_preview" | "shape" | "wordart" | "vml_shape" }
|
|
951
|
+
>["type"],
|
|
952
|
+
): RuntimeStoryAnchoredObject["sourceType"] {
|
|
953
|
+
switch (type) {
|
|
954
|
+
case "chart_preview":
|
|
955
|
+
return "chart-preview";
|
|
956
|
+
case "smartart_preview":
|
|
957
|
+
return "smartart-preview";
|
|
958
|
+
case "shape":
|
|
959
|
+
return "shape";
|
|
960
|
+
case "wordart":
|
|
961
|
+
return "wordart";
|
|
962
|
+
case "vml_shape":
|
|
963
|
+
return "vml-shape";
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function extentTwipsFromEmu(
|
|
968
|
+
widthEmu: number,
|
|
969
|
+
heightEmu: number,
|
|
970
|
+
): RuntimeStoryAnchoredObject["extentTwips"] {
|
|
971
|
+
return {
|
|
972
|
+
widthTwips: Math.max(0, Math.round(widthEmu / EMUS_PER_TWIP)),
|
|
973
|
+
heightTwips: Math.max(0, Math.round(heightEmu / EMUS_PER_TWIP)),
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function resolvePageInstanceFieldDisplayText(
|
|
978
|
+
family: string,
|
|
979
|
+
cachedDisplayText: string,
|
|
980
|
+
context: PageFieldResolutionInput,
|
|
981
|
+
): string {
|
|
982
|
+
switch (family) {
|
|
983
|
+
case "PAGE":
|
|
984
|
+
return formatPageNumberWithChapter(
|
|
985
|
+
context.displayPageNumber,
|
|
986
|
+
context.layout.pageNumbering,
|
|
987
|
+
);
|
|
988
|
+
case "NUMPAGES":
|
|
989
|
+
return formatPageNumber(
|
|
990
|
+
context.pageFieldCounts.contentPageCount,
|
|
991
|
+
context.layout.pageNumbering?.format,
|
|
992
|
+
);
|
|
993
|
+
case "SECTIONPAGES":
|
|
994
|
+
return formatPageNumber(
|
|
995
|
+
context.pageFieldCounts.sectionContentPageCountByIndex.get(context.sectionIndex) ?? 0,
|
|
996
|
+
context.layout.pageNumbering?.format,
|
|
997
|
+
);
|
|
998
|
+
default:
|
|
999
|
+
return cachedDisplayText;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function flattenInline(inlines: ReadonlyArray<InlineNode> | undefined): string {
|
|
1004
|
+
if (!inlines) return "";
|
|
1005
|
+
let buf = "";
|
|
1006
|
+
for (const inline of inlines) {
|
|
1007
|
+
if (inline.type === "text") buf += inline.text;
|
|
1008
|
+
else if (inline.type === "hard_break" || inline.type === "tab") buf += " ";
|
|
1009
|
+
}
|
|
1010
|
+
return buf;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
function classifyFieldInstructionLocal(instr: string): string {
|
|
1014
|
+
const match = /^\s*(\w+)/.exec(instr);
|
|
1015
|
+
return match ? match[1]!.toUpperCase() : "UNKNOWN";
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function isHeaderFooterStoryTarget(
|
|
1019
|
+
target: EditorStoryTarget | undefined,
|
|
1020
|
+
): target is HeaderFooterStoryTarget {
|
|
1021
|
+
return target?.kind === "header" || target?.kind === "footer";
|
|
409
1022
|
}
|
|
410
1023
|
|
|
411
1024
|
function buildPageFrameId(
|
|
@@ -424,6 +1037,7 @@ function buildPageFrameSignature(
|
|
|
424
1037
|
layout: PageLayoutSnapshot;
|
|
425
1038
|
regions: RuntimePageRegions;
|
|
426
1039
|
},
|
|
1040
|
+
pageLocalStories: readonly RuntimePageLocalStoryInstance[],
|
|
427
1041
|
divergenceIds: readonly string[],
|
|
428
1042
|
): string {
|
|
429
1043
|
const regionParts = collectRegions(input.regions).map((region) => {
|
|
@@ -445,6 +1059,7 @@ function buildPageFrameSignature(
|
|
|
445
1059
|
input.layout.pageWidth,
|
|
446
1060
|
input.layout.pageHeight,
|
|
447
1061
|
...regionParts,
|
|
1062
|
+
...pageLocalStories.map((story) => story.signature),
|
|
448
1063
|
...divergenceIds,
|
|
449
1064
|
].join("|");
|
|
450
1065
|
}
|
|
@@ -654,10 +1269,11 @@ export function spliceGraph(
|
|
|
654
1269
|
}));
|
|
655
1270
|
|
|
656
1271
|
const contentPageCount = nextPages.filter((p) => !p.isBlankFiller).length;
|
|
1272
|
+
const normalizedPages = normalizePageLocalStoryFieldsForPages(nextPages);
|
|
657
1273
|
|
|
658
1274
|
return {
|
|
659
1275
|
revision: graphRevision,
|
|
660
|
-
pages:
|
|
1276
|
+
pages: normalizedPages,
|
|
661
1277
|
fragments: mergedFragments,
|
|
662
1278
|
anchors,
|
|
663
1279
|
sections: [...prior.sections],
|
|
@@ -665,6 +1281,77 @@ export function spliceGraph(
|
|
|
665
1281
|
};
|
|
666
1282
|
}
|
|
667
1283
|
|
|
1284
|
+
function normalizePageLocalStoryFieldsForPages(
|
|
1285
|
+
pages: readonly RuntimePageNode[],
|
|
1286
|
+
): RuntimePageNode[] {
|
|
1287
|
+
const pageFieldCounts = buildPageFieldCounts(pages);
|
|
1288
|
+
return pages.map((page) => {
|
|
1289
|
+
const frame = page.frame;
|
|
1290
|
+
if (!frame || frame.pageLocalStories.every((story) => story.resolvedFields.length === 0)) {
|
|
1291
|
+
return page;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
let changed = false;
|
|
1295
|
+
const pageLocalStories = frame.pageLocalStories.map((story) => {
|
|
1296
|
+
if (story.resolvedFields.length === 0) return story;
|
|
1297
|
+
let storyChanged = false;
|
|
1298
|
+
const resolvedFields = story.resolvedFields.map((field) => {
|
|
1299
|
+
const displayText = resolvePageInstanceFieldDisplayText(
|
|
1300
|
+
field.family,
|
|
1301
|
+
field.displayText,
|
|
1302
|
+
{
|
|
1303
|
+
storyKey: story.storyKey,
|
|
1304
|
+
pageIndex: page.pageIndex,
|
|
1305
|
+
sectionIndex: page.sectionIndex,
|
|
1306
|
+
displayPageNumber: page.stories.displayPageNumber,
|
|
1307
|
+
layout: page.layout,
|
|
1308
|
+
pageFieldCounts,
|
|
1309
|
+
},
|
|
1310
|
+
);
|
|
1311
|
+
if (displayText === field.displayText) return field;
|
|
1312
|
+
storyChanged = true;
|
|
1313
|
+
changed = true;
|
|
1314
|
+
return { ...field, displayText };
|
|
1315
|
+
});
|
|
1316
|
+
if (!storyChanged) return story;
|
|
1317
|
+
const sectionPart =
|
|
1318
|
+
story.sectionIndex === undefined ? "section-unknown" : `section-${story.sectionIndex}`;
|
|
1319
|
+
return {
|
|
1320
|
+
...story,
|
|
1321
|
+
resolvedFields,
|
|
1322
|
+
signature: buildPageLocalStorySignature({
|
|
1323
|
+
kind: story.kind,
|
|
1324
|
+
variant: story.variant,
|
|
1325
|
+
relationshipId: story.relationshipId,
|
|
1326
|
+
sectionPart,
|
|
1327
|
+
measuredFrameHeightTwips: story.measuredFrameHeightTwips,
|
|
1328
|
+
resolvedFields,
|
|
1329
|
+
anchoredObjects: story.anchoredObjects,
|
|
1330
|
+
}),
|
|
1331
|
+
};
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
if (!changed) return page;
|
|
1335
|
+
const divergenceIds = frame.divergenceIds;
|
|
1336
|
+
const nextFrame: RuntimePageFrame = {
|
|
1337
|
+
...frame,
|
|
1338
|
+
pageLocalStories,
|
|
1339
|
+
signature: buildPageFrameSignature(
|
|
1340
|
+
{
|
|
1341
|
+
pageIndex: frame.pageIndex,
|
|
1342
|
+
sectionIndex: frame.sectionIndex,
|
|
1343
|
+
displayPageNumber: frame.displayPageNumber,
|
|
1344
|
+
layout: page.layout,
|
|
1345
|
+
regions: page.regions,
|
|
1346
|
+
},
|
|
1347
|
+
pageLocalStories,
|
|
1348
|
+
divergenceIds,
|
|
1349
|
+
),
|
|
1350
|
+
};
|
|
1351
|
+
return { ...page, frame: nextFrame };
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
|
|
668
1355
|
/**
|
|
669
1356
|
* Compare two `RuntimePageNode`s by the fields that matter for
|
|
670
1357
|
* pagination identity — if these all match, the fresh node represents
|