@stll/folio-core 0.54.2 → 0.56.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 +185 -33
- package/dist/ai-edits/headless.js +3 -2
- package/dist/ai-edits/types.d.ts +7 -1
- package/dist/compare/compare.js +1 -0
- package/dist/compare/content-types.d.ts +3 -15
- package/dist/compare/reproducible-package.d.ts +0 -17
- package/dist/compare/reproducible-package.js +12 -18
- package/dist/controller/canonicalClipboard.d.ts +23 -0
- package/dist/controller/canonicalClipboard.js +476 -0
- package/dist/controller/canonicalClipboardResources.d.ts +21 -0
- package/dist/controller/canonicalClipboardResources.js +382 -0
- package/dist/controller/canonicalComposition.d.ts +53 -0
- package/dist/controller/canonicalComposition.js +296 -0
- package/dist/controller/canonicalInlineProjection.d.ts +113 -0
- package/dist/controller/canonicalInlineProjection.js +393 -0
- package/dist/controller/canonicalInput.d.ts +54 -0
- package/dist/controller/canonicalInput.js +377 -0
- package/dist/controller/canonicalInputTimer.d.ts +7 -0
- package/dist/controller/canonicalInputTimer.js +11 -0
- package/dist/controller/canonicalOperations.d.ts +22 -0
- package/dist/controller/canonicalOperations.js +85 -0
- package/dist/controller/canonicalPublicOperations.d.ts +48 -0
- package/dist/controller/canonicalPublicOperations.js +556 -0
- package/dist/controller/canonicalReview.d.ts +12 -0
- package/dist/controller/canonicalReview.js +19 -0
- package/dist/controller/canonicalSession.d.ts +167 -0
- package/dist/controller/canonicalSession.js +1186 -0
- package/dist/controller/canonicalStoryEditor.d.ts +36 -0
- package/dist/controller/canonicalStoryEditor.js +87 -0
- package/dist/controller/canonicalStructure.d.ts +16 -0
- package/dist/controller/canonicalStructure.js +322 -0
- package/dist/controller/canonicalTocSelection.d.ts +11 -0
- package/dist/controller/canonicalTocSelection.js +43 -0
- package/dist/controller/folioEditor.d.ts +3 -10
- package/dist/controller/folioEditor.js +14 -4
- package/dist/controller/headerFooterEditorManager.d.ts +5 -0
- package/dist/controller/headerFooterEditorManager.js +72 -3
- package/dist/controller/hiddenEditorApi.d.ts +45 -0
- package/dist/controller/hiddenEditorApi.js +29 -8
- package/dist/controller/hiddenEditorManager.d.ts +13 -2
- package/dist/controller/hiddenEditorManager.js +639 -31
- package/dist/controller/layoutPipeline.d.ts +1 -1
- package/dist/controller/layoutPipeline.js +2 -0
- package/dist/controller/noteEditorManager.d.ts +5 -0
- package/dist/controller/noteEditorManager.js +114 -15
- package/dist/document-operations.d.ts +8 -1
- package/dist/document-operations.js +6 -4
- package/dist/docx/attributeRemainder.js +1 -0
- package/dist/docx/blockPlainText.d.ts +1 -1
- package/dist/docx/blockPlainText.js +11 -3
- package/dist/docx/canonicalResourceSave.d.ts +16 -0
- package/dist/docx/canonicalResourceSave.js +27 -0
- package/dist/docx/canonicalSave.d.ts +29 -0
- package/dist/docx/canonicalSave.js +101 -0
- package/dist/docx/canonicalSessionInput.d.ts +11 -0
- package/dist/docx/canonicalSessionInput.js +27 -0
- package/dist/docx/canonicalStoryRepack.d.ts +10 -0
- package/dist/docx/canonicalStoryRepack.js +74 -0
- package/dist/docx/contentTypeRegistry.d.ts +8 -0
- package/dist/docx/contentTypeRegistry.js +64 -0
- package/dist/docx/documentParser.d.ts +1 -13
- package/dist/docx/documentParser.js +1 -47
- package/dist/docx/ensureParaIds.d.ts +7 -1
- package/dist/docx/ensureParaIds.js +173 -215
- package/dist/docx/foldedListNumberFields.d.ts +85 -0
- package/dist/docx/foldedListNumberFields.js +267 -0
- package/dist/docx/footnoteParser.d.ts +1 -11
- package/dist/docx/footnoteParser.js +1 -16
- package/dist/docx/headerFooterParser.d.ts +1 -9
- package/dist/docx/headerFooterParser.js +1 -12
- package/dist/docx/headerFooterVerbatim.d.ts +39 -1
- package/dist/docx/headerFooterVerbatim.js +173 -2
- package/dist/docx/imageParser.d.ts +13 -1
- package/dist/docx/imageParser.js +20 -11
- package/dist/docx/metadataPrivacy.js +6 -1
- package/dist/docx/noteIds.d.ts +5 -0
- package/dist/docx/noteIds.js +6 -2
- package/dist/docx/noteReferenceMark.d.ts +3 -1
- package/dist/docx/noteReferenceMark.js +15 -14
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +17 -3
- package/dist/docx/packageParts.d.ts +3 -1
- package/dist/docx/packageParts.js +74 -19
- package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
- package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
- package/dist/docx/paragraphParser.js +101 -53
- package/dist/docx/paragraphPropertySource.d.ts +15 -1
- package/dist/docx/paragraphPropertySource.js +77 -22
- package/dist/docx/parser.js +3 -1
- package/dist/docx/removeHeaderFooterParts.js +17 -6
- package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
- package/dist/docx/reviewHistoryNamespace.js +4 -0
- package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
- package/dist/docx/reviewResolutionProvenance.js +153 -0
- package/dist/docx/rezip.d.ts +54 -6
- package/dist/docx/rezip.js +835 -257
- package/dist/docx/runConsolidator.d.ts +2 -58
- package/dist/docx/runConsolidator.js +3 -175
- package/dist/docx/runParser.d.ts +2 -1
- package/dist/docx/runParser.js +156 -116
- package/dist/docx/saveDiagnostics.d.ts +22 -0
- package/dist/docx/saveDiagnostics.js +0 -0
- package/dist/docx/selectiveSave.d.ts +3 -1
- package/dist/docx/selectiveSave.js +36 -4
- package/dist/docx/selectiveXmlPatch.d.ts +16 -8
- package/dist/docx/selectiveXmlPatch.js +81 -7
- package/dist/docx/serializer/documentSerializer.d.ts +9 -2
- package/dist/docx/serializer/documentSerializer.js +29 -10
- package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
- package/dist/docx/serializer/headerFooterSerializer.js +19 -11
- package/dist/docx/serializer/noteSerializer.d.ts +2 -2
- package/dist/docx/serializer/noteSerializer.js +24 -8
- package/dist/docx/serializer/paragraphSerializer.js +41 -4
- package/dist/docx/serializer/partNamespaces.js +2 -1
- package/dist/docx/serializer/runSerializer.js +3 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
- package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
- package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
- package/dist/docx/settingsParser.d.ts +1 -2
- package/dist/docx/settingsParser.js +5 -6
- package/dist/docx/storyBlockReplay.d.ts +31 -0
- package/dist/docx/storyBlockReplay.js +471 -0
- package/dist/docx/storyPlainText.d.ts +20 -0
- package/dist/docx/storyPlainText.js +23 -0
- package/dist/docx/streamingXmlParser.d.ts +22 -1
- package/dist/docx/streamingXmlParser.js +49 -3
- package/dist/docx/structuralXmlPatch.js +18 -23
- package/dist/docx/styleParser.d.ts +5 -1
- package/dist/docx/styleParser.js +1 -1
- package/dist/docx/tableParser.d.ts +1 -8
- package/dist/docx/tableParser.js +1 -28
- package/dist/docx/textBoxParser.d.ts +1 -5
- package/dist/docx/textBoxParser.js +1 -20
- package/dist/docx/unzip.js +6 -1
- package/dist/docx/verbatimCapture.d.ts +3 -1
- package/dist/docx/verbatimCapture.js +49 -6
- package/dist/i18n/messages/ar.gen.d.ts +5 -0
- package/dist/i18n/messages/ar.gen.js +656 -0
- package/dist/i18n/messages/cs.gen.d.ts +5 -0
- package/dist/i18n/messages/cs.gen.js +656 -0
- package/dist/i18n/messages/de.gen.d.ts +5 -0
- package/dist/i18n/messages/de.gen.js +656 -0
- package/dist/i18n/messages/en.gen.d.ts +5 -0
- package/dist/i18n/messages/en.gen.js +656 -0
- package/dist/i18n/messages/es.gen.d.ts +5 -0
- package/dist/i18n/messages/es.gen.js +656 -0
- package/dist/i18n/messages/et.gen.d.ts +5 -0
- package/dist/i18n/messages/et.gen.js +656 -0
- package/dist/i18n/messages/fr.gen.d.ts +5 -0
- package/dist/i18n/messages/fr.gen.js +656 -0
- package/dist/i18n/messages/he.gen.d.ts +5 -0
- package/dist/i18n/messages/he.gen.js +656 -0
- package/dist/i18n/messages/hi.gen.d.ts +5 -0
- package/dist/i18n/messages/hi.gen.js +656 -0
- package/dist/i18n/messages/hu.gen.d.ts +5 -0
- package/dist/i18n/messages/hu.gen.js +656 -0
- package/dist/i18n/messages/locales.d.ts +5 -0
- package/dist/i18n/messages/locales.js +22 -0
- package/dist/i18n/messages/lt.gen.d.ts +5 -0
- package/dist/i18n/messages/lt.gen.js +656 -0
- package/dist/i18n/messages/lv.gen.d.ts +5 -0
- package/dist/i18n/messages/lv.gen.js +656 -0
- package/dist/i18n/messages/pl.gen.d.ts +5 -0
- package/dist/i18n/messages/pl.gen.js +656 -0
- package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
- package/dist/i18n/messages/pt-BR.gen.js +656 -0
- package/dist/i18n/messages/sk.gen.d.ts +5 -0
- package/dist/i18n/messages/sk.gen.js +656 -0
- package/dist/i18n/messages/tr.gen.d.ts +5 -0
- package/dist/i18n/messages/tr.gen.js +656 -0
- package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
- package/dist/i18n/messages/zh-CN.gen.js +656 -0
- package/dist/i18n/messages.d.ts +2 -3
- package/dist/i18n/messages.js +1 -19
- package/dist/internal/acceptedBlockProjection.d.ts +12 -0
- package/dist/internal/acceptedBlockProjection.js +45 -0
- package/dist/internal/noteMarkerAttrs.d.ts +8 -0
- package/dist/internal/noteMarkerAttrs.js +16 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
- package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
- package/dist/internal/prosemirrorAttrBoundary.js +50 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
- package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
- package/dist/layout-bridge/convert/noteReferences.js +1 -6
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
- package/dist/managers/DocumentLoaderManager.d.ts +1 -0
- package/dist/managers/DocumentLoaderManager.js +9 -1
- package/dist/markdown/escape.d.ts +3 -1
- package/dist/markdown/escape.js +9 -2
- package/dist/markdown/renderBlock.js +20 -121
- package/dist/markdown/renderParagraph.js +13 -15
- package/dist/markdown/renderRuns.d.ts +7 -1
- package/dist/markdown/renderRuns.js +35 -15
- package/dist/markdown/renderTable.js +2 -0
- package/dist/markdown/types.d.ts +3 -9
- package/dist/prosemirror/attrs/index.d.ts +2 -12
- package/dist/prosemirror/attrs/index.js +22 -48
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/canonicalCommands.d.ts +121 -0
- package/dist/prosemirror/canonicalCommands.js +35 -0
- package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
- package/dist/prosemirror/canonicalSelectionRange.js +15 -0
- package/dist/prosemirror/clipboardIntent.d.ts +7 -0
- package/dist/prosemirror/clipboardIntent.js +9 -0
- package/dist/prosemirror/commands/pageBreak.js +15 -1
- package/dist/prosemirror/commands/pastePlainText.js +2 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
- package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
- package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +11 -4
- package/dist/prosemirror/conversion/toProseDoc.js +55 -14
- package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
- package/dist/prosemirror/executeEditorCommand.js +26 -0
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +88 -48
- package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +56 -28
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.js +72 -8
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
- package/dist/prosemirror/insertOperations.js +2 -1
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +16 -7
- package/dist/prosemirror/listNumbering.js +26 -5
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/markupViewNotes.js +3 -2
- package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/noteReferenceReview.js +5 -3
- package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
- package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
- package/dist/prosemirror/plugins/suggestionMode.js +57 -7
- package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
- package/dist/prosemirror/runFormattingFromMarks.js +15 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
- package/dist/prosemirror/schema/marks.d.ts +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +10 -2
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/textInput.js +5 -1
- package/dist/prosemirror/trackedRevisionPath.js +2 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
- package/dist/prosemirror/validation.js +4 -0
- package/dist/types/canonicalCapabilities.d.ts +215 -0
- package/dist/types/canonicalCapabilities.js +212 -0
- package/dist/types/canonicalSave.d.ts +11 -0
- package/dist/types/canonicalSave.js +0 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/types/docxSerialization.d.ts +14 -0
- package/dist/types/docxSerialization.js +7 -0
- package/dist/utils/createDocument.js +2 -2
- package/package.json +16 -4
|
@@ -7,7 +7,7 @@ import { parseNumbering } from "./numberingParser.js";
|
|
|
7
7
|
import { normalizeImportedNumericIds } from "./numericIdNormalization.js";
|
|
8
8
|
import { isUnsafePackagePath } from "./packageParts.js";
|
|
9
9
|
import { RELATIONSHIP_TYPES } from "./relsParser.js";
|
|
10
|
-
import { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_PART_LOWER, addCommentsExtendedOverride, addCommentsExtendedRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, updateCoreProperties, withoutAttachedTemplate } from "./rezip.js";
|
|
10
|
+
import { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_PART_LOWER, addCommentsExtendedOverride, addCommentsExtendedRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedHyperlinkBindings, hasUnmaterializedInlineResources, planAddedStyles, publishCanonicalImageResources, updateCoreProperties, withoutAttachedTemplate } from "./rezip.js";
|
|
11
11
|
import "./selectiveSaveFlags.js";
|
|
12
12
|
import { buildPatchedDocumentXml, buildPatchedNoteXml, collectAddedNumberingDefs, collectParaIds, patchNumberingDefinitions } from "./selectiveXmlPatch.js";
|
|
13
13
|
import { planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
|
|
@@ -17,6 +17,7 @@ import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
|
|
|
17
17
|
import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
|
|
18
18
|
import { buildStructuralDocumentPatch } from "./structuralXmlPatch.js";
|
|
19
19
|
import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
|
|
20
|
+
import { writeZipPart } from "@stll/docx-core/zip";
|
|
20
21
|
//#region src/docx/selectiveSave.ts
|
|
21
22
|
/**
|
|
22
23
|
* Splice edited footnote/endnote paragraphs into their parts and return the
|
|
@@ -203,6 +204,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
203
204
|
const commentPlan = planCommentParts(comments);
|
|
204
205
|
try {
|
|
205
206
|
const zip = await (await import("jszip")).default.loadAsync(originalBuffer);
|
|
207
|
+
if (await hasUnmaterializedHyperlinkBindings(doc, zip)) return null;
|
|
206
208
|
for (const [path, file] of Object.entries(zip.files)) if (!file.dir && isUnsafePackagePath(path)) return null;
|
|
207
209
|
if (originalBuffer !== doc.originalBuffer) {
|
|
208
210
|
const sourceParts = /* @__PURE__ */ new Map();
|
|
@@ -211,14 +213,27 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
211
213
|
if (file.dir || !lowerPath.startsWith("word/") || !lowerPath.endsWith(".xml")) continue;
|
|
212
214
|
sourceParts.set(path, await file.async("text"));
|
|
213
215
|
}
|
|
214
|
-
for (const [path, xml] of normalizeImportedNumericIds(sourceParts)) if (xml !== sourceParts.get(path))
|
|
216
|
+
for (const [path, xml] of normalizeImportedNumericIds(sourceParts)) if (xml !== sourceParts.get(path)) writeZipPart({
|
|
217
|
+
zip,
|
|
218
|
+
path,
|
|
219
|
+
data: xml
|
|
220
|
+
});
|
|
215
221
|
}
|
|
222
|
+
if (options.bodyAuthority === "canonical") await publishCanonicalImageResources({
|
|
223
|
+
document: doc,
|
|
224
|
+
zip,
|
|
225
|
+
compressionLevel: 6
|
|
226
|
+
});
|
|
216
227
|
const updates = /* @__PURE__ */ new Map();
|
|
217
228
|
if (changedParaIds.size > 0 || structuralChange) {
|
|
218
229
|
const docXmlFile = zip.file("word/document.xml");
|
|
219
230
|
if (!docXmlFile) return null;
|
|
220
231
|
const originalDocXml = await docXmlFile.async("text");
|
|
221
|
-
const serializedDocXml = serializeDocument(doc, readRootNamespaceBindings(originalDocXml)
|
|
232
|
+
const serializedDocXml = serializeDocument(doc, readRootNamespaceBindings(originalDocXml), {
|
|
233
|
+
xml: originalDocXml,
|
|
234
|
+
bodyAuthority: options.bodyAuthority,
|
|
235
|
+
onDiagnostic: options.onDiagnostic
|
|
236
|
+
});
|
|
222
237
|
const bodyParaIds = collectParaIds(serializedDocXml);
|
|
223
238
|
const originalBodyParaIds = structuralChange ? collectParaIds(originalDocXml) : void 0;
|
|
224
239
|
if (structuralChange && doc.package.numbering) {
|
|
@@ -268,7 +283,24 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
|
|
|
268
283
|
}
|
|
269
284
|
if (!await patchCommentsExtended(zip, commentPlan, updates)) return null;
|
|
270
285
|
if (!await patchNumberingPart(zip, doc, updates)) return null;
|
|
271
|
-
|
|
286
|
+
const styles = await planAddedStyles(doc, zip);
|
|
287
|
+
switch (styles.type) {
|
|
288
|
+
case "unchanged": break;
|
|
289
|
+
case "patch":
|
|
290
|
+
updates.set(styles.path, styles.xml);
|
|
291
|
+
break;
|
|
292
|
+
case "materialize":
|
|
293
|
+
options.onDiagnostic?.({
|
|
294
|
+
type: "selectiveSaveRefused",
|
|
295
|
+
part: "word/styles.xml"
|
|
296
|
+
});
|
|
297
|
+
return null;
|
|
298
|
+
default: return styles;
|
|
299
|
+
}
|
|
300
|
+
for (const [path, xml] of await collectHeaderFooterUpdates(doc, {
|
|
301
|
+
sourceZip: zip,
|
|
302
|
+
onDiagnostic: options.onDiagnostic
|
|
303
|
+
})) updates.set(path, xml);
|
|
272
304
|
await queueSettingsUpdates(zip, updates);
|
|
273
305
|
const corePropsFile = zip.file("docProps/core.xml");
|
|
274
306
|
if (corePropsFile) {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
+
import { XmlElement } from "./xmlParser.js";
|
|
1
2
|
//#region src/docx/selectiveXmlPatch.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Selective XML Patch Module
|
|
4
|
-
*
|
|
5
|
-
* Patches only changed paragraphs in document.xml, preserving
|
|
6
|
-
* unchanged content byte-for-byte. Uses string offset tracking
|
|
7
|
-
* with proper tag depth counting (not regex) to handle nested elements.
|
|
8
|
-
*/
|
|
9
3
|
/**
|
|
10
4
|
* Whether `char` ends an element's tag name in XML — a whitespace separator
|
|
11
5
|
* (space, tab, CR, or LF, all valid before attributes per XML 1.0 §3.1), the
|
|
@@ -170,6 +164,20 @@ type XmlSplice = {
|
|
|
170
164
|
* end, so its original offsets cannot be applied to the resulting string.
|
|
171
165
|
*/
|
|
172
166
|
declare const spliceXml: (xml: string, splices: readonly XmlSplice[]) => string | null;
|
|
167
|
+
/**
|
|
168
|
+
* A paragraph splice cannot change the owning part's root declarations. Refuse
|
|
169
|
+
* fragments that inherit a prefix from the serialized root when the source
|
|
170
|
+
* root does not bind that prefix to the same URI; the caller can then use the
|
|
171
|
+
* full serializer, which owns the root namespace set.
|
|
172
|
+
*/
|
|
173
|
+
type SpliceNamespaceOptions = {
|
|
174
|
+
originalXml: string;
|
|
175
|
+
serializedXml: string;
|
|
176
|
+
fragments: readonly string[];
|
|
177
|
+
originalRoot?: XmlElement;
|
|
178
|
+
serializedRoot?: XmlElement;
|
|
179
|
+
};
|
|
180
|
+
declare const spliceSupportsRootNamespaces: ({ originalXml, serializedXml, fragments, originalRoot, serializedRoot }: SpliceNamespaceOptions) => boolean;
|
|
173
181
|
type NoteElementName = "footnote" | "endnote";
|
|
174
182
|
declare const collectChangedNoteParaIds: (baselineXml: string, currentXml: string) => Set<string>;
|
|
175
183
|
type BuildPatchedNotePartXmlOptions = {
|
|
@@ -265,4 +273,4 @@ declare const patchNumberingDefinitions: ({ originalXml, baselineXml, currentXml
|
|
|
265
273
|
*/
|
|
266
274
|
declare function appendNumberingDefs(xml: string, currentXml: string, added: ChangedNumberingDefs): string | null;
|
|
267
275
|
//#endregion
|
|
268
|
-
export { ChangedNumberingDefs, NotePartPatch, NotePartPatchRefusal, ParagraphContainer, ParagraphOffsets, PatchValidationResult, ScannedParagraph, XmlSplice, appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, scanParagraphs, spliceXml, validatePatchSafety };
|
|
276
|
+
export { ChangedNumberingDefs, NotePartPatch, NotePartPatchRefusal, ParagraphContainer, ParagraphOffsets, PatchValidationResult, ScannedParagraph, XmlSplice, appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, scanParagraphs, spliceSupportsRootNamespaces, spliceXml, validatePatchSafety };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { patchBreaksCommentRangeBalance } from "./commentRangeIntegrity.js";
|
|
2
2
|
import { resolveParagraphIdentities } from "./paraIdAttribute.js";
|
|
3
|
+
import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
|
|
3
4
|
import { captureVerbatimXml } from "./verbatimCapture.js";
|
|
4
5
|
import { PARAGRAPH_SCAN_NAMES, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, resolveSelectiveScanPrefixes, splicesAsCanonical } from "./wordprocessingPrefixes.js";
|
|
5
|
-
import {
|
|
6
|
+
import { resolveNamespaceUri } from "./xmlNamespaceContext.js";
|
|
7
|
+
import { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findAttributeByNamespaceUri, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
|
|
6
8
|
//#region src/docx/selectiveXmlPatch.ts
|
|
7
9
|
/**
|
|
8
10
|
* Selective XML Patch Module
|
|
@@ -33,7 +35,7 @@ function isXmlNameBoundary(char) {
|
|
|
33
35
|
*/
|
|
34
36
|
function findParagraphOffsets(xml, paraId) {
|
|
35
37
|
const escaped = escapeRegExp(paraId);
|
|
36
|
-
const pattern = new RegExp(`<w:p[\\s][^>]*w14:paraId
|
|
38
|
+
const pattern = new RegExp(`<w:p[\\s][^>]*w14:paraId\\s*=\\s*(["'])${escaped}\\1`, "gu");
|
|
37
39
|
const matches = [];
|
|
38
40
|
let match;
|
|
39
41
|
while ((match = pattern.exec(xml)) !== null) matches.push(match.index);
|
|
@@ -189,7 +191,7 @@ function scanParagraphs(xml) {
|
|
|
189
191
|
const tagEnd = xml.indexOf(">", tagStart);
|
|
190
192
|
if (tagEnd === -1) break;
|
|
191
193
|
const openTag = xml.slice(tagStart, tagEnd + 1);
|
|
192
|
-
const paraId = /\bw14:paraId
|
|
194
|
+
const paraId = /\bw14:paraId\s*=\s*(?<quote>["'])(?<id>[^"']+)\k<quote>/u.exec(openTag)?.groups?.["id"];
|
|
193
195
|
const container = {
|
|
194
196
|
inFallback: depth.fallback > 0,
|
|
195
197
|
inAlternateContent: depth.alternateContent > 0,
|
|
@@ -234,7 +236,7 @@ function countParagraphElements(xml) {
|
|
|
234
236
|
*/
|
|
235
237
|
function collectParaIds(xml) {
|
|
236
238
|
const ids = /* @__PURE__ */ new Map();
|
|
237
|
-
const pattern = /w14:paraId
|
|
239
|
+
const pattern = /w14:paraId\s*=\s*(?<quote>["'])(?<id>[^"']+)\k<quote>/gu;
|
|
238
240
|
let match;
|
|
239
241
|
while ((match = pattern.exec(xml)) !== null) {
|
|
240
242
|
const id = match.groups["id"];
|
|
@@ -243,7 +245,7 @@ function collectParaIds(xml) {
|
|
|
243
245
|
return ids;
|
|
244
246
|
}
|
|
245
247
|
/** Paragraph ids folio writes and this module may have to remove again. */
|
|
246
|
-
const MINTED_PARA_ID_ATTRIBUTE = /\sw14:(
|
|
248
|
+
const MINTED_PARA_ID_ATTRIBUTE = /\sw14:(paraId|textId)="[^"]*"/gu;
|
|
247
249
|
/**
|
|
248
250
|
* The replacement for a paragraph whose source open tag carries no paraId.
|
|
249
251
|
*
|
|
@@ -260,7 +262,9 @@ const MINTED_PARA_ID_ATTRIBUTE = /\sw14:(?:para|text)Id="[^"]*"/gu;
|
|
|
260
262
|
const withoutMintedIds = (paragraphXml, sourceOpenTag) => {
|
|
261
263
|
const tagEnd = paragraphXml.indexOf(">");
|
|
262
264
|
if (tagEnd === -1) return paragraphXml;
|
|
263
|
-
return paragraphXml.slice(0, tagEnd + 1).replace(MINTED_PARA_ID_ATTRIBUTE, (attribute) =>
|
|
265
|
+
return paragraphXml.slice(0, tagEnd + 1).replace(MINTED_PARA_ID_ATTRIBUTE, (attribute, name) => {
|
|
266
|
+
return new RegExp(`\\bw14:${name}\\s*=\\s*(["'])[^"']*\\1`, "u").test(sourceOpenTag) ? attribute : "";
|
|
267
|
+
}) + paragraphXml.slice(tagEnd + 1);
|
|
264
268
|
};
|
|
265
269
|
const storyOf = ({ textBoxDepth }) => textBoxDepth > 0 ? "text-box" : "main";
|
|
266
270
|
const sameContainer = (a, b) => a.inAlternateContent === b.inAlternateContent && a.textBoxDepth === b.textBoxDepth && a.tableDepth === b.tableDepth;
|
|
@@ -425,6 +429,14 @@ const routeChangedParagraphs = (originalXml, serializedXml, changedIds) => {
|
|
|
425
429
|
newXml: authored ? newXml : withoutMintedIds(newXml, sourceXml)
|
|
426
430
|
});
|
|
427
431
|
}
|
|
432
|
+
if (!spliceSupportsRootNamespaces({
|
|
433
|
+
originalXml,
|
|
434
|
+
serializedXml,
|
|
435
|
+
fragments: splices.map(({ newXml }) => newXml)
|
|
436
|
+
})) return {
|
|
437
|
+
type: "refused",
|
|
438
|
+
reason: "root-namespace-requirements-changed"
|
|
439
|
+
};
|
|
428
440
|
const candidate = spliceXml(originalXml, splices);
|
|
429
441
|
if (candidate === null) return {
|
|
430
442
|
type: "refused",
|
|
@@ -514,6 +526,68 @@ const spliceXml = (xml, splices) => {
|
|
|
514
526
|
}
|
|
515
527
|
return patchBreaksCommentRangeBalance(xml, result) ? null : result;
|
|
516
528
|
};
|
|
529
|
+
const spliceSupportsRootNamespaces = ({ originalXml, serializedXml, fragments, originalRoot, serializedRoot }) => {
|
|
530
|
+
const original = originalRoot ?? parseXmlDocument(originalXml);
|
|
531
|
+
const serialized = serializedRoot ?? parseXmlDocument(serializedXml);
|
|
532
|
+
if (!original || !serialized) return false;
|
|
533
|
+
const originalBindings = readRootNamespaceBindings(originalXml);
|
|
534
|
+
const serializedBindings = readRootNamespaceBindings(serializedXml);
|
|
535
|
+
const mcNamespace = /* @__PURE__ */ new Set([NAMESPACES.mc]);
|
|
536
|
+
const ignorableUris = (root) => {
|
|
537
|
+
const uris = /* @__PURE__ */ new Set();
|
|
538
|
+
for (const prefix of (getAttributeByNamespaceUri(root, mcNamespace, "Ignorable") ?? "").split(/\s+/u).filter(Boolean)) {
|
|
539
|
+
const uri = resolveNamespaceUri(root.namespaceScope, prefix);
|
|
540
|
+
if (uri) uris.add(uri);
|
|
541
|
+
}
|
|
542
|
+
return uris;
|
|
543
|
+
};
|
|
544
|
+
const originalNamespaceUses = /* @__PURE__ */ new Set();
|
|
545
|
+
const collectSourceNamespaces = (element) => {
|
|
546
|
+
const uri = getNamespaceUri(element);
|
|
547
|
+
if (uri) originalNamespaceUses.add(uri);
|
|
548
|
+
for (const attribute of Object.keys(element.attributes ?? {})) {
|
|
549
|
+
if (attribute === "xmlns" || attribute.startsWith("xmlns:")) continue;
|
|
550
|
+
const prefix = getNamespacePrefix(attribute);
|
|
551
|
+
const attributeUri = prefix ? resolveNamespaceUri(element.namespaceScope, prefix) : void 0;
|
|
552
|
+
if (attributeUri) originalNamespaceUses.add(attributeUri);
|
|
553
|
+
}
|
|
554
|
+
for (const child of element.elements ?? []) collectSourceNamespaces(child);
|
|
555
|
+
};
|
|
556
|
+
collectSourceNamespaces(original);
|
|
557
|
+
const originalIgnorable = ignorableUris(original);
|
|
558
|
+
const serializedIgnorable = ignorableUris(serialized);
|
|
559
|
+
const inheritedPrefixes = /* @__PURE__ */ new Set();
|
|
560
|
+
const visit = (element, ancestorIgnorableUris) => {
|
|
561
|
+
const localIgnorableUris = new Set(ancestorIgnorableUris);
|
|
562
|
+
for (const uri of ignorableUris(element)) localIgnorableUris.add(uri);
|
|
563
|
+
const requirePrefix = (prefix) => {
|
|
564
|
+
const localUri = resolveNamespaceUri(element.namespaceScope, prefix);
|
|
565
|
+
if (!localUri) inheritedPrefixes.add(prefix);
|
|
566
|
+
const uri = localUri ?? serializedBindings.get(prefix);
|
|
567
|
+
if (!uri) return false;
|
|
568
|
+
return !serializedIgnorable.has(uri) || originalNamespaceUses.has(uri) || originalIgnorable.has(uri) || localIgnorableUris.has(uri);
|
|
569
|
+
};
|
|
570
|
+
const namePrefix = element.name?.split(":").at(0);
|
|
571
|
+
if (namePrefix && namePrefix !== element.name && !requirePrefix(namePrefix)) return false;
|
|
572
|
+
for (const attribute of Object.keys(element.attributes ?? {})) {
|
|
573
|
+
if (attribute === "xmlns" || attribute.startsWith("xmlns:")) continue;
|
|
574
|
+
const prefix = attribute.split(":").at(0);
|
|
575
|
+
if (prefix && prefix !== attribute && !requirePrefix(prefix)) return false;
|
|
576
|
+
}
|
|
577
|
+
for (const child of element.elements ?? []) if (!visit(child, localIgnorableUris)) return false;
|
|
578
|
+
return true;
|
|
579
|
+
};
|
|
580
|
+
for (const fragment of fragments) {
|
|
581
|
+
const parsed = parseXmlDocument(fragment);
|
|
582
|
+
if (!parsed) return false;
|
|
583
|
+
if (!visit(parsed, /* @__PURE__ */ new Set())) return false;
|
|
584
|
+
}
|
|
585
|
+
for (const prefix of inheritedPrefixes) {
|
|
586
|
+
const serializedUri = serializedBindings.get(prefix);
|
|
587
|
+
if (serializedUri === void 0 || originalBindings.get(prefix) !== serializedUri) return false;
|
|
588
|
+
}
|
|
589
|
+
return true;
|
|
590
|
+
};
|
|
517
591
|
/**
|
|
518
592
|
* Depth-count the end of the element that opens at `start` (an `<openLiteral…>`
|
|
519
593
|
* offset), returning its full range. Nested same-name opens increment depth;
|
|
@@ -1269,4 +1343,4 @@ function escapeRegExp(str) {
|
|
|
1269
1343
|
return str.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
1270
1344
|
}
|
|
1271
1345
|
//#endregion
|
|
1272
|
-
export { appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, scanParagraphs, spliceXml, validatePatchSafety };
|
|
1346
|
+
export { appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, patchNumberingDefinitions, scanParagraphs, spliceSupportsRootNamespaces, spliceXml, validatePatchSafety };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { SaveDiagnosticOptions } from "../saveDiagnostics.js";
|
|
2
3
|
//#region src/docx/serializer/documentSerializer.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Serialize a DocumentBody to document.xml body content
|
|
@@ -15,7 +16,13 @@ declare function serializeDocumentBody(body: document_d_exports.DocumentBody): s
|
|
|
15
16
|
* prefix only the source document bound keeps its URI
|
|
16
17
|
* @returns Complete XML string for document.xml
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
+
type DocumentBodyAuthorityOptions = {
|
|
20
|
+
/** Canonical saves preserve trusted source blocks; default saves serialize the model. */
|
|
21
|
+
bodyAuthority?: "canonical" | "model" | undefined;
|
|
22
|
+
};
|
|
23
|
+
declare function serializeDocument(doc: document_d_exports.Document, sourceBindings?: ReadonlyMap<string, string>, source?: SaveDiagnosticOptions & DocumentBodyAuthorityOptions & {
|
|
24
|
+
xml?: string | undefined;
|
|
25
|
+
}): string;
|
|
19
26
|
/**
|
|
20
27
|
* Serialize just the document body (useful for partial updates)
|
|
21
28
|
*
|
|
@@ -59,4 +66,4 @@ declare function createSimpleDocument(paragraphs: {
|
|
|
59
66
|
styleId?: string;
|
|
60
67
|
}[]): document_d_exports.Document;
|
|
61
68
|
//#endregion
|
|
62
|
-
export { createEmptyDocument, createSimpleDocument, getDocumentContentCount, getDocumentParagraphCount, getDocumentTableCount, hasDocumentContent, hasDocumentSections, hasSectionProperties, serializeDocument, serializeDocumentBody, serializeDocumentBodyElement };
|
|
69
|
+
export { DocumentBodyAuthorityOptions, createEmptyDocument, createSimpleDocument, getDocumentContentCount, getDocumentParagraphCount, getDocumentTableCount, hasDocumentContent, hasDocumentSections, hasSectionProperties, serializeDocument, serializeDocumentBody, serializeDocumentBodyElement };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getDocumentSourceBaseline } from "../headerFooterVerbatim.js";
|
|
2
|
+
import { buildDocumentBlockReplay, canReplayDocumentSource } from "../storyBlockReplay.js";
|
|
1
3
|
import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
|
|
2
4
|
import { serializeBlockSdt } from "./blockSdtSerializer.js";
|
|
3
5
|
import { serializeBookmarkMarker } from "./markupRangeAttributes.js";
|
|
@@ -84,24 +86,41 @@ function serializeDocumentBody(body) {
|
|
|
84
86
|
if (body.finalSectionProperties) parts.push(serializeSectionProperties(body.finalSectionProperties));
|
|
85
87
|
return parts.join("");
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*
|
|
90
|
-
* @param doc - The document to serialize
|
|
91
|
-
* @param sourceBindings - Root `xmlns:*` of the part being replaced, so a
|
|
92
|
-
* prefix only the source document bound keeps its URI
|
|
93
|
-
* @returns Complete XML string for document.xml
|
|
94
|
-
*/
|
|
95
|
-
function serializeDocument(doc, sourceBindings) {
|
|
89
|
+
const replayableDocumentSources = /* @__PURE__ */ new WeakMap();
|
|
90
|
+
function serializeDocument(doc, sourceBindings, source) {
|
|
96
91
|
resetAutoIdCounter();
|
|
92
|
+
const baseline = source?.bodyAuthority === "canonical" ? getDocumentSourceBaseline(doc) : { type: "missing" };
|
|
93
|
+
const sourceMatches = baseline.type === "captured" && (source?.xml === void 0 || source.xml === baseline.xml);
|
|
94
|
+
if (baseline.type === "captured" && sourceMatches && JSON.stringify(doc.package.document) === baseline.fingerprint) {
|
|
95
|
+
if (replayableDocumentSources.get(baseline.body) === baseline.xml) return baseline.xml;
|
|
96
|
+
if (canReplayDocumentSource(baseline.xml, baseline.body)) {
|
|
97
|
+
replayableDocumentSources.set(baseline.body, baseline.xml);
|
|
98
|
+
return baseline.xml;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
97
101
|
const body = serializeDocumentBackground(doc.package.document.background) + `<w:body>${serializeDocumentBody(doc.package.document)}</w:body>`;
|
|
98
|
-
|
|
102
|
+
const serializedXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + serializePartElement({
|
|
99
103
|
partPath: "word/document.xml",
|
|
100
104
|
rootName: "w:document",
|
|
101
105
|
baselinePrefixes: DOCUMENT_BASELINE_PREFIXES,
|
|
102
106
|
sourceBindings,
|
|
103
107
|
body
|
|
104
108
|
});
|
|
109
|
+
if (baseline.type === "missing") return serializedXml;
|
|
110
|
+
if (baseline.type === "captured" && sourceMatches) {
|
|
111
|
+
const replay = buildDocumentBlockReplay({
|
|
112
|
+
sourceXml: baseline.xml,
|
|
113
|
+
baseline: baseline.body,
|
|
114
|
+
current: doc.package.document,
|
|
115
|
+
serializedXml
|
|
116
|
+
});
|
|
117
|
+
if (replay !== null) return replay;
|
|
118
|
+
}
|
|
119
|
+
source?.onDiagnostic?.({
|
|
120
|
+
type: "sourceReplayMismatch",
|
|
121
|
+
part: "word/document.xml"
|
|
122
|
+
});
|
|
123
|
+
return serializedXml;
|
|
105
124
|
}
|
|
106
125
|
/**
|
|
107
126
|
* Serialize just the document body (useful for partial updates)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { SaveDiagnosticOptions } from "../saveDiagnostics.js";
|
|
2
3
|
import { SourcePart } from "./partNamespaces.js";
|
|
3
4
|
//#region src/docx/serializer/headerFooterSerializer.d.ts
|
|
4
5
|
/**
|
|
@@ -9,6 +10,9 @@ import { SourcePart } from "./partNamespaces.js";
|
|
|
9
10
|
* document bound keeps its URI
|
|
10
11
|
* @returns Complete XML string for header*.xml or footer*.xml
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
type HeaderFooterSerializeOptions = SourcePart & SaveDiagnosticOptions & {
|
|
14
|
+
originalBuffer?: ArrayBuffer | undefined;
|
|
15
|
+
};
|
|
16
|
+
declare function serializeHeaderFooter(hf: document_d_exports.HeaderFooter, source?: HeaderFooterSerializeOptions): string;
|
|
13
17
|
//#endregion
|
|
14
18
|
export { serializeHeaderFooter };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
|
|
1
|
+
import { canReplayHeaderFooterBlocks, canReplayHeaderFooterVerbatim, getHeaderFooterSourceBaseline, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
|
|
2
2
|
import { isEmptyParagraph } from "../paragraphParser.js";
|
|
3
|
+
import { buildStoryBlockReplay } from "../storyBlockReplay.js";
|
|
3
4
|
import { captureVerbatimXml } from "../verbatimCapture.js";
|
|
4
5
|
import { getLocalName, parseXmlDocument } from "../xmlParser.js";
|
|
5
6
|
import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
|
|
@@ -49,17 +50,14 @@ function serializeBlock(block) {
|
|
|
49
50
|
default: return block;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Serialize a HeaderFooter object to valid OOXML XML
|
|
54
|
-
*
|
|
55
|
-
* @param hf - HeaderFooter object to serialize
|
|
56
|
-
* @param source - The part being replaced, so a prefix only the source
|
|
57
|
-
* document bound keeps its URI
|
|
58
|
-
* @returns Complete XML string for header*.xml or footer*.xml
|
|
59
|
-
*/
|
|
60
53
|
function serializeHeaderFooter(hf, source) {
|
|
54
|
+
const baseline = getHeaderFooterSourceBaseline(hf, source?.originalBuffer);
|
|
55
|
+
if (baseline.type === "mismatch") source?.onDiagnostic?.({
|
|
56
|
+
type: "sourceReplayMismatch",
|
|
57
|
+
part: source.path
|
|
58
|
+
});
|
|
61
59
|
const verbatim = getHeaderFooterVerbatimXml(hf);
|
|
62
|
-
if (verbatim && canReplayHeaderFooterVerbatim(hf)) return verbatim;
|
|
60
|
+
if (baseline.type === "captured" && verbatim && canReplayHeaderFooterVerbatim(hf)) return verbatim;
|
|
63
61
|
const rootTag = hf.type === "header" ? "w:hdr" : "w:ftr";
|
|
64
62
|
const watermarkXml = serializeWatermarkParagraph(hf);
|
|
65
63
|
const watermarkInsertIndex = hf.watermarkBlockIndex !== void 0 ? Math.max(0, Math.min(hf.watermarkBlockIndex, hf.content.length)) : 0;
|
|
@@ -75,13 +73,23 @@ function serializeHeaderFooter(hf, source) {
|
|
|
75
73
|
});
|
|
76
74
|
contentXml = blocksXml.slice(0, watermarkInsertIndex).join("") + (mergesIntoRetainedHost ? "" : watermarkXml) + blocksXml.slice(watermarkInsertIndex).join("");
|
|
77
75
|
} else contentXml = blocksXml.join("");
|
|
78
|
-
|
|
76
|
+
const serializedXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + serializePartElement({
|
|
79
77
|
partPath: source?.path ?? `word/${hf.type}.xml`,
|
|
80
78
|
rootName: rootTag,
|
|
81
79
|
baselinePrefixes: HEADER_FOOTER_BASELINE_PREFIXES,
|
|
82
80
|
sourceBindings: source?.bindings,
|
|
83
81
|
body: contentXml
|
|
84
82
|
});
|
|
83
|
+
if (baseline.type === "captured" && verbatim && canReplayHeaderFooterBlocks(hf, baseline.content)) {
|
|
84
|
+
const replayed = buildStoryBlockReplay({
|
|
85
|
+
sourceXml: verbatim,
|
|
86
|
+
baselineContent: baseline.content,
|
|
87
|
+
currentContent: hf.content,
|
|
88
|
+
serializedXml
|
|
89
|
+
});
|
|
90
|
+
if (replayed !== null) return replayed;
|
|
91
|
+
}
|
|
92
|
+
return serializedXml;
|
|
85
93
|
}
|
|
86
94
|
function serializeRawWatermarkIntoHost({ hostXml, rawWatermarkXml }) {
|
|
87
95
|
const rawHost = parseXmlDocument(rawWatermarkXml);
|
|
@@ -16,9 +16,9 @@ declare function serializeFootnotes(footnotes: readonly document_d_exports.Footn
|
|
|
16
16
|
declare function serializeEndnotes(endnotes: readonly document_d_exports.Endnote[]): string;
|
|
17
17
|
/** Serialize a brand-new footnote part, including Word's required separators
|
|
18
18
|
* and the automatic reference mark at the start of every normal note. */
|
|
19
|
-
declare function serializeNewFootnotesPart(footnotes: readonly document_d_exports.Footnote[]): string;
|
|
19
|
+
declare function serializeNewFootnotesPart(footnotes: readonly document_d_exports.Footnote[], document?: document_d_exports.Document): string;
|
|
20
20
|
/** Serialize a brand-new endnote part, including Word's required separators
|
|
21
21
|
* and the automatic reference mark at the start of every normal note. */
|
|
22
|
-
declare function serializeNewEndnotesPart(endnotes: readonly document_d_exports.Endnote[]): string;
|
|
22
|
+
declare function serializeNewEndnotesPart(endnotes: readonly document_d_exports.Endnote[], document?: document_d_exports.Document): string;
|
|
23
23
|
//#endregion
|
|
24
24
|
export { serializeEndnotes, serializeFootnotes, serializeNewEndnotesPart, serializeNewFootnotesPart };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { hasNoteReferenceMark } from "../noteReferenceMark.js";
|
|
1
2
|
import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
|
|
2
3
|
import { serializeBlockSdt } from "./blockSdtSerializer.js";
|
|
3
4
|
import { serializeBookmarkMarker } from "./markupRangeAttributes.js";
|
|
4
5
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
5
6
|
import { serializePartElement } from "./partNamespaces.js";
|
|
6
7
|
import { serializeTable } from "./tableSerializer.js";
|
|
8
|
+
import { noteUsesCustomMark } from "@stll/docx-core/ops";
|
|
7
9
|
//#region src/docx/serializer/noteSerializer.ts
|
|
8
10
|
const NOTE_BASELINE_PREFIXES = [
|
|
9
11
|
"wpc",
|
|
@@ -66,8 +68,9 @@ const NOTE_SEPARATOR_BODY = "<w:p><w:pPr><w:spacing w:after=\"0\" w:line=\"240\"
|
|
|
66
68
|
function serializeRequiredNoteSeparators(elementName) {
|
|
67
69
|
return `<w:${elementName} w:type="separator" w:id="-1">${NOTE_SEPARATOR_BODY}<w:r><w:separator/></w:r></w:p></w:${elementName}><w:${elementName} w:type="continuationSeparator" w:id="0">${NOTE_SEPARATOR_BODY}<w:r><w:continuationSeparator/></w:r></w:p></w:${elementName}>`;
|
|
68
70
|
}
|
|
69
|
-
function insertNoteReferenceMark(xml, elementName) {
|
|
70
|
-
if (
|
|
71
|
+
function insertNoteReferenceMark({ xml, elementName, note, customMark }) {
|
|
72
|
+
if (customMark) return xml;
|
|
73
|
+
if (hasNoteReferenceMark(elementName, note.content)) return xml;
|
|
71
74
|
const paragraphOpen = /<w:p(?=[\s>])[^>]*>/u.exec(xml);
|
|
72
75
|
if (!paragraphOpen) return `${`<w:p><w:r><w:rPr><w:rStyle w:val="${elementName === "footnote" ? "FootnoteReference" : "EndnoteReference"}"/></w:rPr><w:${elementName}Ref/></w:r></w:p>`}${xml}`;
|
|
73
76
|
const paragraphStart = paragraphOpen.index + paragraphOpen[0].length;
|
|
@@ -77,8 +80,13 @@ function insertNoteReferenceMark(xml, elementName) {
|
|
|
77
80
|
const referenceRun = `<w:r><w:rPr><w:rStyle w:val="${elementName === "footnote" ? "FootnoteReference" : "EndnoteReference"}"/></w:rPr><w:${elementName}Ref/></w:r>`;
|
|
78
81
|
return `${xml.slice(0, insertAt)}${referenceRun}${xml.slice(insertAt)}`;
|
|
79
82
|
}
|
|
80
|
-
function serializeNewNotePart(elementName, notes) {
|
|
81
|
-
const serializedNotes = notes.map((note) => insertNoteReferenceMark(
|
|
83
|
+
function serializeNewNotePart({ elementName, notes, document }) {
|
|
84
|
+
const serializedNotes = notes.map((note) => insertNoteReferenceMark({
|
|
85
|
+
xml: serializeNote(elementName, note),
|
|
86
|
+
elementName,
|
|
87
|
+
note,
|
|
88
|
+
customMark: document ? noteUsesCustomMark(document, note) : false
|
|
89
|
+
})).join("");
|
|
82
90
|
return serializeNotePart(elementName, serializeRequiredNoteSeparators(elementName) + serializedNotes);
|
|
83
91
|
}
|
|
84
92
|
/**
|
|
@@ -101,13 +109,21 @@ function serializeEndnotes(endnotes) {
|
|
|
101
109
|
}
|
|
102
110
|
/** Serialize a brand-new footnote part, including Word's required separators
|
|
103
111
|
* and the automatic reference mark at the start of every normal note. */
|
|
104
|
-
function serializeNewFootnotesPart(footnotes) {
|
|
105
|
-
return serializeNewNotePart(
|
|
112
|
+
function serializeNewFootnotesPart(footnotes, document) {
|
|
113
|
+
return serializeNewNotePart({
|
|
114
|
+
elementName: "footnote",
|
|
115
|
+
notes: footnotes,
|
|
116
|
+
...document ? { document } : {}
|
|
117
|
+
});
|
|
106
118
|
}
|
|
107
119
|
/** Serialize a brand-new endnote part, including Word's required separators
|
|
108
120
|
* and the automatic reference mark at the start of every normal note. */
|
|
109
|
-
function serializeNewEndnotesPart(endnotes) {
|
|
110
|
-
return serializeNewNotePart(
|
|
121
|
+
function serializeNewEndnotesPart(endnotes, document) {
|
|
122
|
+
return serializeNewNotePart({
|
|
123
|
+
elementName: "endnote",
|
|
124
|
+
notes: endnotes,
|
|
125
|
+
...document ? { document } : {}
|
|
126
|
+
});
|
|
111
127
|
}
|
|
112
128
|
//#endregion
|
|
113
129
|
export { serializeEndnotes, serializeFootnotes, serializeNewEndnotesPart, serializeNewFootnotesPart };
|
|
@@ -2,7 +2,9 @@ import { modelParagraphFormattingEmission, serializeParagraphPropertySet } from
|
|
|
2
2
|
import { serializePreservedAttributes } from "../attributeRemainder.js";
|
|
3
3
|
import { CONTAINER_CHILDREN } from "../containerChildren.gen.js";
|
|
4
4
|
import { fieldStateAttributes } from "../fieldState.js";
|
|
5
|
+
import { serializeEmptyMarkProperties } from "../paragraphMarkPropertyPresence.js";
|
|
5
6
|
import { getParagraphPropertySource, paragraphPropertySourceMatchesEmission } from "../paragraphPropertySource.js";
|
|
7
|
+
import { ReviewResolutionProvenanceError, paragraphMarkResolutionJoinAttributes, serializeParagraphMarkResolutionJoin, serializeResolutionJoins } from "../reviewResolutionProvenance.js";
|
|
6
8
|
import { DATE_UTC_ATTRIBUTE } from "../trackedChangeInfo.js";
|
|
7
9
|
import { toTransitionalNamespaceUri } from "../transitionalSpelling.js";
|
|
8
10
|
import { captureVerbatimXml, createCapturedXmlSanitizer } from "../verbatimCapture.js";
|
|
@@ -20,11 +22,22 @@ import { PARAGRAPH_MARK_CHANGE_KINDS } from "@stll/docx-core/model";
|
|
|
20
22
|
import { SEQUENCE_CHILDREN } from "@stll/docx-core/schema";
|
|
21
23
|
//#region src/docx/serializer/paragraphSerializer.ts
|
|
22
24
|
/**
|
|
25
|
+
* Paragraph Serializer - Serialize paragraphs to OOXML XML
|
|
26
|
+
*
|
|
27
|
+
* Converts Paragraph objects back to <w:p> XML format for DOCX files.
|
|
28
|
+
* Handles all paragraph properties and child content (runs, hyperlinks, fields, bookmarks).
|
|
29
|
+
*
|
|
30
|
+
* OOXML Reference:
|
|
31
|
+
* - Paragraph: w:p
|
|
32
|
+
* - Paragraph properties: w:pPr
|
|
33
|
+
* - Runs, hyperlinks, bookmarks, fields as child elements
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
23
36
|
* Serialize paragraph formatting properties to w:pPr XML
|
|
24
37
|
*/
|
|
25
38
|
function serializeParagraphMarkChange(mark) {
|
|
26
39
|
const attrs = serializeTrackedChangeAttributes(mark.info);
|
|
27
|
-
return `<w:${mark.kind} ${attrs}/>`;
|
|
40
|
+
return `<w:${mark.kind} ${attrs}${serializeParagraphMarkResolutionJoin(mark)}/>`;
|
|
28
41
|
}
|
|
29
42
|
const RESERVED_PARAGRAPH_PROPERTY_CHILDREN = /* @__PURE__ */ new Set(["pPrChange", "sectPr"]);
|
|
30
43
|
/**
|
|
@@ -212,7 +225,10 @@ const withTrailingParagraphPropertyChildren = (sourceXml, children) => {
|
|
|
212
225
|
const withParagraphMarkChange = (sourceXml, mark) => {
|
|
213
226
|
const root = parseXml(sourceXml, OOXML_NAMESPACE_SCOPE).elements?.at(0);
|
|
214
227
|
if (!root || root.type !== "element") panic("A validated paragraph-property capture could not be parsed for composition");
|
|
215
|
-
const attributes =
|
|
228
|
+
const attributes = {
|
|
229
|
+
...trackedChangeAttributeRecord(mark.info),
|
|
230
|
+
...paragraphMarkResolutionJoinAttributes(mark)
|
|
231
|
+
};
|
|
216
232
|
const markElement = cloneElement(root, {
|
|
217
233
|
name: `w:${mark.kind}`,
|
|
218
234
|
attributes,
|
|
@@ -339,6 +355,20 @@ function serializeComplexField(field) {
|
|
|
339
355
|
parts.push(`<w:r>${rPrXml}<w:fldChar w:fldCharType="end"/></w:r>`);
|
|
340
356
|
return parts.join("");
|
|
341
357
|
}
|
|
358
|
+
var UnrepresentableTrackedCaptureError = class extends TaggedError("UnrepresentableTrackedCaptureError") {};
|
|
359
|
+
/**
|
|
360
|
+
* Preserved markup inside a removal. Markup captured from a removal already
|
|
361
|
+
* spells its text as deleted text. Markup captured outside one and put under
|
|
362
|
+
* a removal afterwards does not: plain runs are respelled here, as a deleted
|
|
363
|
+
* run is. A folded list-number capture that cannot be respelled is refused,
|
|
364
|
+
* rather than written as live text inside the removal.
|
|
365
|
+
*/
|
|
366
|
+
function serializeRemovedPreservedInline(item) {
|
|
367
|
+
if (!/<w:(?:t|instrText)[\s>]/u.test(item.xml)) return item.xml;
|
|
368
|
+
if (/^<w:r[\s>]/u.test(item.xml) && !/<w:(?:drawing|pict|object)\b|<mc:AlternateContent\b/u.test(item.xml)) return rewriteRunTextAsDeleted(item.xml);
|
|
369
|
+
if (item.foldedListNumber) throw new UnrepresentableTrackedCaptureError({ message: "A folded list-number field holds markup that cannot be written as deleted content." });
|
|
370
|
+
return item.xml;
|
|
371
|
+
}
|
|
342
372
|
var UnrepresentableTrackedSimpleFieldError = class extends TaggedError("UnrepresentableTrackedSimpleFieldError") {};
|
|
343
373
|
/** A revision can contain run-level field characters, but not `w:fldSimple`. */
|
|
344
374
|
function serializeTrackedSimpleField(field) {
|
|
@@ -429,7 +459,12 @@ function trackedChangeTag(change) {
|
|
|
429
459
|
}
|
|
430
460
|
}
|
|
431
461
|
function serializeTrackedChange(tag, change) {
|
|
432
|
-
|
|
462
|
+
if (change.resolutionJoins !== void 0 && change.content.some((item) => item.type === "hyperlink")) throw new ReviewResolutionProvenanceError({
|
|
463
|
+
attribute: "resolutionJoins",
|
|
464
|
+
reason: "unrepresentable",
|
|
465
|
+
message: "Tracked-resolution provenance cannot be repartitioned across serialized hyperlinks."
|
|
466
|
+
});
|
|
467
|
+
const attrs = serializeTrackedChangeAttributes(change.info) + serializeResolutionJoins(change.resolutionJoins);
|
|
433
468
|
const disposition = tag === "del" || tag === "moveFrom" ? "removed" : "kept";
|
|
434
469
|
const serializeContentRun = (run) => serializeInlineRun(run, disposition);
|
|
435
470
|
const serializeWrappedItem = (item) => {
|
|
@@ -451,7 +486,7 @@ function serializeTrackedChange(tag, change) {
|
|
|
451
486
|
case "moveFromRangeEnd": return `<w:moveFromRangeEnd ${markupRangeAttributes(item).join(" ")}/>`;
|
|
452
487
|
case "moveToRangeStart": return serializeMoveRangeStart("moveToRangeStart", item);
|
|
453
488
|
case "moveToRangeEnd": return `<w:moveToRangeEnd ${markupRangeAttributes(item).join(" ")}/>`;
|
|
454
|
-
case "preservedInline": return item.xml;
|
|
489
|
+
case "preservedInline": return disposition === "removed" ? serializeRemovedPreservedInline(item) : item.xml;
|
|
455
490
|
case "inlineWrapper":
|
|
456
491
|
case "inlineSdt": return serializeParagraphContent(item, disposition);
|
|
457
492
|
default: return item;
|
|
@@ -574,6 +609,8 @@ function serializeParagraph(paragraph) {
|
|
|
574
609
|
if (paragraph.paraId) attrs.push(`w14:paraId="${escapeXmlAttribute(paragraph.paraId)}"`);
|
|
575
610
|
if (paragraph.textId) attrs.push(`w14:textId="${escapeXmlAttribute(paragraph.textId)}"`);
|
|
576
611
|
if (paragraph.reviewCarrier) attrs.push(`folio:reviewCarrier="${paragraph.reviewCarrier}"`);
|
|
612
|
+
const emptyMarkProperties = serializeEmptyMarkProperties(paragraph);
|
|
613
|
+
if (emptyMarkProperties !== void 0) attrs.push(emptyMarkProperties);
|
|
577
614
|
const written = serializePreservedAttributes(attrs, paragraph.preservedAttributes);
|
|
578
615
|
const attrsStr = written.length > 0 ? ` ${written.join(" ")}` : "";
|
|
579
616
|
parts.push(serializeParagraphFormattingWithOptions(paragraph.formatting, {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FOLIO_REVIEW_HISTORY_NAMESPACE } from "../reviewHistoryNamespace.js";
|
|
1
2
|
import { toTransitionalNamespaceUri } from "../transitionalSpelling.js";
|
|
2
3
|
import { TaggedError } from "better-result";
|
|
3
4
|
import { assertValidOoxmlNumericIds, escapeXmlAttribute } from "@stll/docx-core";
|
|
@@ -144,7 +145,7 @@ const OOXML_NAMESPACES = {
|
|
|
144
145
|
ignorable: true
|
|
145
146
|
},
|
|
146
147
|
folio: {
|
|
147
|
-
uri:
|
|
148
|
+
uri: FOLIO_REVIEW_HISTORY_NAMESPACE,
|
|
148
149
|
ignorable: true
|
|
149
150
|
},
|
|
150
151
|
wpg: {
|
|
@@ -7,6 +7,7 @@ import { serializeGraphicFrameLocks } from "../graphicFrameLocks.js";
|
|
|
7
7
|
import { DECORATIVE_EXTENSION_URI, DECORATIVE_NAMESPACE } from "../imageParser.js";
|
|
8
8
|
import { canReplayEditableImageRawXml } from "../imageRawXml.js";
|
|
9
9
|
import { serializeNonVisualDrawingNames } from "../nonVisualDrawingProps.js";
|
|
10
|
+
import { serializeBoundaryJoins } from "../reviewResolutionProvenance.js";
|
|
10
11
|
import { runHoldsPayload } from "../runPayload.js";
|
|
11
12
|
import { replayableShapeAlternateContent } from "../shapeAlternateContent.js";
|
|
12
13
|
import { requiredWrapPolygon, serializeWrapPolygon } from "../wrapPolygon.js";
|
|
@@ -51,7 +52,7 @@ function getUniqueId(id) {
|
|
|
51
52
|
}
|
|
52
53
|
function serializeRunPropertyChange(change) {
|
|
53
54
|
const previousRPrXml = serializeTextFormatting(change.previousFormatting) || "<w:rPr/>";
|
|
54
|
-
return `<w:rPrChange ${serializeTrackedChangeAttributes(change.info)}>${previousRPrXml}</w:rPrChange>`;
|
|
55
|
+
return `<w:rPrChange ${serializeTrackedChangeAttributes(change.info)}${serializeBoundaryJoins(change.boundaryJoins)}>${previousRPrXml}</w:rPrChange>`;
|
|
55
56
|
}
|
|
56
57
|
function serializeRunProperties(formatting, propertyChanges) {
|
|
57
58
|
const propertyChange = getSingularRunPropertyChange(propertyChanges);
|
|
@@ -537,6 +538,7 @@ function serializeRunContent(content) {
|
|
|
537
538
|
case "symbol": return serializeSymbolContent(content);
|
|
538
539
|
case "footnoteRef":
|
|
539
540
|
case "endnoteRef": return serializeNoteReference(content);
|
|
541
|
+
case "noteMarker": return content.kind === "footnote" ? "<w:footnoteRef/>" : "<w:endnoteRef/>";
|
|
540
542
|
case "fieldChar": return serializeFieldChar(content);
|
|
541
543
|
case "instrText": return serializeInstrText(content);
|
|
542
544
|
case "softHyphen": return serializeSoftHyphen(content);
|