@beyondwork/docx-react-component 1.0.17 → 1.0.19
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/README.md +8 -2
- package/package.json +32 -34
- package/src/api/README.md +5 -1
- package/src/api/public-types.ts +374 -4
- package/src/api/session-state.ts +58 -0
- package/src/core/commands/formatting-commands.ts +1 -0
- package/src/core/commands/image-commands.ts +147 -0
- package/src/core/commands/index.ts +5 -1
- package/src/core/commands/list-commands.ts +231 -36
- package/src/core/commands/paragraph-layout-commands.ts +339 -0
- package/src/core/commands/section-layout-commands.ts +680 -0
- package/src/core/commands/style-commands.ts +262 -0
- package/src/core/search/search-text.ts +329 -0
- package/src/core/selection/mapping.ts +41 -0
- package/src/core/state/editor-state.ts +1 -1
- package/src/index.ts +30 -0
- package/src/io/docx-session.ts +260 -39
- package/src/io/export/serialize-main-document.ts +202 -5
- package/src/io/export/serialize-numbering.ts +28 -7
- package/src/io/normalize/normalize-text.ts +63 -25
- package/src/io/ooxml/numbering-sentinels.ts +44 -0
- package/src/io/ooxml/parse-footnotes.ts +212 -20
- package/src/io/ooxml/parse-headers-footers.ts +229 -25
- package/src/io/ooxml/parse-inline-media.ts +16 -0
- package/src/io/ooxml/parse-main-document.ts +411 -6
- package/src/io/ooxml/parse-numbering.ts +7 -0
- package/src/io/ooxml/parse-settings.ts +184 -0
- package/src/io/ooxml/parse-shapes.ts +25 -0
- package/src/io/ooxml/parse-styles.ts +463 -0
- package/src/io/ooxml/parse-theme.ts +32 -0
- package/src/model/canonical-document.ts +133 -3
- package/src/model/cds-1.0.0.ts +13 -0
- package/src/model/snapshot.ts +2 -1
- package/src/runtime/document-layout.ts +332 -0
- package/src/runtime/document-navigation.ts +564 -0
- package/src/runtime/document-runtime.ts +265 -35
- package/src/runtime/document-search.ts +145 -0
- package/src/runtime/numbering-prefix.ts +47 -26
- package/src/runtime/page-layout-estimation.ts +212 -0
- package/src/runtime/read-only-diagnostics-runtime.ts +1 -0
- package/src/runtime/session-capabilities.ts +2 -0
- package/src/runtime/story-context.ts +164 -0
- package/src/runtime/story-targeting.ts +162 -0
- package/src/runtime/surface-projection.ts +239 -12
- package/src/runtime/table-schema.ts +87 -5
- package/src/runtime/view-state.ts +459 -0
- package/src/ui/WordReviewEditor.tsx +1902 -312
- package/src/ui/browser-export.ts +52 -0
- package/src/ui/headless/preserve-editor-selection.ts +5 -0
- package/src/ui/headless/selection-helpers.ts +20 -0
- package/src/ui/headless/selection-toolbar-model.ts +22 -0
- package/src/ui/headless/use-editor-keyboard.ts +6 -1
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +125 -14
- package/src/ui-tailwind/editor-surface/perf-probe.ts +107 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +45 -6
- package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +2 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +47 -5
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +95 -22
- package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +394 -77
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
- package/src/ui-tailwind/index.ts +2 -1
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
- package/src/ui-tailwind/review/tw-review-rail.tsx +6 -6
- package/src/ui-tailwind/theme/editor-theme.css +123 -0
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +291 -12
- package/src/ui-tailwind/tw-review-workspace.tsx +926 -27
- package/src/validation/compatibility-engine.ts +92 -20
- package/src/validation/diagnostics.ts +1 -0
- package/src/validation/docx-comment-proof.ts +487 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
BorderSpec,
|
|
2
3
|
TextMark,
|
|
3
4
|
ParagraphBorders,
|
|
4
5
|
ParagraphShading,
|
|
@@ -6,6 +7,16 @@ import type {
|
|
|
6
7
|
ParagraphIndentation,
|
|
7
8
|
TabStop,
|
|
8
9
|
TableLook,
|
|
10
|
+
SectionProperties,
|
|
11
|
+
PageSize,
|
|
12
|
+
PageMargins,
|
|
13
|
+
ColumnProperties,
|
|
14
|
+
PageNumbering,
|
|
15
|
+
HeaderFooterReference,
|
|
16
|
+
HeaderFooterVariant,
|
|
17
|
+
SectionDocumentGrid,
|
|
18
|
+
SectionLineNumbering,
|
|
19
|
+
SectionPageBorders,
|
|
9
20
|
} from "../../model/canonical-document.ts";
|
|
10
21
|
import type { OpcRelationship } from "./part-manifest.ts";
|
|
11
22
|
import {
|
|
@@ -18,6 +29,7 @@ import { parseShapeXml, parseVmlXml } from "./parse-shapes.ts";
|
|
|
18
29
|
|
|
19
30
|
export interface ParsedMainDocument {
|
|
20
31
|
blocks: ParsedBlockNode[];
|
|
32
|
+
finalSectionProperties?: SectionProperties;
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
export type ParsedBlockNode =
|
|
@@ -26,8 +38,15 @@ export type ParsedBlockNode =
|
|
|
26
38
|
| ParsedSdtNode
|
|
27
39
|
| ParsedCustomXmlNode
|
|
28
40
|
| ParsedAltChunkNode
|
|
41
|
+
| ParsedSectionBreakNode
|
|
29
42
|
| ParsedOpaqueBlockNode;
|
|
30
43
|
|
|
44
|
+
export interface ParsedSectionBreakNode {
|
|
45
|
+
type: "section_break";
|
|
46
|
+
sectionPropertiesXml: string;
|
|
47
|
+
sectionProperties: SectionProperties;
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
export interface ParsedParagraphNode {
|
|
32
51
|
type: "paragraph";
|
|
33
52
|
styleId?: string;
|
|
@@ -49,6 +68,8 @@ export interface ParsedParagraphNode {
|
|
|
49
68
|
bidi?: boolean;
|
|
50
69
|
suppressLineNumbers?: boolean;
|
|
51
70
|
cnfStyle?: string;
|
|
71
|
+
sectionProperties?: SectionProperties;
|
|
72
|
+
sectionPropertiesXml?: string;
|
|
52
73
|
children: ParsedInlineNode[];
|
|
53
74
|
rawXml: string;
|
|
54
75
|
}
|
|
@@ -152,6 +173,7 @@ export interface ParsedShapeInlineNode {
|
|
|
152
173
|
type: "shape";
|
|
153
174
|
text?: string;
|
|
154
175
|
geometry?: string;
|
|
176
|
+
isTextBox?: boolean;
|
|
155
177
|
rawXml: string;
|
|
156
178
|
}
|
|
157
179
|
|
|
@@ -195,6 +217,23 @@ export interface ParsedOpaqueBlockNode {
|
|
|
195
217
|
rawXml: string;
|
|
196
218
|
}
|
|
197
219
|
|
|
220
|
+
export interface ParsedSdtCheckboxState {
|
|
221
|
+
checked: boolean;
|
|
222
|
+
checkedChar?: string;
|
|
223
|
+
uncheckedChar?: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface ParsedSdtDatePickerState {
|
|
227
|
+
fullDate?: string;
|
|
228
|
+
dateFormat?: string;
|
|
229
|
+
lid?: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface ParsedSdtDropdownListItem {
|
|
233
|
+
displayText?: string;
|
|
234
|
+
value: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
198
237
|
export interface ParsedSdtNode {
|
|
199
238
|
type: "sdt";
|
|
200
239
|
properties: {
|
|
@@ -203,6 +242,11 @@ export interface ParsedSdtNode {
|
|
|
203
242
|
tag?: string;
|
|
204
243
|
lock?: string;
|
|
205
244
|
propertiesXml?: string;
|
|
245
|
+
checkbox?: ParsedSdtCheckboxState;
|
|
246
|
+
datePicker?: ParsedSdtDatePickerState;
|
|
247
|
+
dropdownList?: ParsedSdtDropdownListItem[];
|
|
248
|
+
comboBox?: ParsedSdtDropdownListItem[];
|
|
249
|
+
showingPlcHdr?: boolean;
|
|
206
250
|
};
|
|
207
251
|
children: ParsedBlockNode[];
|
|
208
252
|
rawXml: string;
|
|
@@ -290,11 +334,24 @@ export function parseMainDocumentXml(
|
|
|
290
334
|
const bodyElement = findChildElement(documentElement, "body");
|
|
291
335
|
const relationshipMap = new Map(relationships.map((relationship) => [relationship.id, relationship]));
|
|
292
336
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
337
|
+
const allBlocks = bodyElement.children
|
|
338
|
+
.filter((node): node is XmlElementNode => node.type === "element")
|
|
339
|
+
.map((node) => parseBodyChild(node, xml, relationshipMap, relationships, mediaParts, sourcePartPath));
|
|
340
|
+
|
|
341
|
+
// The last body-level sectPr is the final section properties (not an intermediate section break).
|
|
342
|
+
// Extract it from the blocks list and store it separately.
|
|
343
|
+
let finalSectionProperties: SectionProperties | undefined;
|
|
344
|
+
const blocks: ParsedBlockNode[] = [];
|
|
345
|
+
for (let i = 0; i < allBlocks.length; i++) {
|
|
346
|
+
const block = allBlocks[i];
|
|
347
|
+
if (block.type === "section_break" && i === allBlocks.length - 1) {
|
|
348
|
+
finalSectionProperties = block.sectionProperties;
|
|
349
|
+
} else {
|
|
350
|
+
blocks.push(block);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return { blocks, finalSectionProperties };
|
|
298
355
|
}
|
|
299
356
|
|
|
300
357
|
function parseBodyChild(
|
|
@@ -340,6 +397,10 @@ function parseBodyChild(
|
|
|
340
397
|
return parseAltChunkElement(node, sourceXml);
|
|
341
398
|
}
|
|
342
399
|
|
|
400
|
+
if (nodeType === "sectPr") {
|
|
401
|
+
return parseSectionBreakElement(node, sourceXml);
|
|
402
|
+
}
|
|
403
|
+
|
|
343
404
|
if (nodeType !== "p") {
|
|
344
405
|
return {
|
|
345
406
|
type: "opaque_block",
|
|
@@ -363,6 +424,8 @@ function parseBodyChild(
|
|
|
363
424
|
let bidi: ParsedParagraphNode["bidi"];
|
|
364
425
|
let suppressLineNumbers: ParsedParagraphNode["suppressLineNumbers"];
|
|
365
426
|
let cnfStyle: ParsedParagraphNode["cnfStyle"];
|
|
427
|
+
let sectionProperties: SectionProperties | undefined;
|
|
428
|
+
let sectionPropertiesXml: string | undefined;
|
|
366
429
|
let paragraphSupported = true;
|
|
367
430
|
const children: ParsedInlineNode[] = [];
|
|
368
431
|
|
|
@@ -389,6 +452,8 @@ function parseBodyChild(
|
|
|
389
452
|
bidi = readOnOffParagraphProperty(child, "bidi");
|
|
390
453
|
suppressLineNumbers = readOnOffParagraphProperty(child, "suppressLineNumbers");
|
|
391
454
|
cnfStyle = readParagraphCnfStyle(child);
|
|
455
|
+
sectionProperties = readSectionPropertiesFromPPr(child);
|
|
456
|
+
sectionPropertiesXml = readSectionPropertiesXmlFromPPr(child, sourceXml);
|
|
392
457
|
paragraphSupported = paragraphSupported && supportsParagraphProperties(child);
|
|
393
458
|
break;
|
|
394
459
|
case "r":
|
|
@@ -452,6 +517,8 @@ function parseBodyChild(
|
|
|
452
517
|
...(bidi ? { bidi } : {}),
|
|
453
518
|
...(suppressLineNumbers ? { suppressLineNumbers } : {}),
|
|
454
519
|
...(cnfStyle ? { cnfStyle } : {}),
|
|
520
|
+
...(sectionProperties ? { sectionProperties } : {}),
|
|
521
|
+
...(sectionPropertiesXml ? { sectionPropertiesXml } : {}),
|
|
455
522
|
children,
|
|
456
523
|
rawXml: sourceXml.slice(node.start, node.end),
|
|
457
524
|
};
|
|
@@ -692,7 +759,68 @@ function readSdtProperties(
|
|
|
692
759
|
properties.lock = readOptionalAttribute(child, "val");
|
|
693
760
|
continue;
|
|
694
761
|
}
|
|
695
|
-
if (
|
|
762
|
+
if (name === "showingPlcHdr") {
|
|
763
|
+
const val = readOptionalAttribute(child, "val");
|
|
764
|
+
properties.showingPlcHdr = val !== "false" && val !== "0";
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Checkbox (w14:checkbox)
|
|
769
|
+
if (name === "checkbox") {
|
|
770
|
+
properties.sdtType = "checkbox";
|
|
771
|
+
const checkedNode = findFirstDescendant(child, "checked");
|
|
772
|
+
const checkedVal = checkedNode ? (readOptionalAttribute(checkedNode, "val") ?? "0") : "0";
|
|
773
|
+
const checkedCharNode = findFirstDescendant(child, "checkedState");
|
|
774
|
+
const uncheckedCharNode = findFirstDescendant(child, "uncheckedState");
|
|
775
|
+
properties.checkbox = {
|
|
776
|
+
checked: checkedVal === "1" || checkedVal === "true",
|
|
777
|
+
...(checkedCharNode ? { checkedChar: readOptionalAttribute(checkedCharNode, "val") } : {}),
|
|
778
|
+
...(uncheckedCharNode ? { uncheckedChar: readOptionalAttribute(uncheckedCharNode, "val") } : {}),
|
|
779
|
+
};
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Date picker
|
|
784
|
+
if (name === "date") {
|
|
785
|
+
properties.sdtType = "date";
|
|
786
|
+
const fullDate = readOptionalAttribute(child, "fullDate");
|
|
787
|
+
const dateFormatNode = findFirstChild(child, "dateFormat");
|
|
788
|
+
const lidNode = findFirstChild(child, "lid");
|
|
789
|
+
properties.datePicker = {
|
|
790
|
+
...(fullDate ? { fullDate } : {}),
|
|
791
|
+
...(dateFormatNode ? { dateFormat: readOptionalAttribute(dateFormatNode, "val") } : {}),
|
|
792
|
+
...(lidNode ? { lid: readOptionalAttribute(lidNode, "val") } : {}),
|
|
793
|
+
};
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Dropdown list
|
|
798
|
+
if (name === "dropDownList") {
|
|
799
|
+
properties.sdtType = "dropDownList";
|
|
800
|
+
properties.dropdownList = readSdtListItems(child);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Combo box
|
|
805
|
+
if (name === "comboBox") {
|
|
806
|
+
properties.sdtType = "comboBox";
|
|
807
|
+
properties.comboBox = readSdtListItems(child);
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Plain text
|
|
812
|
+
if (name === "text") {
|
|
813
|
+
properties.sdtType = "plainText";
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Rich text (richText element is the default, but if explicitly present, tag it)
|
|
818
|
+
if (name === "richText") {
|
|
819
|
+
properties.sdtType = "richText";
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (!properties.sdtType && name !== "id" && name !== "placeholder" && name !== "showingPlcHdr" && name !== "rPr") {
|
|
696
824
|
properties.sdtType = name;
|
|
697
825
|
}
|
|
698
826
|
}
|
|
@@ -700,6 +828,34 @@ function readSdtProperties(
|
|
|
700
828
|
return properties;
|
|
701
829
|
}
|
|
702
830
|
|
|
831
|
+
function readSdtListItems(node: XmlElementNode): ParsedSdtDropdownListItem[] {
|
|
832
|
+
const items: ParsedSdtDropdownListItem[] = [];
|
|
833
|
+
for (const child of node.children) {
|
|
834
|
+
if (child.type !== "element" || localName(child.name) !== "listItem") continue;
|
|
835
|
+
const value = readOptionalAttribute(child, "value") ?? "";
|
|
836
|
+
const displayText = readOptionalAttribute(child, "displayText");
|
|
837
|
+
items.push({ value, ...(displayText ? { displayText } : {}) });
|
|
838
|
+
}
|
|
839
|
+
return items;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function findFirstChild(node: XmlElementNode, local: string): XmlElementNode | undefined {
|
|
843
|
+
for (const child of node.children) {
|
|
844
|
+
if (child.type === "element" && localName(child.name) === local) return child;
|
|
845
|
+
}
|
|
846
|
+
return undefined;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function findFirstDescendant(node: XmlElementNode, local: string): XmlElementNode | undefined {
|
|
850
|
+
for (const child of node.children) {
|
|
851
|
+
if (child.type !== "element") continue;
|
|
852
|
+
if (localName(child.name) === local) return child;
|
|
853
|
+
const nested = findFirstDescendant(child, local);
|
|
854
|
+
if (nested) return nested;
|
|
855
|
+
}
|
|
856
|
+
return undefined;
|
|
857
|
+
}
|
|
858
|
+
|
|
703
859
|
function readTableStyleId(node: XmlElementNode): string | undefined {
|
|
704
860
|
for (const child of node.children) {
|
|
705
861
|
if (child.type !== "element" || localName(child.name) !== "tblStyle") continue;
|
|
@@ -2007,3 +2163,252 @@ function decodeXmlEntities(value: string): string {
|
|
|
2007
2163
|
}
|
|
2008
2164
|
});
|
|
2009
2165
|
}
|
|
2166
|
+
|
|
2167
|
+
// ---- Section properties parsing ----
|
|
2168
|
+
|
|
2169
|
+
function parseSectionBreakElement(
|
|
2170
|
+
node: XmlElementNode,
|
|
2171
|
+
sourceXml: string,
|
|
2172
|
+
): ParsedSectionBreakNode {
|
|
2173
|
+
const props = parseSectionPropertiesFromElement(node);
|
|
2174
|
+
return {
|
|
2175
|
+
type: "section_break",
|
|
2176
|
+
sectionPropertiesXml: sourceXml.slice(node.start, node.end),
|
|
2177
|
+
sectionProperties: props,
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
function readSectionPropertiesFromPPr(
|
|
2182
|
+
pPrNode: XmlElementNode,
|
|
2183
|
+
): SectionProperties | undefined {
|
|
2184
|
+
for (const child of pPrNode.children) {
|
|
2185
|
+
if (child.type === "element" && localName(child.name) === "sectPr") {
|
|
2186
|
+
return parseSectionPropertiesFromElement(child);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
return undefined;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
function readSectionPropertiesXmlFromPPr(
|
|
2193
|
+
pPrNode: XmlElementNode,
|
|
2194
|
+
sourceXml: string,
|
|
2195
|
+
): string | undefined {
|
|
2196
|
+
for (const child of pPrNode.children) {
|
|
2197
|
+
if (child.type === "element" && localName(child.name) === "sectPr") {
|
|
2198
|
+
return sourceXml.slice(child.start, child.end);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
return undefined;
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
export function parseSectionPropertiesFromElement(
|
|
2205
|
+
node: XmlElementNode,
|
|
2206
|
+
): SectionProperties {
|
|
2207
|
+
const props: SectionProperties = {};
|
|
2208
|
+
|
|
2209
|
+
for (const child of node.children) {
|
|
2210
|
+
if (child.type !== "element") continue;
|
|
2211
|
+
const name = localName(child.name);
|
|
2212
|
+
|
|
2213
|
+
switch (name) {
|
|
2214
|
+
case "pgSz": {
|
|
2215
|
+
const w = safeParseInt(child.attributes["w:w"]);
|
|
2216
|
+
const h = safeParseInt(child.attributes["w:h"]);
|
|
2217
|
+
if (w !== undefined && h !== undefined) {
|
|
2218
|
+
const pageSize: PageSize = { width: w, height: h };
|
|
2219
|
+
const orient = child.attributes["w:orient"];
|
|
2220
|
+
if (orient === "landscape" || orient === "portrait") {
|
|
2221
|
+
pageSize.orientation = orient;
|
|
2222
|
+
}
|
|
2223
|
+
props.pageSize = pageSize;
|
|
2224
|
+
}
|
|
2225
|
+
break;
|
|
2226
|
+
}
|
|
2227
|
+
case "pgMar": {
|
|
2228
|
+
const top = safeParseInt(child.attributes["w:top"]);
|
|
2229
|
+
const right = safeParseInt(child.attributes["w:right"]);
|
|
2230
|
+
const bottom = safeParseInt(child.attributes["w:bottom"]);
|
|
2231
|
+
const left = safeParseInt(child.attributes["w:left"]);
|
|
2232
|
+
if (top !== undefined && right !== undefined && bottom !== undefined && left !== undefined) {
|
|
2233
|
+
const margins: PageMargins = { top, right, bottom, left };
|
|
2234
|
+
const header = safeParseInt(child.attributes["w:header"]);
|
|
2235
|
+
const footer = safeParseInt(child.attributes["w:footer"]);
|
|
2236
|
+
const gutter = safeParseInt(child.attributes["w:gutter"]);
|
|
2237
|
+
if (header !== undefined) margins.header = header;
|
|
2238
|
+
if (footer !== undefined) margins.footer = footer;
|
|
2239
|
+
if (gutter !== undefined) margins.gutter = gutter;
|
|
2240
|
+
props.pageMargins = margins;
|
|
2241
|
+
}
|
|
2242
|
+
break;
|
|
2243
|
+
}
|
|
2244
|
+
case "cols": {
|
|
2245
|
+
const columns: ColumnProperties = {};
|
|
2246
|
+
const num = safeParseInt(child.attributes["w:num"]);
|
|
2247
|
+
const space = safeParseInt(child.attributes["w:space"]);
|
|
2248
|
+
const equalWidth = child.attributes["w:equalWidth"];
|
|
2249
|
+
const sep = child.attributes["w:sep"];
|
|
2250
|
+
if (num !== undefined) columns.count = num;
|
|
2251
|
+
if (space !== undefined) columns.space = space;
|
|
2252
|
+
if (equalWidth !== undefined) columns.equalWidth = equalWidth !== "0" && equalWidth !== "false";
|
|
2253
|
+
if (sep === "1" || sep === "true") columns.separator = true;
|
|
2254
|
+
const colDefs: Array<{ width: number; space?: number }> = [];
|
|
2255
|
+
for (const colChild of child.children) {
|
|
2256
|
+
if (colChild.type === "element" && localName(colChild.name) === "col") {
|
|
2257
|
+
const colW = safeParseInt(colChild.attributes["w:w"]);
|
|
2258
|
+
const colSpace = safeParseInt(colChild.attributes["w:space"]);
|
|
2259
|
+
if (colW !== undefined) {
|
|
2260
|
+
colDefs.push(colSpace !== undefined ? { width: colW, space: colSpace } : { width: colW });
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
if (colDefs.length > 0) columns.columns = colDefs;
|
|
2265
|
+
if (Object.keys(columns).length > 0) props.columns = columns;
|
|
2266
|
+
break;
|
|
2267
|
+
}
|
|
2268
|
+
case "pgNumType": {
|
|
2269
|
+
const numbering: PageNumbering = {};
|
|
2270
|
+
const fmt = child.attributes["w:fmt"];
|
|
2271
|
+
const start = safeParseInt(child.attributes["w:start"]);
|
|
2272
|
+
const chapStyle = child.attributes["w:chapStyle"];
|
|
2273
|
+
const chapSep = child.attributes["w:chapSep"];
|
|
2274
|
+
if (fmt) numbering.format = fmt;
|
|
2275
|
+
if (start !== undefined) numbering.start = start;
|
|
2276
|
+
if (chapStyle) numbering.chapStyle = chapStyle;
|
|
2277
|
+
if (chapSep) numbering.chapSep = chapSep;
|
|
2278
|
+
if (Object.keys(numbering).length > 0) props.pageNumbering = numbering;
|
|
2279
|
+
break;
|
|
2280
|
+
}
|
|
2281
|
+
case "lnNumType": {
|
|
2282
|
+
const lineNumbering: SectionLineNumbering = {};
|
|
2283
|
+
const countBy = safeParseInt(child.attributes["w:countBy"]);
|
|
2284
|
+
const start = safeParseInt(child.attributes["w:start"]);
|
|
2285
|
+
const distance = safeParseInt(child.attributes["w:distance"]);
|
|
2286
|
+
const restart = child.attributes["w:restart"];
|
|
2287
|
+
if (countBy !== undefined) lineNumbering.countBy = countBy;
|
|
2288
|
+
if (start !== undefined) lineNumbering.start = start;
|
|
2289
|
+
if (distance !== undefined) lineNumbering.distance = distance;
|
|
2290
|
+
if (
|
|
2291
|
+
restart === "newPage" ||
|
|
2292
|
+
restart === "newSection" ||
|
|
2293
|
+
restart === "continuous"
|
|
2294
|
+
) {
|
|
2295
|
+
lineNumbering.restart = restart;
|
|
2296
|
+
}
|
|
2297
|
+
if (Object.keys(lineNumbering).length > 0) {
|
|
2298
|
+
props.lineNumbering = lineNumbering;
|
|
2299
|
+
}
|
|
2300
|
+
break;
|
|
2301
|
+
}
|
|
2302
|
+
case "pgBorders": {
|
|
2303
|
+
const pageBorders: SectionPageBorders = {};
|
|
2304
|
+
const offsetFrom = child.attributes["w:offsetFrom"];
|
|
2305
|
+
const display = child.attributes["w:display"];
|
|
2306
|
+
const zOrder = child.attributes["w:zOrder"];
|
|
2307
|
+
if (offsetFrom === "page" || offsetFrom === "text") {
|
|
2308
|
+
pageBorders.offsetFrom = offsetFrom;
|
|
2309
|
+
}
|
|
2310
|
+
if (
|
|
2311
|
+
display === "allPages" ||
|
|
2312
|
+
display === "firstPage" ||
|
|
2313
|
+
display === "notFirstPage"
|
|
2314
|
+
) {
|
|
2315
|
+
pageBorders.display = display;
|
|
2316
|
+
}
|
|
2317
|
+
if (zOrder === "front" || zOrder === "back") {
|
|
2318
|
+
pageBorders.zOrder = zOrder;
|
|
2319
|
+
}
|
|
2320
|
+
for (const borderChild of child.children) {
|
|
2321
|
+
if (borderChild.type !== "element") continue;
|
|
2322
|
+
const borderName = localName(borderChild.name);
|
|
2323
|
+
const border = parseBorderSpec(borderChild);
|
|
2324
|
+
if (!border) continue;
|
|
2325
|
+
if (
|
|
2326
|
+
borderName === "top" ||
|
|
2327
|
+
borderName === "left" ||
|
|
2328
|
+
borderName === "bottom" ||
|
|
2329
|
+
borderName === "right"
|
|
2330
|
+
) {
|
|
2331
|
+
pageBorders[borderName] = border;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
if (Object.keys(pageBorders).length > 0) {
|
|
2335
|
+
props.pageBorders = pageBorders;
|
|
2336
|
+
}
|
|
2337
|
+
break;
|
|
2338
|
+
}
|
|
2339
|
+
case "docGrid": {
|
|
2340
|
+
const documentGrid: SectionDocumentGrid = {};
|
|
2341
|
+
const type = child.attributes["w:type"];
|
|
2342
|
+
const linePitch = safeParseInt(child.attributes["w:linePitch"]);
|
|
2343
|
+
const charSpace = safeParseInt(child.attributes["w:charSpace"]);
|
|
2344
|
+
if (
|
|
2345
|
+
type === "default" ||
|
|
2346
|
+
type === "lines" ||
|
|
2347
|
+
type === "linesAndChars" ||
|
|
2348
|
+
type === "snapToChars"
|
|
2349
|
+
) {
|
|
2350
|
+
documentGrid.type = type;
|
|
2351
|
+
}
|
|
2352
|
+
if (linePitch !== undefined) documentGrid.linePitch = linePitch;
|
|
2353
|
+
if (charSpace !== undefined) documentGrid.charSpace = charSpace;
|
|
2354
|
+
if (Object.keys(documentGrid).length > 0) {
|
|
2355
|
+
props.documentGrid = documentGrid;
|
|
2356
|
+
}
|
|
2357
|
+
break;
|
|
2358
|
+
}
|
|
2359
|
+
case "headerReference": {
|
|
2360
|
+
const variant = child.attributes["w:type"] as HeaderFooterVariant | undefined;
|
|
2361
|
+
const rId = child.attributes["r:id"];
|
|
2362
|
+
if (variant && rId) {
|
|
2363
|
+
if (!props.headerReferences) props.headerReferences = [];
|
|
2364
|
+
props.headerReferences.push({ variant, relationshipId: rId });
|
|
2365
|
+
}
|
|
2366
|
+
break;
|
|
2367
|
+
}
|
|
2368
|
+
case "footerReference": {
|
|
2369
|
+
const variant = child.attributes["w:type"] as HeaderFooterVariant | undefined;
|
|
2370
|
+
const rId = child.attributes["r:id"];
|
|
2371
|
+
if (variant && rId) {
|
|
2372
|
+
if (!props.footerReferences) props.footerReferences = [];
|
|
2373
|
+
props.footerReferences.push({ variant, relationshipId: rId });
|
|
2374
|
+
}
|
|
2375
|
+
break;
|
|
2376
|
+
}
|
|
2377
|
+
case "type": {
|
|
2378
|
+
const val = child.attributes["w:val"];
|
|
2379
|
+
if (val === "continuous" || val === "nextPage" || val === "evenPage" || val === "oddPage" || val === "nextColumn") {
|
|
2380
|
+
props.sectionType = val;
|
|
2381
|
+
}
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
case "titlePg": {
|
|
2385
|
+
const val = child.attributes["w:val"];
|
|
2386
|
+
props.titlePage = val !== "false" && val !== "0";
|
|
2387
|
+
break;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
return props;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
function safeParseInt(value: string | undefined): number | undefined {
|
|
2396
|
+
if (value === undefined) return undefined;
|
|
2397
|
+
const n = Number.parseInt(value, 10);
|
|
2398
|
+
return Number.isFinite(n) ? n : undefined;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
function parseBorderSpec(node: XmlElementNode): BorderSpec | undefined {
|
|
2402
|
+
const border: BorderSpec = {};
|
|
2403
|
+
const value = node.attributes["w:val"];
|
|
2404
|
+
const size = safeParseInt(node.attributes["w:sz"]);
|
|
2405
|
+
const space = safeParseInt(node.attributes["w:space"]);
|
|
2406
|
+
const color = node.attributes["w:color"];
|
|
2407
|
+
|
|
2408
|
+
if (value) border.value = value;
|
|
2409
|
+
if (size !== undefined) border.size = size;
|
|
2410
|
+
if (space !== undefined) border.space = space;
|
|
2411
|
+
if (color) border.color = color;
|
|
2412
|
+
|
|
2413
|
+
return Object.keys(border).length > 0 ? border : undefined;
|
|
2414
|
+
}
|
|
@@ -147,6 +147,11 @@ function readLevels(abstractNode: XmlElementNode): NumberingLevelDefinition[] {
|
|
|
147
147
|
const text = textNode?.attributes["w:val"] ?? textNode?.attributes.val ?? `%${level + 1}.`;
|
|
148
148
|
const paragraphStyleId =
|
|
149
149
|
paragraphStyleNode?.attributes["w:val"] ?? paragraphStyleNode?.attributes.val;
|
|
150
|
+
const isLegalNode = findChildElementOptional(child, "isLgl");
|
|
151
|
+
const isLegalNumbering = isLegalNode !== undefined;
|
|
152
|
+
const suffixNode = findChildElementOptional(child, "suff");
|
|
153
|
+
const suffixVal = suffixNode?.attributes["w:val"] ?? suffixNode?.attributes.val;
|
|
154
|
+
const suffix = suffixVal === "space" || suffixVal === "nothing" ? suffixVal : suffixVal === "tab" ? "tab" : undefined;
|
|
150
155
|
|
|
151
156
|
levels.push({
|
|
152
157
|
level,
|
|
@@ -154,6 +159,8 @@ function readLevels(abstractNode: XmlElementNode): NumberingLevelDefinition[] {
|
|
|
154
159
|
text,
|
|
155
160
|
...(startAt !== undefined ? { startAt } : {}),
|
|
156
161
|
...(paragraphStyleId ? { paragraphStyleId } : {}),
|
|
162
|
+
...(isLegalNumbering ? { isLegalNumbering } : {}),
|
|
163
|
+
...(suffix ? { suffix } : {}),
|
|
157
164
|
});
|
|
158
165
|
}
|
|
159
166
|
|