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