@beyondwork/docx-react-component 1.0.55 → 1.0.57
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 +43 -32
- package/src/api/public-types.ts +157 -0
- package/src/compare/diff-engine.ts +3 -0
- package/src/core/commands/formatting-commands.ts +1 -0
- package/src/core/commands/index.ts +17 -11
- package/src/core/selection/mapping.ts +18 -1
- package/src/core/selection/review-anchors.ts +29 -18
- package/src/io/chart-preview-resolver.ts +175 -41
- package/src/io/docx-session.ts +57 -2
- package/src/io/export/serialize-main-document.ts +82 -0
- package/src/io/export/serialize-styles.ts +61 -3
- package/src/io/export/table-properties-xml.ts +19 -4
- package/src/io/normalize/normalize-text.ts +33 -0
- package/src/io/ooxml/parse-anchor.ts +182 -0
- package/src/io/ooxml/parse-drawing.ts +319 -0
- package/src/io/ooxml/parse-fields.ts +115 -2
- package/src/io/ooxml/parse-fill.ts +215 -0
- package/src/io/ooxml/parse-font-table.ts +190 -0
- package/src/io/ooxml/parse-footnotes.ts +52 -1
- package/src/io/ooxml/parse-main-document.ts +241 -1
- package/src/io/ooxml/parse-numbering.ts +96 -0
- package/src/io/ooxml/parse-picture.ts +107 -0
- package/src/io/ooxml/parse-settings.ts +34 -0
- package/src/io/ooxml/parse-shapes.ts +87 -0
- package/src/io/ooxml/parse-solid-fill.ts +11 -0
- package/src/io/ooxml/parse-styles.ts +74 -1
- package/src/io/ooxml/parse-theme.ts +60 -0
- package/src/io/paste/html-clipboard.ts +449 -0
- package/src/io/paste/word-clipboard.ts +5 -1
- package/src/legal/_document-root.ts +26 -0
- package/src/legal/bookmarks.ts +4 -3
- package/src/legal/cross-references.ts +3 -2
- package/src/legal/defined-terms.ts +2 -1
- package/src/legal/signature-blocks.ts +2 -1
- package/src/model/canonical-document.ts +415 -3
- package/src/runtime/chart/chart-model-store.ts +73 -10
- package/src/runtime/document-runtime.ts +693 -41
- package/src/runtime/edit-ops/index.ts +129 -0
- package/src/runtime/event-refresh-hints.ts +7 -0
- package/src/runtime/field-resolver.ts +341 -0
- package/src/runtime/footnote-resolver.ts +55 -0
- package/src/runtime/hyperlink-color-resolver.ts +13 -10
- package/src/runtime/object-grab/index.ts +51 -0
- package/src/runtime/paragraph-style-resolver.ts +105 -0
- package/src/runtime/resolved-numbering-geometry.ts +12 -0
- package/src/runtime/selection/cursor-ops.ts +186 -15
- package/src/runtime/selection/index.ts +17 -1
- package/src/runtime/structure-ops/index.ts +77 -0
- package/src/runtime/styles-cascade.ts +33 -0
- package/src/runtime/surface-projection.ts +186 -12
- package/src/runtime/theme-color-resolver.ts +189 -44
- package/src/runtime/units.ts +46 -0
- package/src/runtime/view-state.ts +13 -2
- package/src/ui/WordReviewEditor.tsx +168 -10
- package/src/ui/editor-runtime-boundary.ts +94 -1
- package/src/ui/editor-shell-view.tsx +1 -1
- package/src/ui/runtime-shortcut-dispatch.ts +17 -3
- package/src/ui-tailwind/chart/ChartSurface.tsx +36 -10
- package/src/ui-tailwind/chart/layout/plot-area.ts +120 -45
- package/src/ui-tailwind/chart/render/area.tsx +22 -4
- package/src/ui-tailwind/chart/render/bar-column.tsx +37 -11
- package/src/ui-tailwind/chart/render/bubble.tsx +6 -2
- package/src/ui-tailwind/chart/render/combo.tsx +37 -4
- package/src/ui-tailwind/chart/render/line.tsx +28 -5
- package/src/ui-tailwind/chart/render/pie.tsx +36 -16
- package/src/ui-tailwind/chart/render/progressive-render.ts +8 -1
- package/src/ui-tailwind/chart/render/scatter.tsx +9 -4
- package/src/ui-tailwind/chrome/avatar-initials.ts +15 -0
- package/src/ui-tailwind/chrome/tw-comment-preview.tsx +3 -1
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +14 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +3 -2
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +30 -11
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +15 -2
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +1 -1
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +24 -7
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +31 -12
- package/src/ui-tailwind/chrome-overlay/page-border-resolver.ts +211 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +1 -0
- package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +74 -0
- package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +65 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-border-overlay.tsx +233 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +135 -13
- package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +51 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +12 -4
- package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +32 -12
- package/src/ui-tailwind/chrome-overlay/tw-toc-outline-sidebar.tsx +133 -0
- package/src/ui-tailwind/editor-surface/chart-node-view.tsx +49 -10
- package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +119 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +236 -9
- package/src/ui-tailwind/editor-surface/pm-schema.ts +192 -11
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +28 -3
- package/src/ui-tailwind/editor-surface/shape-renderer.ts +206 -0
- package/src/ui-tailwind/editor-surface/surface-layer.ts +66 -0
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +29 -0
- package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +7 -1
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +22 -6
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +10 -16
- package/src/ui-tailwind/review/tw-health-panel.tsx +0 -25
- package/src/ui-tailwind/review/tw-rail-card.tsx +38 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +2 -2
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +5 -12
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +2 -2
- package/src/ui-tailwind/theme/editor-theme.css +1 -0
- package/src/ui-tailwind/theme/tokens.css +6 -0
- package/src/ui-tailwind/theme/tokens.ts +10 -0
- package/src/validation/compatibility-engine.ts +2 -0
- package/src/validation/docx-comment-proof.ts +12 -3
|
@@ -36,7 +36,7 @@ const CANONICAL_DOCUMENT_TOP_LEVEL_KEYS = [
|
|
|
36
36
|
"diagnostics",
|
|
37
37
|
] as const;
|
|
38
38
|
|
|
39
|
-
const CANONICAL_DOCUMENT_OPTIONAL_KEYS = ["subParts", "fieldRegistry"] as const;
|
|
39
|
+
const CANONICAL_DOCUMENT_OPTIONAL_KEYS = ["subParts", "fieldRegistry", "fontTable"] as const;
|
|
40
40
|
|
|
41
41
|
const ID_PATTERNS = {
|
|
42
42
|
styleId: /^[A-Za-z_][A-Za-z0-9._-]{0,127}$/,
|
|
@@ -72,8 +72,11 @@ export interface CanonicalDocument {
|
|
|
72
72
|
subParts?: SubPartsCatalog;
|
|
73
73
|
/** Package-backed field registry for supported field families. */
|
|
74
74
|
fieldRegistry?: FieldRegistry;
|
|
75
|
+
/** Parsed fontTable.xml. Undefined when the package has no fontTable part. */
|
|
76
|
+
fontTable?: CanonicalFontTable;
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
|
|
77
80
|
export interface DocumentMetadata {
|
|
78
81
|
title?: string;
|
|
79
82
|
subject?: string;
|
|
@@ -89,11 +92,34 @@ export interface StylesCatalog {
|
|
|
89
92
|
paragraphs: Record<string, ParagraphStyleDefinition>;
|
|
90
93
|
characters: Record<string, CharacterStyleDefinition>;
|
|
91
94
|
tables: Record<string, TableStyleDefinition>;
|
|
95
|
+
/**
|
|
96
|
+
* `w:type="numbering"` styles — preserve-only. Word emits these for
|
|
97
|
+
* style-linked list families (List Bullet, List Number). Canonical
|
|
98
|
+
* consumers do not apply numbering via these styles; they survive
|
|
99
|
+
* only for round-trip fidelity.
|
|
100
|
+
*/
|
|
101
|
+
numberingStyles?: Record<string, NumberingStyleDefinition>;
|
|
92
102
|
latentStyles?: Record<string, LatentStyleDefinition>;
|
|
93
103
|
fromPackage?: boolean;
|
|
94
104
|
docDefaults?: DocumentDefaults;
|
|
95
105
|
}
|
|
96
106
|
|
|
107
|
+
/** A `<w:style w:type="numbering">` entry. Preserve-only. */
|
|
108
|
+
export interface NumberingStyleDefinition {
|
|
109
|
+
styleId: string;
|
|
110
|
+
displayName: string;
|
|
111
|
+
kind: "numbering";
|
|
112
|
+
isDefault: boolean;
|
|
113
|
+
basedOn?: string;
|
|
114
|
+
aliases?: string[];
|
|
115
|
+
/**
|
|
116
|
+
* The `<w:numId w:val="..."/>` inside the `<w:pPr><w:numPr>` subtree,
|
|
117
|
+
* when present. Consumers that later resolve numbering-type styles to
|
|
118
|
+
* numbering instances (Lane 3a) will use this.
|
|
119
|
+
*/
|
|
120
|
+
numberingInstanceId?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
97
123
|
export interface ParagraphStyleDefinition {
|
|
98
124
|
styleId: string;
|
|
99
125
|
basedOn?: string;
|
|
@@ -101,6 +127,17 @@ export interface ParagraphStyleDefinition {
|
|
|
101
127
|
outlineLevel?: number;
|
|
102
128
|
numbering?: ParagraphStyleNumberingReference;
|
|
103
129
|
displayName: string;
|
|
130
|
+
/**
|
|
131
|
+
* Alternate display names parsed from `<w:aliases w:val="A,B"/>`
|
|
132
|
+
* (ECMA-376 §17.7.4.2). Comma-separated in source; stored as array.
|
|
133
|
+
*/
|
|
134
|
+
aliases?: string[];
|
|
135
|
+
/**
|
|
136
|
+
* `<w:autoRedefine/>` — when true, Word re-records paragraph direct
|
|
137
|
+
* formatting into the style definition as the user types (ECMA-376
|
|
138
|
+
* §17.7.4.3). Round-trip preservation only; no render effect.
|
|
139
|
+
*/
|
|
140
|
+
autoRedefine?: boolean;
|
|
104
141
|
kind: "paragraph";
|
|
105
142
|
isDefault: boolean;
|
|
106
143
|
paragraphProperties?: CanonicalParagraphFormatting;
|
|
@@ -124,6 +161,8 @@ export interface CharacterStyleDefinition {
|
|
|
124
161
|
styleId: string;
|
|
125
162
|
basedOn?: string;
|
|
126
163
|
displayName: string;
|
|
164
|
+
/** See `ParagraphStyleDefinition.aliases`. */
|
|
165
|
+
aliases?: string[];
|
|
127
166
|
kind: "character";
|
|
128
167
|
isDefault: boolean;
|
|
129
168
|
runProperties?: CanonicalRunFormatting;
|
|
@@ -139,6 +178,8 @@ export interface TableStyleDefinition {
|
|
|
139
178
|
styleId: string;
|
|
140
179
|
basedOn?: string;
|
|
141
180
|
displayName: string;
|
|
181
|
+
/** See `ParagraphStyleDefinition.aliases`. */
|
|
182
|
+
aliases?: string[];
|
|
142
183
|
kind: "table";
|
|
143
184
|
isDefault: boolean;
|
|
144
185
|
formatting?: TableStyleFormatting;
|
|
@@ -157,6 +198,42 @@ export interface LatentStyleDefinition {
|
|
|
157
198
|
export interface NumberingCatalog {
|
|
158
199
|
abstractDefinitions: Record<string, AbstractNumberingDefinition>;
|
|
159
200
|
instances: Record<string, NumberingInstance>;
|
|
201
|
+
/**
|
|
202
|
+
* `<w:numPicBullet>` media-catalog entries keyed by `w:numPicBulletId`.
|
|
203
|
+
* Each entry preserves the raw drawing/pic XML plus a resolved media
|
|
204
|
+
* pointer when the bullet references a relationship-backed image.
|
|
205
|
+
* Consumed by numbering-prefix when `NumberingLevelDefinition.picBulletId`
|
|
206
|
+
* is set (ECMA-376 §17.9.19). Rendering is Lane 6 territory.
|
|
207
|
+
*/
|
|
208
|
+
numPicBullets?: Record<string, NumPicBullet>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* A single `<w:numPicBullet>` entry — picture-bullet image catalog.
|
|
213
|
+
*
|
|
214
|
+
* OOXML allows either a drawingML `<w:drawing>` (modern) or a VML `<w:pict>`
|
|
215
|
+
* (legacy) inside the element. We preserve the raw XML for a byte-identical
|
|
216
|
+
* round-trip and, when possible, resolve the `r:embed` / `r:id` to a media
|
|
217
|
+
* catalog entry so the renderer can look up the bitmap without re-parsing.
|
|
218
|
+
*/
|
|
219
|
+
export interface NumPicBullet {
|
|
220
|
+
/** `w:numPicBulletId` attribute value — the lookup key. */
|
|
221
|
+
numPicBulletId: string;
|
|
222
|
+
/** Verbatim XML for the `<w:numPicBullet>` element. */
|
|
223
|
+
rawXml: string;
|
|
224
|
+
/**
|
|
225
|
+
* Resolved media-catalog media id when the bullet's inner image references
|
|
226
|
+
* a relationship we could resolve at parse time. Undefined when the
|
|
227
|
+
* bullet uses an unresolved blipRef or a VML path.
|
|
228
|
+
*/
|
|
229
|
+
mediaId?: string;
|
|
230
|
+
/**
|
|
231
|
+
* Bullet cell extent in EMU (English Metric Units) when parsed from the
|
|
232
|
+
* drawing's `wp:extent` — lets the renderer size the bullet image without
|
|
233
|
+
* round-tripping through a new measurement pass. Undefined for VML paths.
|
|
234
|
+
*/
|
|
235
|
+
widthEmu?: number;
|
|
236
|
+
heightEmu?: number;
|
|
160
237
|
}
|
|
161
238
|
|
|
162
239
|
export interface AbstractNumberingDefinition {
|
|
@@ -188,6 +265,12 @@ export interface NumberingLevelDefinition {
|
|
|
188
265
|
paragraphGeometry?: NumberingLevelParagraphGeometry;
|
|
189
266
|
runProperties?: CanonicalRunFormatting;
|
|
190
267
|
restartAfterLevel?: number;
|
|
268
|
+
/**
|
|
269
|
+
* `<w:lvlPicBulletId w:val="N"/>` reference — picks the matching entry
|
|
270
|
+
* from `NumberingCatalog.numPicBullets`. When set, the marker renders as
|
|
271
|
+
* an inline image instead of a text glyph. ECMA-376 §17.9.12.
|
|
272
|
+
*/
|
|
273
|
+
picBulletId?: string;
|
|
191
274
|
}
|
|
192
275
|
|
|
193
276
|
export interface NumberingLevelOverrideDefinition {
|
|
@@ -257,9 +340,28 @@ export interface FootnoteDefinition {
|
|
|
257
340
|
blocks: BlockNode[];
|
|
258
341
|
}
|
|
259
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Separator and continuation-separator content parsed from the special
|
|
345
|
+
* footnote/endnote entries with `w:type="separator"` and
|
|
346
|
+
* `w:type="continuationSeparator"`.
|
|
347
|
+
*
|
|
348
|
+
* Content is stored as raw inner XML (the run children of the <w:p>) so
|
|
349
|
+
* Lane 3a page-chrome can render the horizontal rule without re-parsing.
|
|
350
|
+
*/
|
|
351
|
+
export interface FootnoteSeparators {
|
|
352
|
+
/** Raw XML of the <w:r> children inside the separator paragraph. */
|
|
353
|
+
separatorContent?: string;
|
|
354
|
+
/** Raw XML of the <w:r> children inside the continuationSeparator paragraph. */
|
|
355
|
+
continuationSeparatorContent?: string;
|
|
356
|
+
}
|
|
357
|
+
|
|
260
358
|
export interface FootnoteCollection {
|
|
261
359
|
footnotes: Record<string, FootnoteDefinition>;
|
|
262
360
|
endnotes: Record<string, FootnoteDefinition>;
|
|
361
|
+
/** Separator content parsed from footnotes.xml special entries. */
|
|
362
|
+
footnoteSeparators?: FootnoteSeparators;
|
|
363
|
+
/** Separator content parsed from endnotes.xml special entries. */
|
|
364
|
+
endnoteSeparators?: FootnoteSeparators;
|
|
263
365
|
}
|
|
264
366
|
|
|
265
367
|
export interface ThemeColorScheme {
|
|
@@ -336,6 +438,12 @@ export interface DocumentSettings {
|
|
|
336
438
|
* appears in the re-emitted output.
|
|
337
439
|
*/
|
|
338
440
|
unmodelledSettingsChildren?: string[];
|
|
441
|
+
/**
|
|
442
|
+
* Parsed from `<w:clrSchemeMapping>` in settings.xml. Maps document-level
|
|
443
|
+
* color style slot names to physical clrScheme slots. Absent when the
|
|
444
|
+
* element was not present in the document (identity mapping applies).
|
|
445
|
+
*/
|
|
446
|
+
clrSchemeMapping?: ClrSchemeMapping;
|
|
339
447
|
}
|
|
340
448
|
|
|
341
449
|
export interface SubPartsCatalog {
|
|
@@ -346,6 +454,12 @@ export interface SubPartsCatalog {
|
|
|
346
454
|
finalSectionProperties?: SectionProperties;
|
|
347
455
|
resolvedTheme?: ResolvedTheme;
|
|
348
456
|
settings?: DocumentSettings;
|
|
457
|
+
/**
|
|
458
|
+
* Fully materialized theme for runtime resolution — combines theme1.xml
|
|
459
|
+
* color/font scheme with the clrSchemeMapping from settings.xml.
|
|
460
|
+
* Available after CO1 load wiring lands in docx-session.ts.
|
|
461
|
+
*/
|
|
462
|
+
canonicalTheme?: CanonicalTheme;
|
|
349
463
|
}
|
|
350
464
|
|
|
351
465
|
export interface ResolvedTheme {
|
|
@@ -354,6 +468,55 @@ export interface ResolvedTheme {
|
|
|
354
468
|
minorFont?: string;
|
|
355
469
|
}
|
|
356
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Document-level color style slot name from `<w:clrSchemeMapping>`.
|
|
473
|
+
* These are the attribute names on the XML element (e.g. `w:bg1`).
|
|
474
|
+
*/
|
|
475
|
+
export type ClrSchemeMappingSlot =
|
|
476
|
+
| "bg1" | "bg2" | "t1" | "t2"
|
|
477
|
+
| "accent1" | "accent2" | "accent3" | "accent4" | "accent5" | "accent6"
|
|
478
|
+
| "hlink" | "followedHyperlink";
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Physical slot name in the `<a:clrScheme>` color scheme (theme1.xml).
|
|
482
|
+
*/
|
|
483
|
+
export type ThemeColorSlot =
|
|
484
|
+
| "dk1" | "lt1" | "dk2" | "lt2"
|
|
485
|
+
| "accent1" | "accent2" | "accent3" | "accent4" | "accent5" | "accent6"
|
|
486
|
+
| "hlink" | "folHlink";
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Re-maps document-level color style slot names to physical `clrScheme` slot
|
|
490
|
+
* names. Parsed from `<w:clrSchemeMapping>` in settings.xml.
|
|
491
|
+
*
|
|
492
|
+
* Keys are the attribute local names (e.g. "bg1", "t1", "accent1").
|
|
493
|
+
* Values are the target clrScheme slot (e.g. "lt1", "dk1").
|
|
494
|
+
*
|
|
495
|
+
* An absent key means identity mapping — the slot resolves to its own name.
|
|
496
|
+
*/
|
|
497
|
+
export type ClrSchemeMapping = Partial<Record<ClrSchemeMappingSlot, ThemeColorSlot>>;
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Fully materialized theme for runtime resolution. Combines the color scheme,
|
|
501
|
+
* optional font scheme, and the clrSchemeMapping from settings.xml.
|
|
502
|
+
*
|
|
503
|
+
* `themeHash` and `clrMapHash` are stable content-derived strings used as
|
|
504
|
+
* cache keys per CLAUDE.md §3 (cache keys must be structural, not revisionToken).
|
|
505
|
+
*
|
|
506
|
+
* Populated during document load when theme1.xml is present. Absent in
|
|
507
|
+
* documents with no theme part.
|
|
508
|
+
*/
|
|
509
|
+
export interface CanonicalTheme {
|
|
510
|
+
clrScheme: ThemeColorScheme;
|
|
511
|
+
fontScheme?: ThemeFontScheme;
|
|
512
|
+
/** Resolved clrSchemeMapping: styleSlot → clrScheme slot name. Empty = identity. */
|
|
513
|
+
clrMap: ClrSchemeMapping;
|
|
514
|
+
/** Stable content hash of clrScheme.colors (sorted key:value pairs). */
|
|
515
|
+
themeHash: string;
|
|
516
|
+
/** Stable content hash of clrMap (sorted key:value pairs). */
|
|
517
|
+
clrMapHash: string;
|
|
518
|
+
}
|
|
519
|
+
|
|
357
520
|
// ---- Inline footnote reference node ----
|
|
358
521
|
|
|
359
522
|
export interface FootnoteRefNode {
|
|
@@ -391,7 +554,8 @@ export type DocumentNode =
|
|
|
391
554
|
| SmartArtPreviewNode
|
|
392
555
|
| ShapeNode
|
|
393
556
|
| WordArtNode
|
|
394
|
-
| VmlShapeNode
|
|
557
|
+
| VmlShapeNode
|
|
558
|
+
| DrawingFrameNode;
|
|
395
559
|
|
|
396
560
|
export interface DocumentRootNode {
|
|
397
561
|
type: "doc";
|
|
@@ -554,6 +718,32 @@ export interface DocumentDefaults {
|
|
|
554
718
|
run?: CanonicalRunFormatting;
|
|
555
719
|
}
|
|
556
720
|
|
|
721
|
+
/** Materialized `fontTable.xml` — one entry per `<w:font>` element. */
|
|
722
|
+
export interface CanonicalFontTable {
|
|
723
|
+
fonts: Record<string, CanonicalFontEntry>;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/** Metadata for a single `<w:font>` element from fontTable.xml. */
|
|
727
|
+
export interface CanonicalFontEntry {
|
|
728
|
+
/** `w:name` attribute — the key used in rFonts references. */
|
|
729
|
+
name: string;
|
|
730
|
+
/**
|
|
731
|
+
* `w:family` value. Qualitative category per ECMA-376 §17.8.3.10.
|
|
732
|
+
* "roman" = proportional serif, "swiss" = proportional sans-serif,
|
|
733
|
+
* "modern" = fixed-pitch, "script" = handwriting, "decorative" = display.
|
|
734
|
+
*/
|
|
735
|
+
family?: "roman" | "swiss" | "modern" | "script" | "decorative";
|
|
736
|
+
/** `w:pitch` value — "fixed" maps to monospace metrics. */
|
|
737
|
+
pitch?: "fixed" | "variable" | "default";
|
|
738
|
+
/**
|
|
739
|
+
* Windows charset code from `w:charset w:val`.
|
|
740
|
+
* 2 = Symbol font (charset override required for correct glyph mapping).
|
|
741
|
+
*/
|
|
742
|
+
charset?: number;
|
|
743
|
+
/** `w:altName` — substitution name when the primary font is unavailable. */
|
|
744
|
+
altName?: string;
|
|
745
|
+
}
|
|
746
|
+
|
|
557
747
|
export interface ParagraphNode {
|
|
558
748
|
type: "paragraph";
|
|
559
749
|
styleId?: string;
|
|
@@ -691,12 +881,25 @@ export interface TableStyleFormatting {
|
|
|
691
881
|
shading?: CellShading;
|
|
692
882
|
verticalAlign?: "top" | "center" | "bottom";
|
|
693
883
|
};
|
|
884
|
+
/**
|
|
885
|
+
* `<w:pPr>` directly inside the table style body — applies to every
|
|
886
|
+
* cell paragraph by default. Conditional-region pPr (inside
|
|
887
|
+
* `<w:tblStylePr>`) is not captured here; see Lane 3a follow-up.
|
|
888
|
+
*/
|
|
889
|
+
paragraphProperties?: CanonicalParagraphFormatting;
|
|
890
|
+
/**
|
|
891
|
+
* `<w:rPr>` directly inside the table style body — applies to every
|
|
892
|
+
* run in every cell paragraph by default.
|
|
893
|
+
*/
|
|
894
|
+
runProperties?: CanonicalRunFormatting;
|
|
694
895
|
}
|
|
695
896
|
|
|
696
897
|
export interface TableNode {
|
|
697
898
|
type: "table";
|
|
698
899
|
styleId?: string;
|
|
900
|
+
/** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
|
|
699
901
|
propertiesXml?: string;
|
|
902
|
+
unknownPropertyChildren?: UnknownPropertyChild[];
|
|
700
903
|
gridColumns: number[];
|
|
701
904
|
rows: TableRowNode[];
|
|
702
905
|
width?: TableWidth;
|
|
@@ -715,7 +918,9 @@ export interface TableNode {
|
|
|
715
918
|
|
|
716
919
|
export interface TableRowNode {
|
|
717
920
|
type: "table_row";
|
|
921
|
+
/** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
|
|
718
922
|
propertiesXml?: string;
|
|
923
|
+
unknownPropertyChildren?: UnknownPropertyChild[];
|
|
719
924
|
cells: TableCellNode[];
|
|
720
925
|
gridBefore?: number;
|
|
721
926
|
widthBefore?: TableWidth;
|
|
@@ -731,7 +936,9 @@ export interface TableRowNode {
|
|
|
731
936
|
|
|
732
937
|
export interface TableCellNode {
|
|
733
938
|
type: "table_cell";
|
|
939
|
+
/** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
|
|
734
940
|
propertiesXml?: string;
|
|
941
|
+
unknownPropertyChildren?: UnknownPropertyChild[];
|
|
735
942
|
gridSpan?: number;
|
|
736
943
|
verticalMerge?: "restart" | "continue";
|
|
737
944
|
children: BlockNode[];
|
|
@@ -893,6 +1100,8 @@ export interface FieldRegistryEntry {
|
|
|
893
1100
|
paragraphIndex: number;
|
|
894
1101
|
/** Runtime refresh status. */
|
|
895
1102
|
refreshStatus: FieldRefreshStatus;
|
|
1103
|
+
/** Parsed field switches carried from FieldNode.switches. Present only for REF/PAGEREF/NOTEREF/TOC entries with recognized switches. */
|
|
1104
|
+
switches?: FieldNode["switches"];
|
|
896
1105
|
}
|
|
897
1106
|
|
|
898
1107
|
/**
|
|
@@ -957,6 +1166,10 @@ export interface SectionProperties {
|
|
|
957
1166
|
footerReferences?: HeaderFooterReference[];
|
|
958
1167
|
sectionType?: "continuous" | "nextPage" | "evenPage" | "oddPage" | "nextColumn";
|
|
959
1168
|
titlePage?: boolean;
|
|
1169
|
+
/** Per-section footnote configuration (ECMA-376 §17.11.12). Overrides file-level defaults. */
|
|
1170
|
+
footnotePr?: FootnoteProperties;
|
|
1171
|
+
/** Per-section endnote configuration (ECMA-376 §17.11.4). Overrides file-level defaults. */
|
|
1172
|
+
endnotePr?: EndnoteProperties;
|
|
960
1173
|
/**
|
|
961
1174
|
* Unmodelled direct children of `<w:sectPr>` captured verbatim for
|
|
962
1175
|
* round-trip. Mirrors `CanonicalParagraphFormatting.unknownPropertyChildren`
|
|
@@ -968,6 +1181,35 @@ export interface SectionProperties {
|
|
|
968
1181
|
unknownPropertyChildren?: UnknownPropertyChild[];
|
|
969
1182
|
}
|
|
970
1183
|
|
|
1184
|
+
/**
|
|
1185
|
+
* Per-section footnote numbering + placement configuration.
|
|
1186
|
+
*
|
|
1187
|
+
* `pos` — where footnotes render: `pageBottom` (default), `beneathText`
|
|
1188
|
+
* (immediately after content flow), `sectEnd` (at end of this section),
|
|
1189
|
+
* `docEnd` (at end of document — converts to endnote-like behavior).
|
|
1190
|
+
* `numFmt` — numbering glyph format for the footnote marker.
|
|
1191
|
+
* `numStart` — starting number for the first footnote in the restart scope.
|
|
1192
|
+
* `numRestart` — when to reset the footnote counter: `continuous` (never),
|
|
1193
|
+
* `eachSect` (at each section break), `eachPage` (at every page boundary).
|
|
1194
|
+
*
|
|
1195
|
+
* Defined per ECMA-376 §17.11.11–.18. The same child-element shape serves
|
|
1196
|
+
* both `<w:footnotePr>` (inside sectPr or settings.xml) and its endnote
|
|
1197
|
+
* counterpart; `EndnoteProperties` aliases this shape.
|
|
1198
|
+
*/
|
|
1199
|
+
export interface FootnoteProperties {
|
|
1200
|
+
pos?: "pageBottom" | "beneathText" | "sectEnd" | "docEnd";
|
|
1201
|
+
numFmt?: "decimal" | "upperRoman" | "lowerRoman" | "upperLetter" | "lowerLetter" | "chicago" | "none";
|
|
1202
|
+
numStart?: number;
|
|
1203
|
+
numRestart?: "continuous" | "eachSect" | "eachPage";
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/**
|
|
1207
|
+
* Per-section endnote configuration. Structurally identical to
|
|
1208
|
+
* `FootnoteProperties`; kept as a distinct type so future divergence
|
|
1209
|
+
* (e.g. endnote-only switches) has a home without breaking callers.
|
|
1210
|
+
*/
|
|
1211
|
+
export type EndnoteProperties = FootnoteProperties;
|
|
1212
|
+
|
|
971
1213
|
export interface PageSize {
|
|
972
1214
|
width: number;
|
|
973
1215
|
height: number;
|
|
@@ -1046,7 +1288,8 @@ export type InlineNode =
|
|
|
1046
1288
|
| SmartArtPreviewNode
|
|
1047
1289
|
| ShapeNode
|
|
1048
1290
|
| WordArtNode
|
|
1049
|
-
| VmlShapeNode
|
|
1291
|
+
| VmlShapeNode
|
|
1292
|
+
| DrawingFrameNode;
|
|
1050
1293
|
|
|
1051
1294
|
export interface TextNode {
|
|
1052
1295
|
type: "text";
|
|
@@ -1207,6 +1450,97 @@ export interface VmlShapeNode {
|
|
|
1207
1450
|
rawXml: string;
|
|
1208
1451
|
}
|
|
1209
1452
|
|
|
1453
|
+
export interface AnchorGeometry {
|
|
1454
|
+
display: "inline" | "floating";
|
|
1455
|
+
extent: { widthEmu: number; heightEmu: number };
|
|
1456
|
+
wrapMode: "none" | "square" | "tight" | "through" | "topAndBottom";
|
|
1457
|
+
positionH?: { relativeFrom: string; align?: string; offset?: number };
|
|
1458
|
+
positionV?: { relativeFrom: string; align?: string; offset?: number };
|
|
1459
|
+
distMargins?: { top?: number; bottom?: number; left?: number; right?: number };
|
|
1460
|
+
relativeHeight?: number;
|
|
1461
|
+
behindDoc?: boolean;
|
|
1462
|
+
layoutInCell?: boolean;
|
|
1463
|
+
allowOverlap?: boolean;
|
|
1464
|
+
simplePos?: boolean;
|
|
1465
|
+
docPr?: { id: string; name?: string; descr?: string };
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
export interface PictureContent {
|
|
1469
|
+
type: "picture";
|
|
1470
|
+
blipRef: string;
|
|
1471
|
+
/** Resolved media catalog ID (e.g. "media:word/media/image1.png"), populated by parse-drawing. */
|
|
1472
|
+
mediaId?: string;
|
|
1473
|
+
/** Absolute package path for media catalog lookup. */
|
|
1474
|
+
packagePartName?: string;
|
|
1475
|
+
srcRect?: { top: number; bottom: number; left: number; right: number };
|
|
1476
|
+
stretch?: boolean;
|
|
1477
|
+
rotation?: number;
|
|
1478
|
+
flipH?: boolean;
|
|
1479
|
+
flipV?: boolean;
|
|
1480
|
+
presetGeom?: string;
|
|
1481
|
+
/** Original w:drawing XML slice, preserved for lossless round-trip serialization. */
|
|
1482
|
+
rawXml?: string;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
export interface ShapeContent {
|
|
1486
|
+
type: "shape";
|
|
1487
|
+
geometry?: string;
|
|
1488
|
+
/**
|
|
1489
|
+
* Shape fill — solid, gradient, pattern, or none. srgbClr values on solid +
|
|
1490
|
+
* gradient stops + pattern fg/bg are normalized to uppercase hex. schemeClr
|
|
1491
|
+
* tokens are preserved as-is for CO1 theme-color cascade resolution.
|
|
1492
|
+
*
|
|
1493
|
+
* Gradient direction is linear (angle in 60000ths of degree) or path-based
|
|
1494
|
+
* (circle|rect|shape). Pattern uses the raw OOXML `prst` preset token.
|
|
1495
|
+
*/
|
|
1496
|
+
fill?:
|
|
1497
|
+
| { kind: "solid"; color: string; colorType: "srgbClr" | "schemeClr" }
|
|
1498
|
+
| { kind: "none" }
|
|
1499
|
+
| {
|
|
1500
|
+
kind: "gradient";
|
|
1501
|
+
stops: Array<{ pos: number; color: string; colorType: "srgbClr" | "schemeClr" }>;
|
|
1502
|
+
direction:
|
|
1503
|
+
| { kind: "linear"; angle: number; scaled?: boolean }
|
|
1504
|
+
| { kind: "path"; path: "circle" | "rect" | "shape" };
|
|
1505
|
+
rotWithShape?: boolean;
|
|
1506
|
+
}
|
|
1507
|
+
| {
|
|
1508
|
+
kind: "pattern";
|
|
1509
|
+
preset: string;
|
|
1510
|
+
fg?: { color: string; colorType: "srgbClr" | "schemeClr" };
|
|
1511
|
+
bg?: { color: string; colorType: "srgbClr" | "schemeClr" };
|
|
1512
|
+
};
|
|
1513
|
+
line?: { color?: string; widthEmu?: number; noLine?: boolean };
|
|
1514
|
+
/** True when the shape's geometry + txbxContent presence make it a text box. */
|
|
1515
|
+
isTextBox?: boolean;
|
|
1516
|
+
/** Raw w:txbxContent XML, preserved for serialization + lossless round-trip. */
|
|
1517
|
+
txbxContentXml?: string;
|
|
1518
|
+
/**
|
|
1519
|
+
* Parsed block-level structure from `w:txbxContent`, populated when a
|
|
1520
|
+
* `blockParser` callback is supplied during parse (CO4 F3.3).
|
|
1521
|
+
*
|
|
1522
|
+
* Type is deliberately structural (`{ type: string; ... }`) rather than
|
|
1523
|
+
* canonical `BlockNode[]` because the recursion stops at the parse layer
|
|
1524
|
+
* before the style + numbering normalization pass that converts
|
|
1525
|
+
* `ParsedBlockNode` → canonical `BlockNode`. Consumers that need the fully
|
|
1526
|
+
* normalized form run normalization on this subtree explicitly. Testing
|
|
1527
|
+
* that `txbxBlocks.length > 0` proves the recursion executed.
|
|
1528
|
+
*/
|
|
1529
|
+
txbxBlocks?: ReadonlyArray<{ type: string; [key: string]: unknown }>;
|
|
1530
|
+
rawXml: string;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
export interface DrawingFrameNode {
|
|
1534
|
+
type: "drawing_frame";
|
|
1535
|
+
anchor: AnchorGeometry;
|
|
1536
|
+
content:
|
|
1537
|
+
| PictureContent
|
|
1538
|
+
| ShapeContent
|
|
1539
|
+
| { type: "chart_preview"; rawXml: string }
|
|
1540
|
+
| { type: "smartart_preview"; rawXml: string }
|
|
1541
|
+
| { type: "opaque"; rawXml: string };
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1210
1544
|
export interface OpaqueBlockNode {
|
|
1211
1545
|
type: "opaque_block";
|
|
1212
1546
|
fragmentId: string;
|
|
@@ -1865,6 +2199,71 @@ function validateDocumentNode(
|
|
|
1865
2199
|
case "vml_shape":
|
|
1866
2200
|
expectString(record.rawXml, `${path}.rawXml`, issues);
|
|
1867
2201
|
return;
|
|
2202
|
+
case "drawing_frame": {
|
|
2203
|
+
const anchor = asPlainObject(record.anchor, `${path}.anchor`, issues);
|
|
2204
|
+
const content = asPlainObject(record.content, `${path}.content`, issues);
|
|
2205
|
+
if (anchor) {
|
|
2206
|
+
const extent = asPlainObject(anchor.extent, `${path}.anchor.extent`, issues);
|
|
2207
|
+
if (anchor.display !== "inline" && anchor.display !== "floating") {
|
|
2208
|
+
issues.push({
|
|
2209
|
+
path: `${path}.anchor.display`,
|
|
2210
|
+
message: "anchor.display must be 'inline' or 'floating'.",
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
if (
|
|
2214
|
+
anchor.wrapMode !== "none" &&
|
|
2215
|
+
anchor.wrapMode !== "square" &&
|
|
2216
|
+
anchor.wrapMode !== "tight" &&
|
|
2217
|
+
anchor.wrapMode !== "through" &&
|
|
2218
|
+
anchor.wrapMode !== "topAndBottom"
|
|
2219
|
+
) {
|
|
2220
|
+
issues.push({
|
|
2221
|
+
path: `${path}.anchor.wrapMode`,
|
|
2222
|
+
message: "anchor.wrapMode must be one of none, square, tight, through, or topAndBottom.",
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
if (extent) {
|
|
2226
|
+
if (typeof extent.widthEmu !== "number") {
|
|
2227
|
+
issues.push({
|
|
2228
|
+
path: `${path}.anchor.extent.widthEmu`,
|
|
2229
|
+
message: "anchor.extent.widthEmu must be a number.",
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
if (typeof extent.heightEmu !== "number") {
|
|
2233
|
+
issues.push({
|
|
2234
|
+
path: `${path}.anchor.extent.heightEmu`,
|
|
2235
|
+
message: "anchor.extent.heightEmu must be a number.",
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
if (content) {
|
|
2241
|
+
const contentType = expectString(content.type, `${path}.content.type`, issues);
|
|
2242
|
+
if (
|
|
2243
|
+
contentType !== "picture" &&
|
|
2244
|
+
contentType !== "shape" &&
|
|
2245
|
+
contentType !== "chart_preview" &&
|
|
2246
|
+
contentType !== "smartart_preview" &&
|
|
2247
|
+
contentType !== "opaque"
|
|
2248
|
+
) {
|
|
2249
|
+
issues.push({
|
|
2250
|
+
path: `${path}.content.type`,
|
|
2251
|
+
message: "drawing_frame.content.type must be picture, shape, chart_preview, smartart_preview, or opaque.",
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
if (contentType === "picture") {
|
|
2255
|
+
expectString(content.blipRef, `${path}.content.blipRef`, issues);
|
|
2256
|
+
} else if (
|
|
2257
|
+
contentType === "shape" ||
|
|
2258
|
+
contentType === "chart_preview" ||
|
|
2259
|
+
contentType === "smartart_preview" ||
|
|
2260
|
+
contentType === "opaque"
|
|
2261
|
+
) {
|
|
2262
|
+
expectString(content.rawXml, `${path}.content.rawXml`, issues);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
1868
2267
|
default:
|
|
1869
2268
|
issues.push({
|
|
1870
2269
|
path: `${path}.type`,
|
|
@@ -2656,6 +3055,19 @@ function validateDocumentNodeReferences(
|
|
|
2656
3055
|
message: "mediaId must reference an existing media catalog item.",
|
|
2657
3056
|
});
|
|
2658
3057
|
}
|
|
3058
|
+
} else if (type === "drawing_frame") {
|
|
3059
|
+
const content = asPlainObject(record.content, `${path}.content`, []);
|
|
3060
|
+
if (
|
|
3061
|
+
content &&
|
|
3062
|
+
content.type === "picture" &&
|
|
3063
|
+
typeof content.mediaId === "string" &&
|
|
3064
|
+
!references.mediaIds.has(content.mediaId)
|
|
3065
|
+
) {
|
|
3066
|
+
issues.push({
|
|
3067
|
+
path: `${path}.content.mediaId`,
|
|
3068
|
+
message: "content.mediaId must reference an existing media catalog item.",
|
|
3069
|
+
});
|
|
3070
|
+
}
|
|
2659
3071
|
} else if (type === "opaque_inline" || type === "opaque_block") {
|
|
2660
3072
|
if (typeof record.fragmentId === "string" && !references.fragmentIds.has(record.fragmentId)) {
|
|
2661
3073
|
issues.push({
|