@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
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
ParagraphNode,
|
|
9
9
|
PreservationStore,
|
|
10
10
|
SdtNode,
|
|
11
|
+
SectionProperties,
|
|
11
12
|
TableNode,
|
|
12
13
|
TableCellNode,
|
|
13
14
|
TextMark,
|
|
@@ -30,6 +31,7 @@ export interface SerializedMainDocument {
|
|
|
30
31
|
export interface SerializeMainDocumentOptions {
|
|
31
32
|
documentAttributes?: Record<string, string>;
|
|
32
33
|
media?: MediaCatalog;
|
|
34
|
+
finalSectionProperties?: SectionProperties;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
interface SerializationState {
|
|
@@ -90,7 +92,9 @@ export function serializeMainDocument(
|
|
|
90
92
|
const bodyPieces: string[] = [];
|
|
91
93
|
const paragraphBoundaries: RevisionParagraphBoundary[] = [];
|
|
92
94
|
let bodyLength = 0;
|
|
93
|
-
let sectionPropertiesXml =
|
|
95
|
+
let sectionPropertiesXml = options.finalSectionProperties
|
|
96
|
+
? serializeSectionPropertiesXml(options.finalSectionProperties)
|
|
97
|
+
: "<w:sectPr/>";
|
|
94
98
|
let cursor = 0;
|
|
95
99
|
let paragraphIndex = -1;
|
|
96
100
|
let previousWasParagraph = false;
|
|
@@ -154,9 +158,20 @@ export function serializeMainDocument(
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
if (block.type === "section_break") {
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
// Inline section breaks must be emitted as a paragraph with <w:sectPr>
|
|
162
|
+
// in its <w:pPr> element (OOXML compliance). The body-level <w:sectPr>
|
|
163
|
+
// is reserved for the final section only.
|
|
164
|
+
let inlineSectPr: string;
|
|
165
|
+
if (block.sectionPropertiesXml ?? block.propertiesXml) {
|
|
166
|
+
inlineSectPr = block.sectionPropertiesXml ?? block.propertiesXml!;
|
|
167
|
+
} else if (block.sectionProperties) {
|
|
168
|
+
inlineSectPr = serializeSectionPropertiesXml(block.sectionProperties);
|
|
169
|
+
} else {
|
|
170
|
+
inlineSectPr = "<w:sectPr/>";
|
|
159
171
|
}
|
|
172
|
+
const sectionParagraphXml = `<w:p><w:pPr>${inlineSectPr}</w:pPr></w:p>`;
|
|
173
|
+
bodyPieces.push(sectionParagraphXml);
|
|
174
|
+
bodyLength += sectionParagraphXml.length;
|
|
160
175
|
cursor += 1;
|
|
161
176
|
previousWasParagraph = false;
|
|
162
177
|
continue;
|
|
@@ -242,7 +257,11 @@ function serializeBlockNode(
|
|
|
242
257
|
case "opaque_block":
|
|
243
258
|
return lookupOpaqueXml(block.fragmentId, state);
|
|
244
259
|
case "section_break":
|
|
245
|
-
|
|
260
|
+
if (block.sectionPropertiesXml ?? block.propertiesXml) {
|
|
261
|
+
return block.sectionPropertiesXml ?? block.propertiesXml!;
|
|
262
|
+
}
|
|
263
|
+
if (block.sectionProperties) return serializeSectionPropertiesXml(block.sectionProperties);
|
|
264
|
+
return "<w:sectPr/>";
|
|
246
265
|
}
|
|
247
266
|
}
|
|
248
267
|
|
|
@@ -303,7 +322,40 @@ function buildSdtPropertiesXml(block: SdtNode): string {
|
|
|
303
322
|
if (block.properties.lock) {
|
|
304
323
|
children.push(`<w:lock w:val="${escapeAttribute(block.properties.lock)}"/>`);
|
|
305
324
|
}
|
|
306
|
-
if (block.properties.
|
|
325
|
+
if (block.properties.showingPlcHdr) {
|
|
326
|
+
children.push(`<w:showingPlcHdr/>`);
|
|
327
|
+
}
|
|
328
|
+
if (block.properties.checkbox) {
|
|
329
|
+
const cb = block.properties.checkbox;
|
|
330
|
+
const cbParts: string[] = [];
|
|
331
|
+
cbParts.push(`<w14:checked w14:val="${cb.checked ? "1" : "0"}"/>`);
|
|
332
|
+
if (cb.checkedChar) cbParts.push(`<w14:checkedState w14:val="${escapeAttribute(cb.checkedChar)}"/>`);
|
|
333
|
+
if (cb.uncheckedChar) cbParts.push(`<w14:uncheckedState w14:val="${escapeAttribute(cb.uncheckedChar)}"/>`);
|
|
334
|
+
children.push(`<w14:checkbox>${cbParts.join("")}</w14:checkbox>`);
|
|
335
|
+
} else if (block.properties.datePicker) {
|
|
336
|
+
const dp = block.properties.datePicker;
|
|
337
|
+
const dateAttrs = dp.fullDate ? ` w:fullDate="${escapeAttribute(dp.fullDate)}"` : "";
|
|
338
|
+
const dpParts: string[] = [];
|
|
339
|
+
if (dp.dateFormat) dpParts.push(`<w:dateFormat w:val="${escapeAttribute(dp.dateFormat)}"/>`);
|
|
340
|
+
if (dp.lid) dpParts.push(`<w:lid w:val="${escapeAttribute(dp.lid)}"/>`);
|
|
341
|
+
children.push(dpParts.length > 0 ? `<w:date${dateAttrs}>${dpParts.join("")}</w:date>` : `<w:date${dateAttrs}/>`);
|
|
342
|
+
} else if (block.properties.dropdownList) {
|
|
343
|
+
const items = block.properties.dropdownList.map((item) => {
|
|
344
|
+
const dt = item.displayText ? ` w:displayText="${escapeAttribute(item.displayText)}"` : "";
|
|
345
|
+
return `<w:listItem${dt} w:value="${escapeAttribute(item.value)}"/>`;
|
|
346
|
+
}).join("");
|
|
347
|
+
children.push(`<w:dropDownList>${items}</w:dropDownList>`);
|
|
348
|
+
} else if (block.properties.comboBox) {
|
|
349
|
+
const items = block.properties.comboBox.map((item) => {
|
|
350
|
+
const dt = item.displayText ? ` w:displayText="${escapeAttribute(item.displayText)}"` : "";
|
|
351
|
+
return `<w:listItem${dt} w:value="${escapeAttribute(item.value)}"/>`;
|
|
352
|
+
}).join("");
|
|
353
|
+
children.push(`<w:comboBox>${items}</w:comboBox>`);
|
|
354
|
+
} else if (block.properties.sdtType === "plainText") {
|
|
355
|
+
children.push(`<w:text/>`);
|
|
356
|
+
} else if (block.properties.sdtType === "richText") {
|
|
357
|
+
children.push(`<w:richText/>`);
|
|
358
|
+
} else if (block.properties.sdtType) {
|
|
307
359
|
children.push(`<w:${block.properties.sdtType}/>`);
|
|
308
360
|
}
|
|
309
361
|
return children.length > 0 ? `<w:sdtPr>${children.join("")}</w:sdtPr>` : "<w:sdtPr/>";
|
|
@@ -1049,6 +1101,151 @@ function serializeDocumentAttributes(
|
|
|
1049
1101
|
.join("");
|
|
1050
1102
|
}
|
|
1051
1103
|
|
|
1104
|
+
export function serializeSectionPropertiesXml(props: SectionProperties): string {
|
|
1105
|
+
const children: string[] = [];
|
|
1106
|
+
|
|
1107
|
+
// Header references
|
|
1108
|
+
if (props.headerReferences) {
|
|
1109
|
+
for (const ref of props.headerReferences) {
|
|
1110
|
+
children.push(`<w:headerReference w:type="${escapeAttribute(ref.variant)}" r:id="${escapeAttribute(ref.relationshipId)}"/>`);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// Footer references
|
|
1115
|
+
if (props.footerReferences) {
|
|
1116
|
+
for (const ref of props.footerReferences) {
|
|
1117
|
+
children.push(`<w:footerReference w:type="${escapeAttribute(ref.variant)}" r:id="${escapeAttribute(ref.relationshipId)}"/>`);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Section type
|
|
1122
|
+
if (props.sectionType) {
|
|
1123
|
+
children.push(`<w:type w:val="${escapeAttribute(props.sectionType)}"/>`);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// Page size
|
|
1127
|
+
if (props.pageSize) {
|
|
1128
|
+
let pgSz = `<w:pgSz w:w="${props.pageSize.width}" w:h="${props.pageSize.height}"`;
|
|
1129
|
+
if (props.pageSize.orientation) {
|
|
1130
|
+
pgSz += ` w:orient="${props.pageSize.orientation}"`;
|
|
1131
|
+
}
|
|
1132
|
+
pgSz += "/>";
|
|
1133
|
+
children.push(pgSz);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Page margins
|
|
1137
|
+
if (props.pageMargins) {
|
|
1138
|
+
let pgMar = `<w:pgMar w:top="${props.pageMargins.top}" w:right="${props.pageMargins.right}" w:bottom="${props.pageMargins.bottom}" w:left="${props.pageMargins.left}"`;
|
|
1139
|
+
if (props.pageMargins.header !== undefined) pgMar += ` w:header="${props.pageMargins.header}"`;
|
|
1140
|
+
if (props.pageMargins.footer !== undefined) pgMar += ` w:footer="${props.pageMargins.footer}"`;
|
|
1141
|
+
if (props.pageMargins.gutter !== undefined) pgMar += ` w:gutter="${props.pageMargins.gutter}"`;
|
|
1142
|
+
pgMar += "/>";
|
|
1143
|
+
children.push(pgMar);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// Columns
|
|
1147
|
+
if (props.columns) {
|
|
1148
|
+
let cols = "<w:cols";
|
|
1149
|
+
if (props.columns.count !== undefined) cols += ` w:num="${props.columns.count}"`;
|
|
1150
|
+
if (props.columns.space !== undefined) cols += ` w:space="${props.columns.space}"`;
|
|
1151
|
+
if (props.columns.equalWidth !== undefined) cols += ` w:equalWidth="${props.columns.equalWidth ? "1" : "0"}"`;
|
|
1152
|
+
if (props.columns.separator) cols += ` w:sep="1"`;
|
|
1153
|
+
if (props.columns.columns && props.columns.columns.length > 0) {
|
|
1154
|
+
cols += ">";
|
|
1155
|
+
for (const col of props.columns.columns) {
|
|
1156
|
+
let colXml = `<w:col w:w="${col.width}"`;
|
|
1157
|
+
if (col.space !== undefined) colXml += ` w:space="${col.space}"`;
|
|
1158
|
+
colXml += "/>";
|
|
1159
|
+
cols += colXml;
|
|
1160
|
+
}
|
|
1161
|
+
cols += "</w:cols>";
|
|
1162
|
+
} else {
|
|
1163
|
+
cols += "/>";
|
|
1164
|
+
}
|
|
1165
|
+
children.push(cols);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Page numbering
|
|
1169
|
+
if (props.pageNumbering) {
|
|
1170
|
+
let pgNum = "<w:pgNumType";
|
|
1171
|
+
if (props.pageNumbering.format) pgNum += ` w:fmt="${escapeAttribute(props.pageNumbering.format)}"`;
|
|
1172
|
+
if (props.pageNumbering.start !== undefined) pgNum += ` w:start="${props.pageNumbering.start}"`;
|
|
1173
|
+
if (props.pageNumbering.chapStyle) pgNum += ` w:chapStyle="${escapeAttribute(props.pageNumbering.chapStyle)}"`;
|
|
1174
|
+
if (props.pageNumbering.chapSep) pgNum += ` w:chapSep="${escapeAttribute(props.pageNumbering.chapSep)}"`;
|
|
1175
|
+
pgNum += "/>";
|
|
1176
|
+
children.push(pgNum);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
if (props.lineNumbering) {
|
|
1180
|
+
let lineNumbering = "<w:lnNumType";
|
|
1181
|
+
if (props.lineNumbering.countBy !== undefined) {
|
|
1182
|
+
lineNumbering += ` w:countBy="${props.lineNumbering.countBy}"`;
|
|
1183
|
+
}
|
|
1184
|
+
if (props.lineNumbering.start !== undefined) {
|
|
1185
|
+
lineNumbering += ` w:start="${props.lineNumbering.start}"`;
|
|
1186
|
+
}
|
|
1187
|
+
if (props.lineNumbering.distance !== undefined) {
|
|
1188
|
+
lineNumbering += ` w:distance="${props.lineNumbering.distance}"`;
|
|
1189
|
+
}
|
|
1190
|
+
if (props.lineNumbering.restart) {
|
|
1191
|
+
lineNumbering += ` w:restart="${escapeAttribute(props.lineNumbering.restart)}"`;
|
|
1192
|
+
}
|
|
1193
|
+
lineNumbering += "/>";
|
|
1194
|
+
children.push(lineNumbering);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
if (props.pageBorders) {
|
|
1198
|
+
const attrs: string[] = [];
|
|
1199
|
+
if (props.pageBorders.offsetFrom) {
|
|
1200
|
+
attrs.push(`w:offsetFrom="${escapeAttribute(props.pageBorders.offsetFrom)}"`);
|
|
1201
|
+
}
|
|
1202
|
+
if (props.pageBorders.display) {
|
|
1203
|
+
attrs.push(`w:display="${escapeAttribute(props.pageBorders.display)}"`);
|
|
1204
|
+
}
|
|
1205
|
+
if (props.pageBorders.zOrder) {
|
|
1206
|
+
attrs.push(`w:zOrder="${escapeAttribute(props.pageBorders.zOrder)}"`);
|
|
1207
|
+
}
|
|
1208
|
+
const borderXml = [
|
|
1209
|
+
serializeBorder("top", props.pageBorders.top),
|
|
1210
|
+
serializeBorder("left", props.pageBorders.left),
|
|
1211
|
+
serializeBorder("bottom", props.pageBorders.bottom),
|
|
1212
|
+
serializeBorder("right", props.pageBorders.right),
|
|
1213
|
+
].filter((entry) => entry.length > 0);
|
|
1214
|
+
if (attrs.length > 0 || borderXml.length > 0) {
|
|
1215
|
+
children.push(
|
|
1216
|
+
`<w:pgBorders${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${borderXml.join("")}</w:pgBorders>`,
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// Title page
|
|
1222
|
+
if (props.titlePage) {
|
|
1223
|
+
children.push("<w:titlePg/>");
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (props.documentGrid) {
|
|
1227
|
+
const attrs: string[] = [];
|
|
1228
|
+
if (props.documentGrid.type) {
|
|
1229
|
+
attrs.push(`w:type="${escapeAttribute(props.documentGrid.type)}"`);
|
|
1230
|
+
}
|
|
1231
|
+
if (props.documentGrid.linePitch !== undefined) {
|
|
1232
|
+
attrs.push(`w:linePitch="${props.documentGrid.linePitch}"`);
|
|
1233
|
+
}
|
|
1234
|
+
if (props.documentGrid.charSpace !== undefined) {
|
|
1235
|
+
attrs.push(`w:charSpace="${props.documentGrid.charSpace}"`);
|
|
1236
|
+
}
|
|
1237
|
+
if (attrs.length > 0) {
|
|
1238
|
+
children.push(`<w:docGrid ${attrs.join(" ")}/>`);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (children.length === 0) {
|
|
1243
|
+
return "<w:sectPr/>";
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
return `<w:sectPr>${children.join("")}</w:sectPr>`;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1052
1249
|
function documentNeedsW14Namespace(content: DocumentRootNode): boolean {
|
|
1053
1250
|
const blockQueue = [...content.children];
|
|
1054
1251
|
while (blockQueue.length > 0) {
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import type { NumberingCatalog, ParagraphNode } from "../../model/canonical-document.ts";
|
|
2
|
+
import {
|
|
3
|
+
isSyntheticDocxNullAbstractDefinition,
|
|
4
|
+
isSyntheticDocxNullNumberingInstance,
|
|
5
|
+
} from "../ooxml/numbering-sentinels.ts";
|
|
2
6
|
|
|
3
7
|
export const WORD_NUMBERING_CONTENT_TYPE =
|
|
4
8
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml";
|
|
5
9
|
|
|
6
10
|
export function serializeNumberingXml(catalog: NumberingCatalog): string {
|
|
7
|
-
const abstractDefinitions = Object.values(catalog.abstractDefinitions)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
11
|
+
const abstractDefinitions = Object.values(catalog.abstractDefinitions)
|
|
12
|
+
.filter((definition) => !isSyntheticDocxNullAbstractDefinition(definition))
|
|
13
|
+
.sort((left, right) =>
|
|
14
|
+
compareSerializedIds(left.abstractNumberingId, right.abstractNumberingId),
|
|
15
|
+
);
|
|
16
|
+
const instances = Object.values(catalog.instances)
|
|
17
|
+
.filter((instance) => !isSyntheticDocxNullNumberingInstance(instance))
|
|
18
|
+
.sort((left, right) =>
|
|
19
|
+
compareSerializedIds(left.numberingInstanceId, right.numberingInstanceId),
|
|
20
|
+
);
|
|
13
21
|
|
|
14
22
|
const body = [
|
|
15
23
|
...abstractDefinitions.map((definition) => serializeAbstractDefinition(definition)),
|
|
@@ -22,6 +30,17 @@ export function serializeNumberingXml(catalog: NumberingCatalog): string {
|
|
|
22
30
|
].join("\n");
|
|
23
31
|
}
|
|
24
32
|
|
|
33
|
+
export function hasSerializableNumberingEntries(catalog: NumberingCatalog): boolean {
|
|
34
|
+
return (
|
|
35
|
+
Object.values(catalog.abstractDefinitions).some(
|
|
36
|
+
(definition) => !isSyntheticDocxNullAbstractDefinition(definition),
|
|
37
|
+
) ||
|
|
38
|
+
Object.values(catalog.instances).some(
|
|
39
|
+
(instance) => !isSyntheticDocxNullNumberingInstance(instance),
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
25
44
|
export function serializeParagraphNumberingProperties(
|
|
26
45
|
numbering: ParagraphNode["numbering"],
|
|
27
46
|
): string {
|
|
@@ -51,10 +70,12 @@ function serializeLevel(level: NumberingCatalog["abstractDefinitions"][string]["
|
|
|
51
70
|
const paragraphStyle = level.paragraphStyleId
|
|
52
71
|
? `<w:pStyle w:val="${escapeAttribute(level.paragraphStyleId)}"/>`
|
|
53
72
|
: "";
|
|
73
|
+
const isLegal = level.isLegalNumbering ? "<w:isLgl/>" : "";
|
|
74
|
+
const suffix = level.suffix ? `<w:suff w:val="${escapeAttribute(level.suffix)}"/>` : "";
|
|
54
75
|
|
|
55
76
|
return `<w:lvl w:ilvl="${level.level}">${start}<w:numFmt w:val="${escapeAttribute(
|
|
56
77
|
level.format,
|
|
57
|
-
)}"/><w:lvlText w:val="${escapeAttribute(level.text)}"/>${paragraphStyle}</w:lvl>`;
|
|
78
|
+
)}"/><w:lvlText w:val="${escapeAttribute(level.text)}"/>${paragraphStyle}${isLegal}${suffix}</w:lvl>`;
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
function serializeInstance(instance: NumberingCatalog["instances"][string]): string {
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
OpaqueInlineNode,
|
|
12
12
|
ParagraphNode,
|
|
13
13
|
PreservationStore,
|
|
14
|
+
SectionBreakNode,
|
|
14
15
|
TableCellNode,
|
|
15
16
|
TableNode,
|
|
16
17
|
TableRowNode,
|
|
@@ -27,6 +28,7 @@ import type {
|
|
|
27
28
|
ParsedImageNode,
|
|
28
29
|
ParsedMainDocument,
|
|
29
30
|
ParsedParagraphNode,
|
|
31
|
+
ParsedSectionBreakNode,
|
|
30
32
|
ParsedSdtNode,
|
|
31
33
|
ParsedTableBlockNode,
|
|
32
34
|
ParsedTableCellNode,
|
|
@@ -38,6 +40,7 @@ export interface NormalizedTextDocument {
|
|
|
38
40
|
media: MediaCatalog;
|
|
39
41
|
preservation: PreservationStore;
|
|
40
42
|
diagnostics: DiagnosticStore;
|
|
43
|
+
finalSectionProperties?: ParsedMainDocument["finalSectionProperties"];
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
interface NormalizationState {
|
|
@@ -72,17 +75,19 @@ export function normalizeParsedTextDocument(
|
|
|
72
75
|
},
|
|
73
76
|
};
|
|
74
77
|
|
|
75
|
-
const children =
|
|
76
|
-
|
|
77
|
-
index > 0 &&
|
|
78
|
-
document.blocks[index - 1]?.type === "paragraph" &&
|
|
79
|
-
block.type === "paragraph"
|
|
80
|
-
) {
|
|
81
|
-
state.cursor += 1;
|
|
82
|
-
}
|
|
78
|
+
const children: BlockNode[] = [];
|
|
79
|
+
let previousParagraph = false;
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
for (const block of document.blocks) {
|
|
82
|
+
const normalizedBlocks = normalizeBlocks(block, state, packagePartName);
|
|
83
|
+
for (const normalizedBlock of normalizedBlocks) {
|
|
84
|
+
if (previousParagraph && normalizedBlock.type === "paragraph") {
|
|
85
|
+
state.cursor += 1;
|
|
86
|
+
}
|
|
87
|
+
children.push(normalizedBlock);
|
|
88
|
+
previousParagraph = normalizedBlock.type === "paragraph";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
86
91
|
|
|
87
92
|
return {
|
|
88
93
|
content: {
|
|
@@ -92,41 +97,53 @@ export function normalizeParsedTextDocument(
|
|
|
92
97
|
media: state.media,
|
|
93
98
|
preservation: state.preservation,
|
|
94
99
|
diagnostics: state.diagnostics,
|
|
100
|
+
...(document.finalSectionProperties !== undefined
|
|
101
|
+
? { finalSectionProperties: document.finalSectionProperties }
|
|
102
|
+
: {}),
|
|
95
103
|
};
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
function
|
|
106
|
+
function normalizeBlocks(
|
|
99
107
|
block: ParsedBlockNode,
|
|
100
108
|
state: NormalizationState,
|
|
101
109
|
packagePartName: string,
|
|
102
|
-
): BlockNode {
|
|
110
|
+
): BlockNode[] {
|
|
103
111
|
if (block.type === "opaque_block") {
|
|
104
112
|
const opaque = recordOpaqueFragment("opaque_block", block.rawXml, state, packagePartName);
|
|
105
113
|
state.cursor += 1;
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
type: "opaque_block",
|
|
117
|
+
fragmentId: opaque.fragmentId,
|
|
118
|
+
warningId: opaque.warningId,
|
|
119
|
+
},
|
|
120
|
+
];
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
if (block.type === "table") {
|
|
114
|
-
return normalizeTable(block, state, packagePartName);
|
|
124
|
+
return [normalizeTable(block, state, packagePartName)];
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
if (block.type === "sdt") {
|
|
118
|
-
return normalizeSdt(block, state, packagePartName);
|
|
128
|
+
return [normalizeSdt(block, state, packagePartName)];
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
if (block.type === "custom_xml") {
|
|
122
|
-
return normalizeCustomXml(block, state, packagePartName);
|
|
132
|
+
return [normalizeCustomXml(block, state, packagePartName)];
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
if (block.type === "alt_chunk") {
|
|
126
|
-
return normalizeAltChunk(block, state);
|
|
136
|
+
return [normalizeAltChunk(block, state)];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (block.type === "section_break") {
|
|
140
|
+
return [normalizeSectionBreak(block)];
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
|
|
143
|
+
const normalizedParagraph = normalizeParagraph(block, state, packagePartName);
|
|
144
|
+
return block.sectionProperties
|
|
145
|
+
? [normalizedParagraph, normalizeInlineSectionBreak(block)]
|
|
146
|
+
: [normalizedParagraph];
|
|
130
147
|
}
|
|
131
148
|
|
|
132
149
|
function normalizeParagraph(
|
|
@@ -190,7 +207,7 @@ function normalizeTableCell(
|
|
|
190
207
|
): TableCellNode {
|
|
191
208
|
const children: BlockNode[] = [];
|
|
192
209
|
for (const block of cell.children) {
|
|
193
|
-
children.push(
|
|
210
|
+
children.push(...normalizeBlocks(block, state, packagePartName));
|
|
194
211
|
}
|
|
195
212
|
// Ensure at least one child (OOXML requires at least one <w:p> per cell)
|
|
196
213
|
if (children.length === 0) {
|
|
@@ -213,7 +230,7 @@ function normalizeSdt(
|
|
|
213
230
|
return {
|
|
214
231
|
type: "sdt",
|
|
215
232
|
properties: { ...block.properties },
|
|
216
|
-
children: block.children.
|
|
233
|
+
children: block.children.flatMap((child) => normalizeBlocks(child, state, packagePartName)),
|
|
217
234
|
};
|
|
218
235
|
}
|
|
219
236
|
|
|
@@ -226,7 +243,27 @@ function normalizeCustomXml(
|
|
|
226
243
|
type: "custom_xml",
|
|
227
244
|
...(block.uri ? { uri: block.uri } : {}),
|
|
228
245
|
...(block.element ? { element: block.element } : {}),
|
|
229
|
-
children: block.children.
|
|
246
|
+
children: block.children.flatMap((child) => normalizeBlocks(child, state, packagePartName)),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function normalizeSectionBreak(block: ParsedSectionBreakNode): SectionBreakNode {
|
|
251
|
+
return {
|
|
252
|
+
type: "section_break",
|
|
253
|
+
sectionPropertiesXml: block.sectionPropertiesXml,
|
|
254
|
+
sectionProperties: block.sectionProperties,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function normalizeInlineSectionBreak(
|
|
259
|
+
paragraph: ParsedParagraphNode,
|
|
260
|
+
): SectionBreakNode {
|
|
261
|
+
return {
|
|
262
|
+
type: "section_break",
|
|
263
|
+
...(paragraph.sectionPropertiesXml
|
|
264
|
+
? { sectionPropertiesXml: paragraph.sectionPropertiesXml }
|
|
265
|
+
: {}),
|
|
266
|
+
sectionProperties: paragraph.sectionProperties!,
|
|
230
267
|
};
|
|
231
268
|
}
|
|
232
269
|
|
|
@@ -324,6 +361,7 @@ function normalizeInlineChildren(
|
|
|
324
361
|
type: "shape",
|
|
325
362
|
...(node.text ? { text: node.text } : {}),
|
|
326
363
|
...(node.geometry ? { geometry: node.geometry } : {}),
|
|
364
|
+
...(node.isTextBox ? { isTextBox: true } : {}),
|
|
327
365
|
rawXml: node.rawXml,
|
|
328
366
|
});
|
|
329
367
|
state.cursor += 1;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { NumberingCatalog } from "../../model/canonical-document.ts";
|
|
2
|
+
|
|
3
|
+
export const DOCX_NULL_NUMBERING_INSTANCE_ID = "num:0";
|
|
4
|
+
export const DOCX_NULL_ABSTRACT_NUMBERING_ID = "abstract-num:__docx-import-null__";
|
|
5
|
+
|
|
6
|
+
export function createSyntheticDocxNullNumberingCatalog(): Pick<
|
|
7
|
+
NumberingCatalog,
|
|
8
|
+
"abstractDefinitions" | "instances"
|
|
9
|
+
> {
|
|
10
|
+
return {
|
|
11
|
+
abstractDefinitions: {
|
|
12
|
+
[DOCX_NULL_ABSTRACT_NUMBERING_ID]: {
|
|
13
|
+
abstractNumberingId: DOCX_NULL_ABSTRACT_NUMBERING_ID,
|
|
14
|
+
levels: Array.from({ length: 9 }, (_unused, level) => ({
|
|
15
|
+
level,
|
|
16
|
+
format: "none",
|
|
17
|
+
text: "",
|
|
18
|
+
})),
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
instances: {
|
|
22
|
+
[DOCX_NULL_NUMBERING_INSTANCE_ID]: {
|
|
23
|
+
numberingInstanceId: DOCX_NULL_NUMBERING_INSTANCE_ID,
|
|
24
|
+
abstractNumberingId: DOCX_NULL_ABSTRACT_NUMBERING_ID,
|
|
25
|
+
overrides: [],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isSyntheticDocxNullAbstractDefinition(
|
|
32
|
+
definition: NumberingCatalog["abstractDefinitions"][string],
|
|
33
|
+
): boolean {
|
|
34
|
+
return definition.abstractNumberingId === DOCX_NULL_ABSTRACT_NUMBERING_ID;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isSyntheticDocxNullNumberingInstance(
|
|
38
|
+
instance: NumberingCatalog["instances"][string],
|
|
39
|
+
): boolean {
|
|
40
|
+
return (
|
|
41
|
+
instance.numberingInstanceId === DOCX_NULL_NUMBERING_INSTANCE_ID &&
|
|
42
|
+
instance.abstractNumberingId === DOCX_NULL_ABSTRACT_NUMBERING_ID
|
|
43
|
+
);
|
|
44
|
+
}
|