@stll/folio-core 0.45.0 → 0.46.0
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/dist/ai-edits/apply.js +59 -19
- package/dist/ai-edits/headless.js +1 -1
- package/dist/compare/compare.js +2 -5
- package/dist/compare/scenario.js +2 -5
- package/dist/compare/types.d.ts +9 -1
- package/dist/compare/types.js +12 -1
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/content-controls/checkboxDisplay.d.ts +11 -0
- package/dist/content-controls/checkboxDisplay.js +40 -0
- package/dist/content-controls/findContentControls.js +3 -1
- package/dist/content-controls/mutateContentControls.js +17 -2
- package/dist/controller/contentControlWidgetController.d.ts +1 -0
- package/dist/controller/contentControlWidgetController.js +3 -0
- package/dist/controller/hiddenEditorApi.js +3 -1
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +6 -3
- package/dist/display-list/build/buildDisplayList.js +20 -1
- package/dist/display-list/build/imagePrimitives.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +3 -1
- package/dist/display-list/build/paragraphPrimitives.js +21 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +10 -2
- package/dist/display-list/types.d.ts +17 -0
- package/dist/docx/attributeRemainder.d.ts +43 -0
- package/dist/docx/attributeRemainder.js +65 -0
- package/dist/docx/blockContentParser.d.ts +1 -1
- package/dist/docx/blockContentParser.js +87 -63
- package/dist/docx/blockPlainText.d.ts +3 -4
- package/dist/docx/blockPlainText.js +1 -0
- package/dist/docx/bookmarkPlacement.js +2 -0
- package/dist/docx/commentAnchorIndex.d.ts +30 -0
- package/dist/docx/commentAnchorIndex.js +50 -0
- package/dist/docx/commentParser.d.ts +1 -1
- package/dist/docx/commentParser.js +54 -37
- package/dist/docx/commentRangeIntegrity.d.ts +16 -1
- package/dist/docx/commentRangeIntegrity.js +42 -1
- package/dist/docx/commentRangeJoin.d.ts +9 -0
- package/dist/docx/commentRangeJoin.js +33 -0
- package/dist/docx/commentReferenceCompletion.d.ts +9 -0
- package/dist/docx/commentReferenceCompletion.js +47 -0
- package/dist/docx/commentReferenceNormalization.js +1 -0
- package/dist/docx/commentReplyMarkers.js +11 -19
- package/dist/docx/compatibility.js +1 -0
- package/dist/docx/containerChildren.d.ts +111 -0
- package/dist/docx/containerChildren.gen.d.ts +28 -0
- package/dist/docx/containerChildren.gen.js +246 -0
- package/dist/docx/containerChildren.js +103 -0
- package/dist/docx/documentParser.d.ts +1 -1
- package/dist/docx/documentParser.js +3 -3
- package/dist/docx/ensureParaIds.js +17 -7
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +4 -4
- package/dist/docx/fieldState.d.ts +26 -0
- package/dist/docx/fieldState.js +53 -0
- package/dist/docx/footnoteParser.d.ts +1 -1
- package/dist/docx/graphicFrameLocks.d.ts +16 -4
- package/dist/docx/graphicFrameLocks.js +19 -6
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/headerFooterReferenceNormalization.js +1 -0
- package/dist/docx/hyperlinkParser.d.ts +27 -8
- package/dist/docx/hyperlinkParser.js +76 -18
- package/dist/docx/imageParser.js +33 -8
- package/dist/docx/inlineWrapperContent.d.ts +85 -0
- package/dist/docx/inlineWrapperContent.js +84 -0
- package/dist/docx/normalizeBaseDirection.js +10 -1
- package/dist/docx/paraIdAttribute.d.ts +21 -0
- package/dist/docx/paraIdAttribute.js +64 -0
- package/dist/docx/paragraphParser.d.ts +1 -1
- package/dist/docx/paragraphParser.js +277 -168
- package/dist/docx/paragraphPropertySource.js +5 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +6 -52
- package/dist/docx/paragraphTraversal.js +7 -4
- package/dist/docx/parseWarningMessage.js +2 -1
- package/dist/docx/preservedRunContent.d.ts +32 -0
- package/dist/docx/preservedRunContent.js +86 -0
- package/dist/docx/renderedPageBreakNormalization.js +2 -4
- package/dist/docx/rezip.js +74 -18
- package/dist/docx/runParser.d.ts +1 -1
- package/dist/docx/runParser.js +16 -12
- package/dist/docx/sectionParser.js +8 -1
- package/dist/docx/selectiveXmlPatch.d.ts +46 -2
- package/dist/docx/selectiveXmlPatch.js +86 -39
- package/dist/docx/serializer/borderSerializer.js +2 -2
- package/dist/docx/serializer/commentSerializer.js +5 -7
- package/dist/docx/serializer/documentSerializer.js +8 -11
- package/dist/docx/serializer/headerFooterSerializer.js +7 -9
- package/dist/docx/serializer/noteSerializer.js +8 -8
- package/dist/docx/serializer/paragraphSerializer.js +76 -56
- package/dist/docx/serializer/runSerializer.js +25 -16
- package/dist/docx/serializer/sectionPropertiesSerializer.js +8 -4
- package/dist/docx/serializer/tableSerializer.js +18 -8
- package/dist/docx/server/createBilingualDocument.js +3 -1
- package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
- package/dist/docx/server/materializeYjsDocx.js +9 -1
- package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
- package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
- package/dist/docx/shapeParser.js +5 -5
- package/dist/docx/tableParser.d.ts +1 -1
- package/dist/docx/tableParser.js +206 -85
- package/dist/docx/textBoxParser.d.ts +25 -2
- package/dist/docx/textBoxParser.js +52 -2
- package/dist/docx/unzip.js +5 -4
- package/dist/docx/vmlImageParser.d.ts +17 -1
- package/dist/docx/vmlImageParser.js +17 -1
- package/dist/docx/xmlEncoding.d.ts +5 -0
- package/dist/docx/xmlEncoding.js +12 -0
- package/dist/docx/xmlParser.d.ts +10 -1
- package/dist/docx/xmlParser.js +14 -1
- package/dist/headless-layout.js +10 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +2 -0
- package/dist/layout-engine/types.d.ts +23 -1
- package/dist/layout-painter/renderImage.d.ts +5 -1
- package/dist/layout-painter/renderImage.js +10 -3
- package/dist/layout-painter/renderPage.d.ts +2 -0
- package/dist/layout-painter/renderPage.js +4 -0
- package/dist/layout-painter/renderParagraph.d.ts +13 -1
- package/dist/layout-painter/renderParagraph.js +22 -3
- package/dist/managers/autoSaveCodec.d.ts +9 -1
- package/dist/managers/autoSaveCodec.js +12 -4
- package/dist/markdown/renderBlock.js +1 -0
- package/dist/markdown/renderRuns.d.ts +1 -1
- package/dist/markdown/renderRuns.js +28 -5
- package/dist/markdown/renderTable.js +22 -5
- package/dist/markdown/trailers.js +4 -1
- package/dist/pdf/images.js +142 -6
- package/dist/prosemirror/attrs/index.d.ts +14 -3
- package/dist/prosemirror/attrs/index.js +155 -2
- package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
- package/dist/prosemirror/authoredTransformAttrs.js +64 -0
- package/dist/prosemirror/commands/contentControls.js +10 -2
- package/dist/prosemirror/commands/image.d.ts +9 -2
- package/dist/prosemirror/commands/image.js +42 -28
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
- package/dist/prosemirror/commentReferenceAttrs.js +33 -0
- package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
- package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
- package/dist/prosemirror/conversion/fromProseDoc.js +269 -70
- package/dist/prosemirror/conversion/index.d.ts +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
- package/dist/prosemirror/conversion/toProseDoc.js +468 -308
- package/dist/prosemirror/extensions/StarterKit.js +8 -58
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
- package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
- package/dist/prosemirror/extensions/markRegistry.js +75 -0
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +16 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +3 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +5 -1
- package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
- package/dist/prosemirror/inlineWrapperStack.js +75 -0
- package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
- package/dist/prosemirror/pageBreakRunProjection.js +19 -9
- package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
- package/dist/prosemirror/replacedAnnotations.js +165 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +25 -1
- package/dist/prosemirror/schema/nodes.d.ts +90 -3
- package/dist/prosemirror/schema/nodes.js +16 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
- package/dist/prosemirror/validation.js +16 -2
- package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
- package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
- package/dist/prosemirror/zeroWidthAnchors.js +2 -0
- package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
- package/dist/render-dom/commentAnchorAttributes.js +54 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +3 -1
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/clipboard.d.ts +9 -1
- package/dist/utils/clipboard.js +27 -1
- package/dist/utils/findReplace.js +1 -1
- package/dist/utils/imageLuminance.d.ts +15 -0
- package/dist/utils/imageLuminance.js +32 -0
- package/dist/utils/mergeDocumentContent.js +7 -17
- package/dist/utils/replaceText.js +1 -0
- package/package.json +2 -2
- package/dist/docx/blockRangeMarkers.d.ts +0 -36
- package/dist/docx/blockRangeMarkers.js +0 -59
- package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
- package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
|
@@ -262,10 +262,11 @@ const invalidTableCellParagraphSourcePayload = (classification, path) => {
|
|
|
262
262
|
const tableCellBlockTraversalByType = {
|
|
263
263
|
blockSdt: "blockSdt",
|
|
264
264
|
paragraph: "paragraph",
|
|
265
|
+
preservedBlock: "leaf",
|
|
265
266
|
table: "table"
|
|
266
267
|
};
|
|
267
268
|
const tableCellParagraphContentTraversalByType = {
|
|
268
|
-
|
|
269
|
+
inlineWrapper: "content",
|
|
269
270
|
bookmarkEnd: "leaf",
|
|
270
271
|
bookmarkStart: "leaf",
|
|
271
272
|
commentRangeEnd: "leaf",
|
|
@@ -283,6 +284,7 @@ const tableCellParagraphContentTraversalByType = {
|
|
|
283
284
|
moveTo: "content",
|
|
284
285
|
moveToRangeEnd: "leaf",
|
|
285
286
|
moveToRangeStart: "leaf",
|
|
287
|
+
preservedInline: "leaf",
|
|
286
288
|
run: "run",
|
|
287
289
|
simpleField: "content"
|
|
288
290
|
};
|
|
@@ -294,6 +296,7 @@ const tableCellRunContentTraversalByType = {
|
|
|
294
296
|
footnoteRef: "leaf",
|
|
295
297
|
instrText: "leaf",
|
|
296
298
|
noBreakHyphen: "leaf",
|
|
299
|
+
preservedXml: "leaf",
|
|
297
300
|
renderedPageBreak: "leaf",
|
|
298
301
|
shape: "shape",
|
|
299
302
|
softHyphen: "leaf",
|
|
@@ -453,6 +456,7 @@ function visitDecodedTableCellBlock(value, path, context) {
|
|
|
453
456
|
for (const [index, child] of content.entries()) visitDecodedTableCellBlock(child, `${path}.content[${index}]`, context);
|
|
454
457
|
return;
|
|
455
458
|
}
|
|
459
|
+
case "leaf": return;
|
|
456
460
|
default: return traversal;
|
|
457
461
|
}
|
|
458
462
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
2
|
import { NumberingMap } from "./numberingParser.js";
|
|
3
|
-
import { StyleMap } from "./styleParser.js";
|
|
4
3
|
import { XmlElement } from "./xmlParser.js";
|
|
4
|
+
import { StyleMap } from "./styleParser.js";
|
|
5
5
|
import { TableParserFn } from "./textBoxParser.js";
|
|
6
6
|
//#region src/docx/paragraphTextBoxEnrichment.d.ts
|
|
7
7
|
declare const enrichParagraphTextBoxes: (paragraph: document_d_exports.Paragraph, paraXml: XmlElement, styles: StyleMap | null, theme: document_d_exports.Theme | null, numbering: NumberingMap | null, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, parseTable: TableParserFn) => void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pixelsToEmu } from "../utils/units.js";
|
|
2
2
|
import { parseParagraph } from "./paragraphParser.js";
|
|
3
|
-
import { getTextBoxContentElement,
|
|
3
|
+
import { getTextBoxContentElement, parseTextBox, parseTextBoxContent, scanRunForTextBoxDrawings } from "./textBoxParser.js";
|
|
4
|
+
import { isVmlPictParsedByRunParser } from "./vmlImageParser.js";
|
|
4
5
|
import { findDeep, getAttribute, getChildElements, getLocalName } from "./xmlParser.js";
|
|
5
6
|
//#region src/docx/paragraphTextBoxEnrichment.ts
|
|
6
7
|
const VML_HORIZONTAL_RELATIVES = /* @__PURE__ */ new Set([
|
|
@@ -182,7 +183,10 @@ const enrichTextBoxRuns = ({ content, xmlChildren, styles, theme, numbering, rel
|
|
|
182
183
|
if (parsedIndex < content.length && parsedContent?.type !== "run") parsedIndex += 1;
|
|
183
184
|
continue;
|
|
184
185
|
}
|
|
185
|
-
const { textBoxDrawings, vmlTextBoxes, hasNonTextBoxContent } = scanRunForTextBoxDrawings(
|
|
186
|
+
const { textBoxDrawings, vmlTextBoxes, hasNonTextBoxContent } = scanRunForTextBoxDrawings({
|
|
187
|
+
xmlRun: xmlChild,
|
|
188
|
+
claimedByRunParser: (pictElement) => isVmlPictParsedByRunParser(pictElement, rels, media)
|
|
189
|
+
});
|
|
186
190
|
const parsedRun = parsedContent?.type === "run" ? parsedContent : void 0;
|
|
187
191
|
const targetRun = parsedRun ?? (hasNonTextBoxContent ? lastConsumedRun : void 0);
|
|
188
192
|
const targetRunMatchesXml = targetRun !== void 0 && (hasNonTextBoxContent || parsedRun?.content.length === 0);
|
|
@@ -254,56 +258,6 @@ const enrichTextBoxRuns = ({ content, xmlChildren, styles, theme, numbering, rel
|
|
|
254
258
|
}
|
|
255
259
|
}
|
|
256
260
|
};
|
|
257
|
-
const scanRunForTextBoxDrawings = (xmlRun) => {
|
|
258
|
-
const textBoxDrawings = [];
|
|
259
|
-
const vmlTextBoxes = [];
|
|
260
|
-
let hasNonTextBoxContent = false;
|
|
261
|
-
const visitDrawing = (drawingEl) => {
|
|
262
|
-
if (isTextBoxDrawing(drawingEl)) {
|
|
263
|
-
textBoxDrawings.push(drawingEl);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
hasNonTextBoxContent = true;
|
|
267
|
-
};
|
|
268
|
-
for (const el of getChildElements(xmlRun)) {
|
|
269
|
-
const name = getLocalName(el.name ?? "");
|
|
270
|
-
if (name === "rPr") continue;
|
|
271
|
-
if (name === "drawing") {
|
|
272
|
-
visitDrawing(el);
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
if (name === "pict") {
|
|
276
|
-
if (findDeep(el, "v", "textbox")) if (findDeep(el, "v", "imagedata")) hasNonTextBoxContent = true;
|
|
277
|
-
else vmlTextBoxes.push(el);
|
|
278
|
-
else hasNonTextBoxContent = true;
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (name === "AlternateContent") {
|
|
282
|
-
const branches = getChildElements(el);
|
|
283
|
-
const choice = branches.find((branch) => getLocalName(branch.name ?? "") === "Choice");
|
|
284
|
-
const fallback = branches.find((branch) => getLocalName(branch.name ?? "") === "Fallback");
|
|
285
|
-
const tryBranch = (branch) => {
|
|
286
|
-
if (!branch) return false;
|
|
287
|
-
let found = false;
|
|
288
|
-
for (const innerEl of getChildElements(branch)) if (getLocalName(innerEl.name ?? "") === "drawing") {
|
|
289
|
-
visitDrawing(innerEl);
|
|
290
|
-
found = true;
|
|
291
|
-
}
|
|
292
|
-
return found;
|
|
293
|
-
};
|
|
294
|
-
let foundInBranch = tryBranch(choice);
|
|
295
|
-
if (!foundInBranch) foundInBranch = tryBranch(fallback);
|
|
296
|
-
if (!foundInBranch) hasNonTextBoxContent = true;
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
hasNonTextBoxContent = true;
|
|
300
|
-
}
|
|
301
|
-
return {
|
|
302
|
-
textBoxDrawings,
|
|
303
|
-
vmlTextBoxes,
|
|
304
|
-
hasNonTextBoxContent
|
|
305
|
-
};
|
|
306
|
-
};
|
|
307
261
|
const parseVmlTextBoxShape = (pictEl, styles, theme, numbering, rels, media, parseTable) => {
|
|
308
262
|
const shapeEl = findDeep(pictEl, "v", "shape");
|
|
309
263
|
const textBoxEl = shapeEl ? findDeep(shapeEl, "v", "textbox") : null;
|
|
@@ -16,7 +16,7 @@ const visitParagraphRuns = (paragraph, visit) => {
|
|
|
16
16
|
case "moveFrom":
|
|
17
17
|
case "moveTo":
|
|
18
18
|
case "inlineSdt":
|
|
19
|
-
case "
|
|
19
|
+
case "inlineWrapper":
|
|
20
20
|
for (const child of content.content) visitParagraphContent(child);
|
|
21
21
|
return;
|
|
22
22
|
case "complexField":
|
|
@@ -32,7 +32,8 @@ const visitParagraphRuns = (paragraph, visit) => {
|
|
|
32
32
|
case "moveFromRangeEnd":
|
|
33
33
|
case "moveToRangeStart":
|
|
34
34
|
case "moveToRangeEnd":
|
|
35
|
-
case "mathEquation":
|
|
35
|
+
case "mathEquation":
|
|
36
|
+
case "preservedInline": return;
|
|
36
37
|
default: return content;
|
|
37
38
|
}
|
|
38
39
|
};
|
|
@@ -69,7 +70,7 @@ const visitInlineContentSlots = (paragraph, visit) => {
|
|
|
69
70
|
case "deletion":
|
|
70
71
|
case "moveFrom":
|
|
71
72
|
case "moveTo":
|
|
72
|
-
case "
|
|
73
|
+
case "inlineWrapper":
|
|
73
74
|
visitContent(item.content);
|
|
74
75
|
break;
|
|
75
76
|
case "run":
|
|
@@ -83,7 +84,8 @@ const visitInlineContentSlots = (paragraph, visit) => {
|
|
|
83
84
|
case "moveFromRangeEnd":
|
|
84
85
|
case "moveToRangeStart":
|
|
85
86
|
case "moveToRangeEnd":
|
|
86
|
-
case "mathEquation":
|
|
87
|
+
case "mathEquation":
|
|
88
|
+
case "preservedInline": break;
|
|
87
89
|
default: panic(`Unsupported paragraph content: ${JSON.stringify(item)}`);
|
|
88
90
|
}
|
|
89
91
|
}
|
|
@@ -145,6 +147,7 @@ const visitDocxParagraphs = ({ documentBody, headers, footers, footnotes, endnot
|
|
|
145
147
|
visitTable(block);
|
|
146
148
|
return;
|
|
147
149
|
}
|
|
150
|
+
if (block.type === "preservedBlock") return;
|
|
148
151
|
visitBlocks(block.content);
|
|
149
152
|
};
|
|
150
153
|
const visitBlocks = (blocks) => {
|
|
@@ -36,7 +36,8 @@ const PARSE_WARNING_MESSAGES = {
|
|
|
36
36
|
[PARSE_WARNING_CODES.unrecognisedOnOffValue]: (warning) => `Ignored on/off value${quoted(warning.value)}${where(warning)}; ST_OnOff is 1, 0, true, false, on or off.`,
|
|
37
37
|
[PARSE_WARNING_CODES.borderWithoutValue]: (warning) => `Read a border with no w:val${where(warning)} as having no border style.`,
|
|
38
38
|
[PARSE_WARNING_CODES.styleSetDuplicateStyleId]: (warning) => `Dropped a style repeating the id${quoted(warning.value)} another style in the set already defines.`,
|
|
39
|
-
[PARSE_WARNING_CODES.styleSetInitialStyleMissing]: (warning) => `The style set names initial paragraph style${quoted(warning.value)}, which it does not contain; used the set's default instead${where(warning)}
|
|
39
|
+
[PARSE_WARNING_CODES.styleSetInitialStyleMissing]: (warning) => `The style set names initial paragraph style${quoted(warning.value)}, which it does not contain; used the set's default instead${where(warning)}.`,
|
|
40
|
+
[PARSE_WARNING_CODES.pageBreakProjectionApproximated]: (warning) => `${warning.detail ?? "An explicit page break is laid out approximately"}${where(warning)}.`
|
|
40
41
|
};
|
|
41
42
|
const formatParseWarning = (warning) => PARSE_WARNING_MESSAGES[warning.code](warning);
|
|
42
43
|
const formatParseWarnings = (warnings) => warnings.map(formatParseWarning);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/preservedRunContent.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The visible text a preserved run child contributes, or `""` when it shows
|
|
6
|
+
* nothing. Unknown markup contributes nothing on purpose: guessing at the text
|
|
7
|
+
* of an element folio has never seen would put invented words in a document.
|
|
8
|
+
*/
|
|
9
|
+
declare const preservedRunChildText: (element: XmlElement) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Capture one unmodelled run child.
|
|
12
|
+
*
|
|
13
|
+
* `captureVerbatimXml` materialises exactly the namespace bindings the
|
|
14
|
+
* fragment's own prefixes need and leaves the canonical ones to the rebuilt
|
|
15
|
+
* root, so ordinary `w:` markup comes back byte for byte while a foreign
|
|
16
|
+
* prefix still arrives bound. Copying the whole root scope onto the fragment
|
|
17
|
+
* instead would rewrite every capture with declarations it does not use.
|
|
18
|
+
*/
|
|
19
|
+
declare const preserveRunChild: (element: XmlElement) => document_d_exports.PreservedXmlContent;
|
|
20
|
+
/**
|
|
21
|
+
* One capture from the shared dispatcher's sink, as a member of the
|
|
22
|
+
* container's own inline union.
|
|
23
|
+
*
|
|
24
|
+
* The sink holds markup the schema does not declare for the container at all,
|
|
25
|
+
* so nothing is known about it beyond its bytes: `text` is empty because a
|
|
26
|
+
* name folio has never seen shows nothing folio can read.
|
|
27
|
+
*/
|
|
28
|
+
declare const preservedInlineCapture: (xml: string) => document_d_exports.PreservedInline;
|
|
29
|
+
/** Capture one unmodelled inline child of a paragraph or an inline wrapper. */
|
|
30
|
+
declare const preserveInlineChild: (element: XmlElement) => document_d_exports.PreservedInline;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { preserveInlineChild, preserveRunChild, preservedInlineCapture, preservedRunChildText };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
2
|
+
import { findChildrenByLocalName, getChildElements, getLocalName, getTextContent } from "./xmlParser.js";
|
|
3
|
+
//#region src/docx/preservedRunContent.ts
|
|
4
|
+
/**
|
|
5
|
+
* Where a preserved run child hides text a reader sees, by local name: the
|
|
6
|
+
* child elements whose `w:t` descendants are on the line rather than above,
|
|
7
|
+
* beside, or nowhere.
|
|
8
|
+
*
|
|
9
|
+
* `w:ruby` is the whole list today. Its `w:rt` is the annotation printed above
|
|
10
|
+
* the base and is not the sentence's text; its `w:rubyBase` is.
|
|
11
|
+
*/
|
|
12
|
+
const VISIBLE_TEXT_CHILDREN = /* @__PURE__ */ new Map([["ruby", ["rubyBase"]]]);
|
|
13
|
+
/** Cap on the text one preserved child contributes, mirroring `w:t` handling. */
|
|
14
|
+
const MAX_PRESERVED_TEXT_LENGTH = 1e5;
|
|
15
|
+
const collectTextContent = (element, into) => {
|
|
16
|
+
for (const child of getChildElements(element)) {
|
|
17
|
+
if (getLocalName(child.name) === "t") {
|
|
18
|
+
into.push(getTextContent(child));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
collectTextContent(child, into);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The visible text a preserved run child contributes, or `""` when it shows
|
|
26
|
+
* nothing. Unknown markup contributes nothing on purpose: guessing at the text
|
|
27
|
+
* of an element folio has never seen would put invented words in a document.
|
|
28
|
+
*/
|
|
29
|
+
const preservedRunChildText = (element) => {
|
|
30
|
+
const sources = VISIBLE_TEXT_CHILDREN.get(getLocalName(element.name));
|
|
31
|
+
if (sources === void 0) return "";
|
|
32
|
+
const parts = [];
|
|
33
|
+
for (const localName of sources) for (const source of findChildrenByLocalName(element, localName)) collectTextContent(source, parts);
|
|
34
|
+
return parts.join("").slice(0, MAX_PRESERVED_TEXT_LENGTH);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Capture one unmodelled run child.
|
|
38
|
+
*
|
|
39
|
+
* `captureVerbatimXml` materialises exactly the namespace bindings the
|
|
40
|
+
* fragment's own prefixes need and leaves the canonical ones to the rebuilt
|
|
41
|
+
* root, so ordinary `w:` markup comes back byte for byte while a foreign
|
|
42
|
+
* prefix still arrives bound. Copying the whole root scope onto the fragment
|
|
43
|
+
* instead would rewrite every capture with declarations it does not use.
|
|
44
|
+
*/
|
|
45
|
+
const preserveRunChild = (element) => ({
|
|
46
|
+
type: "preservedXml",
|
|
47
|
+
xml: captureVerbatimXml(element),
|
|
48
|
+
text: preservedRunChildText(element)
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* Where an unmodelled *inline* child hides text a reader sees, by local name.
|
|
52
|
+
*
|
|
53
|
+
* `w:customXml` and `w:smartTag` are transparent wrappers (ECMA-376 §17.5.1,
|
|
54
|
+
* §17.5.1.9): their content is ordinary inline content, so their `w:t`
|
|
55
|
+
* descendants are on the line. Every other inline child folio captures —
|
|
56
|
+
* `w:permStart`, `w:proofErr` and the custom-XML revision ranges — is an
|
|
57
|
+
* empty marker, and an element folio has never seen contributes nothing on
|
|
58
|
+
* purpose, because guessing at its text would put invented words in a
|
|
59
|
+
* document.
|
|
60
|
+
*/
|
|
61
|
+
const VISIBLE_TEXT_INLINE_CHILDREN = /* @__PURE__ */ new Set(["customXml", "smartTag"]);
|
|
62
|
+
/**
|
|
63
|
+
* One capture from the shared dispatcher's sink, as a member of the
|
|
64
|
+
* container's own inline union.
|
|
65
|
+
*
|
|
66
|
+
* The sink holds markup the schema does not declare for the container at all,
|
|
67
|
+
* so nothing is known about it beyond its bytes: `text` is empty because a
|
|
68
|
+
* name folio has never seen shows nothing folio can read.
|
|
69
|
+
*/
|
|
70
|
+
const preservedInlineCapture = (xml) => ({
|
|
71
|
+
type: "preservedInline",
|
|
72
|
+
xml,
|
|
73
|
+
text: ""
|
|
74
|
+
});
|
|
75
|
+
/** Capture one unmodelled inline child of a paragraph or an inline wrapper. */
|
|
76
|
+
const preserveInlineChild = (element) => {
|
|
77
|
+
const parts = [];
|
|
78
|
+
if (VISIBLE_TEXT_INLINE_CHILDREN.has(getLocalName(element.name))) collectTextContent(element, parts);
|
|
79
|
+
return {
|
|
80
|
+
type: "preservedInline",
|
|
81
|
+
xml: captureVerbatimXml(element),
|
|
82
|
+
text: parts.join("").slice(0, MAX_PRESERVED_TEXT_LENGTH)
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
export { preserveInlineChild, preserveRunChild, preservedInlineCapture, preservedRunChildText };
|
|
@@ -37,9 +37,7 @@ const scanHyperlink = (hyperlink, scan) => {
|
|
|
37
37
|
};
|
|
38
38
|
const scanInlineContent = (content, scan) => {
|
|
39
39
|
for (const child of content) {
|
|
40
|
-
|
|
41
|
-
if (child.type === "run") result = scanRun(child, scan);
|
|
42
|
-
else if (child.type === "hyperlink") result = scanHyperlink(child, scan);
|
|
40
|
+
const result = scanParagraphContent(child, scan);
|
|
43
41
|
if (result !== void 0) return result;
|
|
44
42
|
}
|
|
45
43
|
};
|
|
@@ -48,7 +46,7 @@ const scanParagraphContent = (content, scan) => {
|
|
|
48
46
|
if (content.type === "hyperlink") return scanHyperlink(content, scan);
|
|
49
47
|
if (content.type === "simpleField") return scanInlineContent(content.content, scan);
|
|
50
48
|
if (content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") return scanInlineContent(content.content, scan);
|
|
51
|
-
if (content.type === "inlineSdt") {
|
|
49
|
+
if (content.type === "inlineSdt" || content.type === "inlineWrapper") {
|
|
52
50
|
for (const child of content.content) {
|
|
53
51
|
const result = scanParagraphContent(child, scan);
|
|
54
52
|
if (result !== void 0) return result;
|
package/dist/docx/rezip.js
CHANGED
|
@@ -29,7 +29,7 @@ import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
|
|
|
29
29
|
import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
|
|
30
30
|
import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerializer.js";
|
|
31
31
|
import { serializeThemeXml } from "./serializer/themeSerializer.js";
|
|
32
|
-
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, findChild, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
|
|
32
|
+
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
|
|
33
33
|
import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
|
|
34
34
|
import { panic } from "better-result";
|
|
35
35
|
import { escapeXmlAttribute, escapeXmlText, validateDocxPackage } from "@stll/docx-core";
|
|
@@ -443,6 +443,7 @@ function collectHyperlinksWithoutRId(blocks) {
|
|
|
443
443
|
break;
|
|
444
444
|
case "simpleField":
|
|
445
445
|
case "inlineSdt":
|
|
446
|
+
case "inlineWrapper":
|
|
446
447
|
case "insertion":
|
|
447
448
|
case "deletion":
|
|
448
449
|
case "moveFrom":
|
|
@@ -454,7 +455,7 @@ function collectHyperlinksWithoutRId(blocks) {
|
|
|
454
455
|
};
|
|
455
456
|
for (const block of blocks) if (block.type === "paragraph") collectInlineHyperlinks(block.content);
|
|
456
457
|
else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) hyperlinks.push(...collectHyperlinksWithoutRId(cell.content));
|
|
457
|
-
else hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
|
|
458
|
+
else if (block.type === "blockSdt") hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
|
|
458
459
|
return hyperlinks;
|
|
459
460
|
}
|
|
460
461
|
/** The selective save boundary must use the same resource census as full repack. */
|
|
@@ -1425,7 +1426,26 @@ const seedStylesXmlWith = (missing) => {
|
|
|
1425
1426
|
const rootClose = SEED_STYLES_XML.lastIndexOf(STYLES_CLOSE_ROOT);
|
|
1426
1427
|
return SEED_STYLES_XML.slice(0, rootClose) + missing.map(serializeStyle).join("") + SEED_STYLES_XML.slice(rootClose);
|
|
1427
1428
|
};
|
|
1428
|
-
|
|
1429
|
+
/**
|
|
1430
|
+
* The style ids `word/styles.xml` already defines.
|
|
1431
|
+
*
|
|
1432
|
+
* Read by parsing the part, because the model holds the *decoded* id and the
|
|
1433
|
+
* part holds its XML spelling: `w:styleId='Body A'` is single-quoted,
|
|
1434
|
+
* `w:styleId="Header & Footer"` is escaped, and either may sit behind an
|
|
1435
|
+
* attribute value containing `>`. A reader that misses one of those reports a
|
|
1436
|
+
* style the part already defines as missing, and
|
|
1437
|
+
* {@link serializeAddedStylesIntoZip} appends it — on every save, for ever.
|
|
1438
|
+
*/
|
|
1439
|
+
const definedStyleIds = (stylesXml) => {
|
|
1440
|
+
const root = parseXml(stylesXml);
|
|
1441
|
+
const styles = findChildByNamespaceUri(root, WORDPROCESSINGML_NAMESPACE_URIS, "styles") ?? root;
|
|
1442
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1443
|
+
for (const style of findChildrenByNamespaceUri(styles, WORDPROCESSINGML_NAMESPACE_URIS, "style")) {
|
|
1444
|
+
const id = getAttributeByNamespaceUri(style, WORDPROCESSINGML_NAMESPACE_URIS, "styleId") ?? style.attributes?.["w:styleId"];
|
|
1445
|
+
if (id !== void 0 && id !== null && id !== "") ids.add(String(id));
|
|
1446
|
+
}
|
|
1447
|
+
return ids;
|
|
1448
|
+
};
|
|
1429
1449
|
/**
|
|
1430
1450
|
* Append styles the model defines but the original `word/styles.xml` lacks.
|
|
1431
1451
|
* Existing styles stay byte-exact (edits to them are not written here, the
|
|
@@ -1471,9 +1491,13 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
|
|
|
1471
1491
|
});
|
|
1472
1492
|
return;
|
|
1473
1493
|
}
|
|
1474
|
-
const existing =
|
|
1475
|
-
|
|
1476
|
-
const
|
|
1494
|
+
const existing = new Set(definedStyleIds(originalXml));
|
|
1495
|
+
const added = [];
|
|
1496
|
+
for (const style of styles.styles) {
|
|
1497
|
+
if (existing.has(style.styleId)) continue;
|
|
1498
|
+
existing.add(style.styleId);
|
|
1499
|
+
added.push(style);
|
|
1500
|
+
}
|
|
1477
1501
|
if (added.length === 0) return;
|
|
1478
1502
|
const patched = originalXml.slice(0, rootClose) + added.map(serializeStyle).join("") + originalXml.slice(rootClose);
|
|
1479
1503
|
newZip.file(file.name, patched, {
|
|
@@ -1481,28 +1505,60 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
|
|
|
1481
1505
|
compressionOptions: { level: compressionLevel }
|
|
1482
1506
|
});
|
|
1483
1507
|
}
|
|
1508
|
+
/**
|
|
1509
|
+
* The XML to write for a patched note part, or null to keep the original.
|
|
1510
|
+
*
|
|
1511
|
+
* A refusal over comment-range balance is not a failure to serialize: the
|
|
1512
|
+
* splice was too narrow to keep a comment's range whole, so the part is
|
|
1513
|
+
* written from the model, which is balanced with itself and with the
|
|
1514
|
+
* `document.xml` this repack rewrites beside it. Word's required separator
|
|
1515
|
+
* notes come back synthesized rather than byte-exact, the price of keeping the
|
|
1516
|
+
* comment anchored to the text it was written about. Any other refusal means a
|
|
1517
|
+
* changed paragraph could not be located in the part at all, which no wider
|
|
1518
|
+
* rewrite fixes.
|
|
1519
|
+
*/
|
|
1520
|
+
const notePartXmlFor = ({ patch, originalXml, replacementXml, hasDirtyParagraph, elementName, partName }) => {
|
|
1521
|
+
const keepOriginal = () => {
|
|
1522
|
+
if (hasDirtyParagraph) throw new DocxPackageFidelityError(`Cannot serialize changed ${elementName} paragraphs into ${partName}`);
|
|
1523
|
+
return null;
|
|
1524
|
+
};
|
|
1525
|
+
switch (patch.type) {
|
|
1526
|
+
case "patched": return patch.xml === originalXml ? keepOriginal() : patch.xml;
|
|
1527
|
+
case "refused": switch (patch.reason) {
|
|
1528
|
+
case "comment-range-balance": return replacementXml;
|
|
1529
|
+
case "unroutable-paragraph": return keepOriginal();
|
|
1530
|
+
default: {
|
|
1531
|
+
const unreachable = patch.reason;
|
|
1532
|
+
return panic("Unhandled note-part patch refusal", { reason: unreachable });
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
default: return panic("Unhandled note-part patch", { patch });
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1484
1538
|
async function patchNotePartIntoZip({ conventionalLowerPath, currentXml, replacementXml, baselineFrom, elementName, changedNoteParaIds, originalZip, newZip, compressionLevel }) {
|
|
1485
1539
|
const file = findNotePartEntry(originalZip, conventionalLowerPath);
|
|
1486
1540
|
if (!file) return;
|
|
1487
1541
|
const originalXml = await file.async("text");
|
|
1488
1542
|
const baselineXml = baselineFrom(originalXml);
|
|
1489
1543
|
const effectiveChangedNoteParaIds = changedNoteParaIds ?? collectChangedNoteParaIds(baselineXml, currentXml);
|
|
1490
|
-
const
|
|
1544
|
+
const currentParaIds = collectParaIds(currentXml);
|
|
1545
|
+
const patchedXml = notePartXmlFor({
|
|
1546
|
+
patch: buildPatchedNotePartXml({
|
|
1547
|
+
originalXml,
|
|
1548
|
+
baselineXml,
|
|
1549
|
+
serializedXml: currentXml,
|
|
1550
|
+
replacementXml,
|
|
1551
|
+
elementName,
|
|
1552
|
+
changedParaIds: effectiveChangedNoteParaIds
|
|
1553
|
+
}),
|
|
1491
1554
|
originalXml,
|
|
1492
|
-
baselineXml,
|
|
1493
|
-
serializedXml: currentXml,
|
|
1494
1555
|
replacementXml,
|
|
1556
|
+
hasDirtyParagraph: [...effectiveChangedNoteParaIds].some((paraId) => currentParaIds.has(paraId)),
|
|
1495
1557
|
elementName,
|
|
1496
|
-
|
|
1558
|
+
partName: file.name
|
|
1497
1559
|
});
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
if (patched === null || hasDirtyParagraph && patched === originalXml) {
|
|
1501
|
-
if (hasDirtyParagraph) throw new DocxPackageFidelityError(`Cannot serialize changed ${elementName} paragraphs into ${file.name}`);
|
|
1502
|
-
return;
|
|
1503
|
-
}
|
|
1504
|
-
if (patched === originalXml) return;
|
|
1505
|
-
newZip.file(file.name, patched, {
|
|
1560
|
+
if (patchedXml === null || patchedXml === originalXml) return;
|
|
1561
|
+
newZip.file(file.name, patchedXml, {
|
|
1506
1562
|
compression: "DEFLATE",
|
|
1507
1563
|
compressionOptions: { level: compressionLevel }
|
|
1508
1564
|
});
|
package/dist/docx/runParser.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
-
import { StyleMap } from "./styleParser.js";
|
|
3
2
|
import { XmlElement } from "./xmlParser.js";
|
|
3
|
+
import { StyleMap } from "./styleParser.js";
|
|
4
4
|
//#region src/docx/runParser.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* `w:vertAlign` `baseline` is the reserved value that means "no vertical
|
package/dist/docx/runParser.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
|
|
2
|
+
import { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
|
|
2
3
|
import { parseDiagramPreview } from "./diagramPreview.js";
|
|
4
|
+
import { parseFieldState } from "./fieldState.js";
|
|
3
5
|
import { isGroupDrawing, parseGroupDrawing } from "./groupDrawingParser.js";
|
|
4
6
|
import { parseImage } from "./imageParser.js";
|
|
5
7
|
import { imageRawXmlFingerprint } from "./imageRawXml.js";
|
|
6
8
|
import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
9
|
+
import { preserveRunChild } from "./preservedRunContent.js";
|
|
7
10
|
import { parseShading } from "./shadingParser.js";
|
|
8
11
|
import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
|
|
9
12
|
import { isTextBoxDrawing } from "./textBoxParser.js";
|
|
@@ -11,7 +14,7 @@ import { resolveThemeFontRef } from "./themeParser.js";
|
|
|
11
14
|
import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
|
|
12
15
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
13
16
|
import { parseVmlImageContent, shouldPreserveRawVmlPict } from "./vmlImageParser.js";
|
|
14
|
-
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute,
|
|
17
|
+
import { cloneWithXmlnsDeclarations, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, selectAlternateContentBranch } from "./xmlParser.js";
|
|
15
18
|
import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
|
|
16
19
|
//#region src/docx/runParser.ts
|
|
17
20
|
/**
|
|
@@ -419,17 +422,14 @@ function parseEndnoteReference(element) {
|
|
|
419
422
|
*/
|
|
420
423
|
function parseFieldChar(element) {
|
|
421
424
|
const fldCharType = getAttribute(element, "w", "fldCharType");
|
|
422
|
-
const fldLock = parseOnOffAttribute(element, "w", "fldLock") === true;
|
|
423
|
-
const dirty = parseOnOffAttribute(element, "w", "dirty") === true;
|
|
424
425
|
let charType = "begin";
|
|
425
426
|
if (fldCharType === "separate") charType = "separate";
|
|
426
427
|
else if (fldCharType === "end") charType = "end";
|
|
427
428
|
const content = {
|
|
428
429
|
type: "fieldChar",
|
|
429
|
-
charType
|
|
430
|
+
charType,
|
|
431
|
+
...parseFieldState(element)
|
|
430
432
|
};
|
|
431
|
-
if (fldLock) content.fldLock = true;
|
|
432
|
-
if (dirty) content.dirty = true;
|
|
433
433
|
const numberingChange = findChild(element, "w", "numberingChange");
|
|
434
434
|
if (numberingChange) {
|
|
435
435
|
const original = getAttribute(numberingChange, "w", "original");
|
|
@@ -576,10 +576,11 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
576
576
|
}
|
|
577
577
|
case "object": {
|
|
578
578
|
const objectPreview = parseVmlImageContent(child, rels, media, rootXmlns);
|
|
579
|
-
|
|
579
|
+
contents.push(objectPreview ?? preserveRunChild(child));
|
|
580
580
|
break;
|
|
581
581
|
}
|
|
582
582
|
case "rPr": break;
|
|
583
|
+
case "commentReference": break;
|
|
583
584
|
case "lastRenderedPageBreak":
|
|
584
585
|
contents.push({ type: "renderedPageBreak" });
|
|
585
586
|
break;
|
|
@@ -637,11 +638,9 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
|
|
|
637
638
|
if (contents.length === contentsBeforeAlternate && !hasTextBoxBranch) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
|
|
638
639
|
break;
|
|
639
640
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
case "continuationSeparator": break;
|
|
644
|
-
default: break;
|
|
641
|
+
default:
|
|
642
|
+
contents.push(preserveRunChild(child));
|
|
643
|
+
break;
|
|
645
644
|
}
|
|
646
645
|
return contents;
|
|
647
646
|
}
|
|
@@ -668,6 +667,11 @@ function parseRun(node, styles, theme, rels = null, media = null, rootXmlns = {}
|
|
|
668
667
|
if (propertyChangesResult) run.propertyChanges = propertyChangesResult;
|
|
669
668
|
}
|
|
670
669
|
run.content = parseRunContents(node, rels, media, mergeXmlnsDeclarations(rootXmlns, node));
|
|
670
|
+
const remainder = attributeRemainder({
|
|
671
|
+
element: node,
|
|
672
|
+
modelled: NO_MODELLED_ATTRIBUTES
|
|
673
|
+
});
|
|
674
|
+
if (remainder) run.preservedAttributes = remainder;
|
|
671
675
|
return run;
|
|
672
676
|
}
|
|
673
677
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
|
|
1
2
|
import { parseBorderSpec } from "./borderParser.js";
|
|
2
3
|
import { parseFooterReference, parseHeaderReference } from "./headerFooterRefParser.js";
|
|
3
4
|
import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
|
|
@@ -159,7 +160,8 @@ function parseSectionProperties(sectPr, context) {
|
|
|
159
160
|
if (space !== void 0) props.columnSpace = space;
|
|
160
161
|
const equalWidth = parseOnOffAttribute(cols, "w", "equalWidth");
|
|
161
162
|
if (equalWidth !== void 0) props.equalWidth = equalWidth;
|
|
162
|
-
|
|
163
|
+
const separator = parseOnOffAttribute(cols, "w", "sep");
|
|
164
|
+
if (separator !== void 0) props.separator = separator;
|
|
163
165
|
const colElements = findChildren(cols, "w", "col");
|
|
164
166
|
if (colElements.length > 0) {
|
|
165
167
|
props.columns = [];
|
|
@@ -310,6 +312,11 @@ function parseSectionProperties(sectPr, context) {
|
|
|
310
312
|
return change;
|
|
311
313
|
});
|
|
312
314
|
if (propertyChanges.length > 0) props.propertyChanges = propertyChanges;
|
|
315
|
+
const remainder = attributeRemainder({
|
|
316
|
+
element: sectPr,
|
|
317
|
+
modelled: NO_MODELLED_ATTRIBUTES
|
|
318
|
+
});
|
|
319
|
+
if (remainder) props.preservedAttributes = remainder;
|
|
313
320
|
Object.defineProperty(props, unserializedSectionPropertyChildNames, {
|
|
314
321
|
enumerable: true,
|
|
315
322
|
value: unhandledChildNames
|
|
@@ -109,6 +109,24 @@ declare function buildPatchedDocumentXml(originalXml: string, serializedXml: str
|
|
|
109
109
|
* the caller can fall back to preserving the original part verbatim.
|
|
110
110
|
*/
|
|
111
111
|
declare function buildPatchedNoteXml(originalXml: string, serializedXml: string, changedIds: Set<string>): string | null;
|
|
112
|
+
/** One region of a part replaced by re-serialized XML. */
|
|
113
|
+
type XmlSplice = {
|
|
114
|
+
start: number;
|
|
115
|
+
end: number;
|
|
116
|
+
newXml: string;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Apply `splices` to `xml`, end-to-start so earlier offsets stay valid.
|
|
120
|
+
*
|
|
121
|
+
* Every selective patch is a splice, and every splice goes through here, so
|
|
122
|
+
* the refusal below is a property of the operation rather than a check each
|
|
123
|
+
* site has to remember. A patch rewrites the regions an edit touched and keeps
|
|
124
|
+
* the rest of the part byte-for-byte, so it can write half a comment range:
|
|
125
|
+
* invalid OOXML that anchors the comment to nothing. Answers null when it
|
|
126
|
+
* would, leaving the caller to rewrite a wider region — ultimately the whole
|
|
127
|
+
* part from the model, which is balanced with itself.
|
|
128
|
+
*/
|
|
129
|
+
declare const spliceXml: (xml: string, splices: readonly XmlSplice[]) => string | null;
|
|
112
130
|
type NoteElementName = "footnote" | "endnote";
|
|
113
131
|
declare const collectChangedNoteParaIds: (baselineXml: string, currentXml: string) => Set<string>;
|
|
114
132
|
type BuildPatchedNotePartXmlOptions = {
|
|
@@ -119,6 +137,25 @@ type BuildPatchedNotePartXmlOptions = {
|
|
|
119
137
|
elementName: NoteElementName;
|
|
120
138
|
changedParaIds?: ReadonlySet<string>;
|
|
121
139
|
};
|
|
140
|
+
/** Why a note part could not be patched; see {@link NotePartPatch}. */
|
|
141
|
+
type NotePartPatchRefusal =
|
|
142
|
+
/** A note element, or a changed paragraph, was missing or ambiguous. */
|
|
143
|
+
"unroutable-paragraph" |
|
|
144
|
+
/** Even rewriting whole notes would leave a comment range with one half. */
|
|
145
|
+
"comment-range-balance";
|
|
146
|
+
/**
|
|
147
|
+
* What patching a note part produced. `refused` is not a failure to serialize:
|
|
148
|
+
* the caller writes the part from the model instead, the note-part reading of
|
|
149
|
+
* the fall back to a full repack the document story takes when its own splice
|
|
150
|
+
* is refused.
|
|
151
|
+
*/
|
|
152
|
+
type NotePartPatch = {
|
|
153
|
+
type: "patched";
|
|
154
|
+
xml: string;
|
|
155
|
+
} | {
|
|
156
|
+
type: "refused";
|
|
157
|
+
reason: NotePartPatchRefusal;
|
|
158
|
+
};
|
|
122
159
|
/**
|
|
123
160
|
* Patch an existing note part from its model serialization.
|
|
124
161
|
*
|
|
@@ -130,8 +167,15 @@ type BuildPatchedNotePartXmlOptions = {
|
|
|
130
167
|
* notes, and unaffected equal-shape paragraphs remain byte-exact.
|
|
131
168
|
* `replacementXml` also supplies synthesized automatic note-reference marks,
|
|
132
169
|
* which the parsed model intentionally omits.
|
|
170
|
+
*
|
|
171
|
+
* A comment can be anchored on a note's own text, so its range spans that
|
|
172
|
+
* note's paragraphs and an edit inside the span moves a range half from one
|
|
173
|
+
* paragraph to another. Replacing only the dirty paragraph then drops the half
|
|
174
|
+
* it held and leaves the other standing, so {@link spliceXml} refuses the
|
|
175
|
+
* result and the changed notes are rewritten whole instead — model content on
|
|
176
|
+
* both sides of the range, with every other note still byte-exact.
|
|
133
177
|
*/
|
|
134
|
-
declare function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, replacementXml, elementName, changedParaIds }: BuildPatchedNotePartXmlOptions):
|
|
178
|
+
declare function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, replacementXml, elementName, changedParaIds }: BuildPatchedNotePartXmlOptions): NotePartPatch;
|
|
135
179
|
type ChangedNumberingDefs = {
|
|
136
180
|
abstractNums: Set<string>;
|
|
137
181
|
nums: Set<string>;
|
|
@@ -178,4 +222,4 @@ declare const patchNumberingDefinitions: ({ originalXml, baselineXml, currentXml
|
|
|
178
222
|
*/
|
|
179
223
|
declare function appendNumberingDefs(xml: string, currentXml: string, added: ChangedNumberingDefs): string | null;
|
|
180
224
|
//#endregion
|
|
181
|
-
export { ChangedNumberingDefs, ParagraphOffsets, PatchSafetyOptions, PatchValidationResult, appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, validatePatchSafety };
|
|
225
|
+
export { ChangedNumberingDefs, NotePartPatch, NotePartPatchRefusal, ParagraphOffsets, PatchSafetyOptions, PatchValidationResult, XmlSplice, appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, spliceXml, validatePatchSafety };
|