@stll/folio-core 0.32.2 → 0.33.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/__fixtures__/paragraphs.d.ts +17 -0
- package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
- package/dist/ai-edits/apply.d.ts +49 -3
- package/dist/ai-edits/apply.js +649 -85
- package/dist/ai-edits/blockRange.d.ts +7 -3
- package/dist/ai-edits/blockRange.js +14 -6
- package/dist/ai-edits/headless.d.ts +39 -5
- package/dist/ai-edits/headless.js +76 -17
- package/dist/ai-edits/index.d.ts +6 -6
- package/dist/ai-edits/index.js +3 -3
- package/dist/ai-edits/read.d.ts +4 -0
- package/dist/ai-edits/read.js +30 -6
- package/dist/ai-edits/scoped-reading.js +4 -3
- package/dist/ai-edits/snapshot.d.ts +42 -2
- package/dist/ai-edits/snapshot.js +127 -62
- package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
- package/dist/ai-edits/table-row-column-mutations.js +98 -6
- package/dist/ai-edits/table-targets.d.ts +12 -3
- package/dist/ai-edits/table-targets.js +15 -5
- package/dist/ai-edits/types.d.ts +219 -15
- package/dist/ai-edits/word-diff.d.ts +54 -11
- package/dist/ai-edits/word-diff.js +221 -61
- package/dist/compare/__fixtures__/body-sequence.d.ts +33 -0
- package/dist/compare/__fixtures__/body-sequence.js +67 -0
- package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
- package/dist/compare/__fixtures__/nested-table.js +50 -0
- package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
- package/dist/compare/__fixtures__/numbered-list.js +85 -0
- package/dist/compare/column-alignment.d.ts +27 -0
- package/dist/compare/column-alignment.js +162 -0
- package/dist/compare/compare.d.ts +107 -0
- package/dist/compare/compare.js +406 -0
- package/dist/compare/formatting.d.ts +29 -0
- package/dist/compare/formatting.js +84 -0
- package/dist/compare/plan.d.ts +22 -0
- package/dist/compare/plan.js +1059 -0
- package/dist/compare/reproducible-package.d.ts +17 -0
- package/dist/compare/reproducible-package.js +30 -0
- package/dist/compare/scenario.d.ts +77 -0
- package/dist/compare/scenario.js +263 -0
- package/dist/compare/types.d.ts +282 -0
- package/dist/compare/types.js +32 -0
- package/dist/compare/verification.d.ts +55 -0
- package/dist/compare/verification.js +146 -0
- package/dist/compat/eigenpal.d.ts +9 -6
- package/dist/compat/eigenpal.js +6 -3
- package/dist/controller/layoutPipeline.d.ts +13 -0
- package/dist/controller/layoutPipeline.js +15 -1
- package/dist/display-list/build/buildContext.d.ts +39 -0
- package/dist/display-list/build/buildContext.js +31 -0
- package/dist/display-list/build/buildDisplayList.d.ts +79 -0
- package/dist/display-list/build/buildDisplayList.js +283 -0
- package/dist/display-list/build/colors.d.ts +20 -0
- package/dist/display-list/build/colors.js +136 -0
- package/dist/display-list/build/floatingImages.d.ts +24 -0
- package/dist/display-list/build/floatingImages.js +41 -0
- package/dist/display-list/build/fontTable.d.ts +31 -0
- package/dist/display-list/build/fontTable.js +97 -0
- package/dist/display-list/build/furniture.d.ts +86 -0
- package/dist/display-list/build/furniture.js +98 -0
- package/dist/display-list/build/glyphs.d.ts +55 -0
- package/dist/display-list/build/glyphs.js +103 -0
- package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
- package/dist/display-list/build/headerFooterPrimitives.js +40 -0
- package/dist/display-list/build/imagePrimitives.d.ts +35 -0
- package/dist/display-list/build/imagePrimitives.js +264 -0
- package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
- package/dist/display-list/build/pageBorderPrimitives.js +111 -0
- package/dist/display-list/build/pageFurniture.d.ts +35 -0
- package/dist/display-list/build/pageFurniture.js +141 -0
- package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
- package/dist/display-list/build/paragraphPrimitives.js +819 -0
- package/dist/display-list/build/regions.d.ts +46 -0
- package/dist/display-list/build/regions.js +56 -0
- package/dist/display-list/build/storyPrimitives.d.ts +29 -0
- package/dist/display-list/build/storyPrimitives.js +279 -0
- package/dist/display-list/build/strokes.d.ts +19 -0
- package/dist/display-list/build/strokes.js +78 -0
- package/dist/display-list/build/tablePrimitives.d.ts +38 -0
- package/dist/display-list/build/tablePrimitives.js +409 -0
- package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
- package/dist/display-list/build/textBoxPrimitives.js +129 -0
- package/dist/display-list/build/textDecorations.d.ts +29 -0
- package/dist/display-list/build/textDecorations.js +29 -0
- package/dist/display-list/build/unsupported.d.ts +57 -0
- package/dist/display-list/build/unsupported.js +0 -0
- package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
- package/dist/display-list/build/watermarkPrimitives.js +128 -0
- package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
- package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
- package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
- package/dist/display-list/editor/displayListPagePainter.js +63 -0
- package/dist/display-list/editor/pageRenderer.d.ts +24 -0
- package/dist/display-list/editor/pageRenderer.js +36 -0
- package/dist/display-list/primitives.d.ts +95 -0
- package/dist/display-list/primitives.js +127 -0
- package/dist/display-list/types.d.ts +472 -0
- package/dist/display-list/types.js +0 -0
- package/dist/document-operations.d.ts +39 -8
- package/dist/document-operations.js +169 -16
- package/dist/document-stories.d.ts +16 -0
- package/dist/document-stories.js +48 -4
- package/dist/docx/packageParts.d.ts +35 -0
- package/dist/docx/packageParts.js +120 -0
- package/dist/docx/paragraphParser.js +22 -22
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/renderedPageBreakNormalization.js +3 -1
- package/dist/docx/revisionIdNormalization.d.ts +13 -0
- package/dist/docx/revisionIdNormalization.js +103 -0
- package/dist/docx/rezip.js +26 -17
- package/dist/docx/selectiveSave.js +2 -2
- package/dist/docx/serializer/commentSerializer.js +6 -1
- package/dist/docx/serializer/paragraphSerializer.js +33 -4
- package/dist/docx/serializer/runSerializer.js +11 -10
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
- package/dist/docx/server/createBilingualDocx.js +3 -2
- package/dist/docx/streamingXmlParser.d.ts +13 -1
- package/dist/docx/streamingXmlParser.js +49 -11
- package/dist/docx/unzip.d.ts +1 -2
- package/dist/docx/unzip.js +25 -6
- package/dist/docx/xmlParser.d.ts +3 -1
- package/dist/docx/xmlParser.js +33 -27
- package/dist/export-pdf.d.ts +43 -0
- package/dist/export-pdf.js +98 -0
- package/dist/fonts/headlessMeasure.d.ts +76 -0
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/fonts/sfnt/parse.d.ts +51 -0
- package/dist/fonts/sfnt/parse.js +525 -0
- package/dist/fonts/sfnt/subset.d.ts +20 -0
- package/dist/fonts/sfnt/subset.js +350 -0
- package/dist/fonts/sfnt/tables.d.ts +88 -0
- package/dist/fonts/sfnt/tables.js +131 -0
- package/dist/fonts/sfnt/woff.d.ts +14 -0
- package/dist/fonts/sfnt/woff.js +163 -0
- package/dist/generated/text_shaper.js +324 -0
- package/dist/generated/text_shaper_bg.wasm +0 -0
- package/dist/headless-layout.d.ts +75 -0
- package/dist/headless-layout.js +350 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.js +6 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
- package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
- package/dist/layout-engine/measure/advanceComposition.js +148 -0
- package/dist/layout-engine/measure/cache.d.ts +2 -0
- package/dist/layout-engine/measure/measureContainer.js +13 -0
- package/dist/layout-engine/measure/measureParagraph.js +1 -2
- package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
- package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
- package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
- package/dist/layout-engine/tableIndentCompatibility.js +15 -0
- package/dist/layout-engine/types.d.ts +8 -0
- package/dist/layout-painter/renderPage.d.ts +29 -1
- package/dist/layout-painter/renderPage.js +47 -19
- package/dist/layout-painter/renderParagraph.d.ts +21 -2
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/layout-painter/renderTable.js +37 -4
- package/dist/managers/editorShortcuts.d.ts +33 -1
- package/dist/managers/editorShortcuts.js +20 -1
- package/dist/markdown/renderRuns.js +7 -2
- package/dist/markdown/renderTable.js +5 -1
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/pdf/contentStream.d.ts +73 -0
- package/dist/pdf/contentStream.js +143 -0
- package/dist/pdf/fonts.d.ts +96 -0
- package/dist/pdf/fonts.js +593 -0
- package/dist/pdf/images.d.ts +28 -0
- package/dist/pdf/images.js +439 -0
- package/dist/pdf/objects.d.ts +93 -0
- package/dist/pdf/objects.js +237 -0
- package/dist/pdf/pageSpace.d.ts +43 -0
- package/dist/pdf/pageSpace.js +57 -0
- package/dist/pdf/paint.d.ts +32 -0
- package/dist/pdf/paint.js +442 -0
- package/dist/pdf/writePdf.d.ts +56 -0
- package/dist/pdf/writePdf.js +345 -0
- package/dist/prosemirror/commands/comments.js +91 -6
- package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
- package/dist/prosemirror/conversion/toProseDoc.js +41 -4
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
- package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
- package/dist/prosemirror/plugins/suggestionMode.js +1 -0
- package/dist/prosemirror/revisionCarriers.js +19 -2
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
- package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
- package/dist/prosemirror/validation.js +55 -27
- package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
- package/dist/prosemirror/zeroWidthAnchors.js +23 -0
- package/dist/redline.js +12 -79
- package/dist/server.d.ts +3 -3
- package/dist/server.js +2 -2
- package/dist/shaping/placeRun.d.ts +51 -0
- package/dist/shaping/placeRun.js +84 -0
- package/dist/shaping/shaper.d.ts +92 -0
- package/dist/shaping/shaper.js +102 -0
- package/dist/types/block-id.d.ts +18 -2
- package/dist/types/block-id.js +49 -5
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/clipboard.js +4 -3
- package/dist/utils/fontResolver.js +183 -0
- package/dist/utils/htmlComments.d.ts +17 -0
- package/dist/utils/htmlComments.js +22 -0
- package/dist/utils/scriptSegments.d.ts +12 -1
- package/dist/utils/scriptSegments.js +17 -1
- package/dist/version-comparison.d.ts +1 -1
- package/package.json +7 -3
|
@@ -11,6 +11,11 @@ const FOLIO_DOCUMENT_OPERATION_TYPES = Object.freeze([
|
|
|
11
11
|
"insertBeforeBlock",
|
|
12
12
|
"replaceBlock",
|
|
13
13
|
"deleteBlock",
|
|
14
|
+
"splitBlock",
|
|
15
|
+
"mergeBlockWithNext",
|
|
16
|
+
"setBlockParagraphProperties",
|
|
17
|
+
"insertTable",
|
|
18
|
+
"deleteTable",
|
|
14
19
|
"commentOnBlock",
|
|
15
20
|
"insertSignatureTable",
|
|
16
21
|
"insertTableRow",
|
|
@@ -56,6 +61,11 @@ const FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE = Object.freeze({
|
|
|
56
61
|
insertBeforeBlock: DIRECT_TRACKED_AND_SUGGESTED_MODES,
|
|
57
62
|
replaceBlock: DIRECT_TRACKED_AND_SUGGESTED_MODES,
|
|
58
63
|
deleteBlock: DIRECT_TRACKED_AND_SUGGESTED_MODES,
|
|
64
|
+
splitBlock: DIRECT_AND_TRACKED_MODES,
|
|
65
|
+
mergeBlockWithNext: DIRECT_AND_TRACKED_MODES,
|
|
66
|
+
setBlockParagraphProperties: DIRECT_AND_TRACKED_MODES,
|
|
67
|
+
insertTable: DIRECT_AND_TRACKED_MODES,
|
|
68
|
+
deleteTable: DIRECT_AND_TRACKED_MODES,
|
|
59
69
|
commentOnBlock: DIRECT_AND_TRACKED_MODES,
|
|
60
70
|
insertSignatureTable: DIRECT_AND_SUGGESTED_MODES,
|
|
61
71
|
insertTableRow: DIRECT_TRACKED_AND_SUGGESTED_MODES,
|
|
@@ -147,6 +157,49 @@ const readNonNegativeInteger = (value, key, path) => {
|
|
|
147
157
|
if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 0) return candidate;
|
|
148
158
|
return invalidBatch(`${path}.${key}`, "expected a non-negative integer");
|
|
149
159
|
};
|
|
160
|
+
/**
|
|
161
|
+
* A non-negative integer that may also be cleared. `undefined` is "say
|
|
162
|
+
* nothing"; `null` is "there is none", which is a different instruction and
|
|
163
|
+
* the one absence alone cannot give.
|
|
164
|
+
*/
|
|
165
|
+
const readClearableNonNegativeInteger = (value, key, path) => {
|
|
166
|
+
const candidate = value[key];
|
|
167
|
+
if (candidate === void 0) return;
|
|
168
|
+
return candidate === null ? null : readNonNegativeInteger(value, key, path);
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* A rectangular grid of cell texts. Rectangular because a table whose rows
|
|
172
|
+
* hold different cell counts is not a table any consumer can lay out, and the
|
|
173
|
+
* batch is the last place to catch that.
|
|
174
|
+
*/
|
|
175
|
+
const readTableRows = (value, path) => {
|
|
176
|
+
const rows = value["rows"];
|
|
177
|
+
const rowsPath = `${path}.rows`;
|
|
178
|
+
if (!Array.isArray(rows) || rows.length === 0) return invalidBatch(rowsPath, "expected a non-empty array");
|
|
179
|
+
const parsed = rows.map((row, index) => {
|
|
180
|
+
if (!Array.isArray(row) || row.length === 0) return invalidBatch(`${rowsPath}[${String(index)}]`, "expected a non-empty array");
|
|
181
|
+
return row.map((cell, cellIndex) => {
|
|
182
|
+
if (typeof cell !== "string") return invalidBatch(`${rowsPath}[${String(index)}][${String(cellIndex)}]`, "expected a string");
|
|
183
|
+
return cell;
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
const width = parsed[0]?.length ?? 0;
|
|
187
|
+
if (parsed.some((row) => row.length !== width)) return invalidBatch(rowsPath, "expected every row to hold the same number of cells");
|
|
188
|
+
return parsed;
|
|
189
|
+
};
|
|
190
|
+
const readParagraphProperties = (value, path) => {
|
|
191
|
+
const candidate = value["properties"];
|
|
192
|
+
const propertiesPath = `${path}.properties`;
|
|
193
|
+
if (!isPlainObject(candidate)) return invalidBatch(propertiesPath, "expected an object");
|
|
194
|
+
assertAllowedKeys(candidate, propertiesPath, ["styleId", "listLevel"]);
|
|
195
|
+
const styleId = candidate["styleId"] === null ? null : readOptionalString(candidate, "styleId", propertiesPath);
|
|
196
|
+
const listLevel = readClearableNonNegativeInteger(candidate, "listLevel", propertiesPath);
|
|
197
|
+
if (styleId === void 0 && listLevel === void 0) return invalidBatch(propertiesPath, "expected at least one property to set");
|
|
198
|
+
return {
|
|
199
|
+
...styleId !== void 0 && { styleId },
|
|
200
|
+
...listLevel !== void 0 && { listLevel }
|
|
201
|
+
};
|
|
202
|
+
};
|
|
150
203
|
const readTextRange = (value, path) => {
|
|
151
204
|
const candidate = value["range"];
|
|
152
205
|
const rangePath = `${path}.range`;
|
|
@@ -184,16 +237,19 @@ const readInlineFormatting = (value, path) => {
|
|
|
184
237
|
assertAllowedKeys(candidate, formattingPath, [
|
|
185
238
|
"bold",
|
|
186
239
|
"italic",
|
|
187
|
-
"underline"
|
|
240
|
+
"underline",
|
|
241
|
+
"strike"
|
|
188
242
|
]);
|
|
189
243
|
const bold = readOptionalBoolean(candidate, "bold", formattingPath);
|
|
190
244
|
const italic = readOptionalBoolean(candidate, "italic", formattingPath);
|
|
191
245
|
const underline = readOptionalBoolean(candidate, "underline", formattingPath);
|
|
192
|
-
|
|
246
|
+
const strike = readOptionalBoolean(candidate, "strike", formattingPath);
|
|
247
|
+
if (bold === void 0 && italic === void 0 && underline === void 0 && strike === void 0) return invalidBatch(formattingPath, "expected at least one formatting property");
|
|
193
248
|
return {
|
|
194
249
|
...bold !== void 0 && { bold },
|
|
195
250
|
...italic !== void 0 && { italic },
|
|
196
|
-
...underline !== void 0 && { underline }
|
|
251
|
+
...underline !== void 0 && { underline },
|
|
252
|
+
...strike !== void 0 && { strike }
|
|
197
253
|
};
|
|
198
254
|
};
|
|
199
255
|
const readOptionalComment = (value, path) => {
|
|
@@ -271,6 +327,8 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
|
|
|
271
327
|
...COMMON_OPERATION_KEYS,
|
|
272
328
|
"text",
|
|
273
329
|
"inheritFormatting",
|
|
330
|
+
"listLevel",
|
|
331
|
+
"moveId",
|
|
274
332
|
"pageBreakBefore",
|
|
275
333
|
"styleId",
|
|
276
334
|
"comment"
|
|
@@ -279,6 +337,8 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
|
|
|
279
337
|
...COMMON_OPERATION_KEYS,
|
|
280
338
|
"text",
|
|
281
339
|
"inheritFormatting",
|
|
340
|
+
"listLevel",
|
|
341
|
+
"moveId",
|
|
282
342
|
"pageBreakBefore",
|
|
283
343
|
"styleId",
|
|
284
344
|
"comment"
|
|
@@ -290,7 +350,24 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
|
|
|
290
350
|
"styleId",
|
|
291
351
|
"comment"
|
|
292
352
|
],
|
|
293
|
-
deleteBlock: [
|
|
353
|
+
deleteBlock: [
|
|
354
|
+
...COMMON_OPERATION_KEYS,
|
|
355
|
+
"moveId",
|
|
356
|
+
"comment"
|
|
357
|
+
],
|
|
358
|
+
splitBlock: [
|
|
359
|
+
...COMMON_OPERATION_KEYS,
|
|
360
|
+
"offset",
|
|
361
|
+
"separator"
|
|
362
|
+
],
|
|
363
|
+
mergeBlockWithNext: [...COMMON_OPERATION_KEYS, "separator"],
|
|
364
|
+
setBlockParagraphProperties: [...COMMON_OPERATION_KEYS, "properties"],
|
|
365
|
+
insertTable: [
|
|
366
|
+
...COMMON_OPERATION_KEYS,
|
|
367
|
+
"position",
|
|
368
|
+
"rows"
|
|
369
|
+
],
|
|
370
|
+
deleteTable: COMMON_OPERATION_KEYS,
|
|
294
371
|
commentOnBlock: [
|
|
295
372
|
...COMMON_OPERATION_KEYS,
|
|
296
373
|
"quote",
|
|
@@ -383,6 +460,49 @@ const parseDocumentOperation = (value, index) => {
|
|
|
383
460
|
formatting: readInlineFormatting(value, path)
|
|
384
461
|
};
|
|
385
462
|
const blockId = readString(value, "blockId", path);
|
|
463
|
+
if (type === "insertTable") {
|
|
464
|
+
const position = value["position"];
|
|
465
|
+
if (position !== void 0 && position !== "after" && position !== "before") return invalidBatch(`${path}.position`, "expected \"after\" or \"before\" when provided");
|
|
466
|
+
return {
|
|
467
|
+
...operationMeta,
|
|
468
|
+
id,
|
|
469
|
+
type,
|
|
470
|
+
blockId,
|
|
471
|
+
...position !== void 0 && { position },
|
|
472
|
+
rows: readTableRows(value, path)
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
if (type === "deleteTable") return {
|
|
476
|
+
...operationMeta,
|
|
477
|
+
id,
|
|
478
|
+
type,
|
|
479
|
+
blockId
|
|
480
|
+
};
|
|
481
|
+
if (type === "setBlockParagraphProperties") return {
|
|
482
|
+
...operationMeta,
|
|
483
|
+
id,
|
|
484
|
+
type,
|
|
485
|
+
blockId,
|
|
486
|
+
properties: readParagraphProperties(value, path)
|
|
487
|
+
};
|
|
488
|
+
if (type === "splitBlock" || type === "mergeBlockWithNext") {
|
|
489
|
+
const separator = readOptionalString(value, "separator", path);
|
|
490
|
+
if (type === "mergeBlockWithNext") return {
|
|
491
|
+
...operationMeta,
|
|
492
|
+
id,
|
|
493
|
+
type,
|
|
494
|
+
blockId,
|
|
495
|
+
...separator !== void 0 && { separator }
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
...operationMeta,
|
|
499
|
+
id,
|
|
500
|
+
type,
|
|
501
|
+
blockId,
|
|
502
|
+
offset: readNonNegativeInteger(value, "offset", path),
|
|
503
|
+
...separator !== void 0 && { separator }
|
|
504
|
+
};
|
|
505
|
+
}
|
|
386
506
|
if (type === "replaceInBlock") return {
|
|
387
507
|
...operationMeta,
|
|
388
508
|
id,
|
|
@@ -395,7 +515,9 @@ const parseDocumentOperation = (value, index) => {
|
|
|
395
515
|
if (type === "insertAfterBlock" || type === "insertBeforeBlock") {
|
|
396
516
|
const inheritFormatting = readOptionalBoolean(value, "inheritFormatting", path);
|
|
397
517
|
const pageBreakBefore = readOptionalBoolean(value, "pageBreakBefore", path);
|
|
398
|
-
const styleId = readOptionalString(value, "styleId", path);
|
|
518
|
+
const styleId = value["styleId"] === null ? null : readOptionalString(value, "styleId", path);
|
|
519
|
+
const moveId = readOptionalString(value, "moveId", path);
|
|
520
|
+
const listLevel = readClearableNonNegativeInteger(value, "listLevel", path);
|
|
399
521
|
return {
|
|
400
522
|
...operationMeta,
|
|
401
523
|
id,
|
|
@@ -403,6 +525,8 @@ const parseDocumentOperation = (value, index) => {
|
|
|
403
525
|
blockId,
|
|
404
526
|
text: readString(value, "text", path),
|
|
405
527
|
...inheritFormatting !== void 0 && { inheritFormatting },
|
|
528
|
+
...listLevel !== void 0 && { listLevel },
|
|
529
|
+
...moveId !== void 0 && { moveId },
|
|
406
530
|
...pageBreakBefore !== void 0 && { pageBreakBefore },
|
|
407
531
|
...styleId !== void 0 && { styleId },
|
|
408
532
|
...comment !== void 0 && { comment }
|
|
@@ -422,13 +546,17 @@ const parseDocumentOperation = (value, index) => {
|
|
|
422
546
|
...comment !== void 0 && { comment }
|
|
423
547
|
};
|
|
424
548
|
}
|
|
425
|
-
if (type === "deleteBlock")
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
549
|
+
if (type === "deleteBlock") {
|
|
550
|
+
const moveId = readOptionalString(value, "moveId", path);
|
|
551
|
+
return {
|
|
552
|
+
...operationMeta,
|
|
553
|
+
id,
|
|
554
|
+
type,
|
|
555
|
+
blockId,
|
|
556
|
+
...moveId !== void 0 && { moveId },
|
|
557
|
+
...comment !== void 0 && { comment }
|
|
558
|
+
};
|
|
559
|
+
}
|
|
432
560
|
if (type === "commentOnBlock") {
|
|
433
561
|
if (comment === void 0) return invalidBatch(`${path}.comment`, "expected an object");
|
|
434
562
|
const quote = readOptionalString(value, "quote", path);
|
|
@@ -642,6 +770,14 @@ const getPrimaryAffectedTarget = (operation, story) => {
|
|
|
642
770
|
blockId: operation.blockId,
|
|
643
771
|
effect: "deleted"
|
|
644
772
|
};
|
|
773
|
+
case "setBlockParagraphProperties":
|
|
774
|
+
case "splitBlock":
|
|
775
|
+
case "mergeBlockWithNext": return {
|
|
776
|
+
type: "block",
|
|
777
|
+
story,
|
|
778
|
+
blockId: operation.blockId,
|
|
779
|
+
effect: "updated"
|
|
780
|
+
};
|
|
645
781
|
case "commentOnBlock": return {
|
|
646
782
|
type: "block",
|
|
647
783
|
story,
|
|
@@ -655,6 +791,19 @@ const getPrimaryAffectedTarget = (operation, story) => {
|
|
|
655
791
|
position: operation.position ?? "after",
|
|
656
792
|
content: "signatureTable"
|
|
657
793
|
};
|
|
794
|
+
case "insertTable": return {
|
|
795
|
+
type: "insertion",
|
|
796
|
+
story,
|
|
797
|
+
anchorBlockId: operation.blockId,
|
|
798
|
+
position: operation.position ?? "after",
|
|
799
|
+
content: "table"
|
|
800
|
+
};
|
|
801
|
+
case "deleteTable": return {
|
|
802
|
+
type: "block",
|
|
803
|
+
story,
|
|
804
|
+
blockId: operation.blockId,
|
|
805
|
+
effect: "deleted"
|
|
806
|
+
};
|
|
658
807
|
case "insertTableRow": return {
|
|
659
808
|
type: "insertion",
|
|
660
809
|
story,
|
|
@@ -730,7 +879,7 @@ const getFolioDocumentOperationReceiptsForStory = ({ operations, applied, story
|
|
|
730
879
|
});
|
|
731
880
|
return receipts;
|
|
732
881
|
};
|
|
733
|
-
const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle }) => {
|
|
882
|
+
const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle, revisionStamp, wordDiff }) => {
|
|
734
883
|
const parsedBatch = parseFolioDocumentOperationBatch(batch);
|
|
735
884
|
const apply = ({ targetView, targetCreateCommentId = createCommentId, preview = false }) => {
|
|
736
885
|
return (preview ? previewFolioAIEditOperations : applyFolioAIEditOperations)({
|
|
@@ -739,7 +888,9 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
|
|
|
739
888
|
operations: parsedBatch.operations,
|
|
740
889
|
mode: parsedBatch.mode ?? "tracked-changes",
|
|
741
890
|
...author !== void 0 && { author },
|
|
742
|
-
...targetCreateCommentId !== void 0 && { createCommentId: targetCreateCommentId }
|
|
891
|
+
...targetCreateCommentId !== void 0 && { createCommentId: targetCreateCommentId },
|
|
892
|
+
...revisionStamp !== void 0 && { revisionStamp },
|
|
893
|
+
...wordDiff !== void 0 && { wordDiff }
|
|
743
894
|
});
|
|
744
895
|
};
|
|
745
896
|
const preview = () => apply({
|
|
@@ -760,7 +911,8 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
|
|
|
760
911
|
issues: getFolioDocumentOperationIssues(parsedBatch.operations, skipped),
|
|
761
912
|
receipts: [],
|
|
762
913
|
...previewResult.normalizations !== void 0 && { normalizations: previewResult.normalizations },
|
|
763
|
-
undoHandle: null
|
|
914
|
+
undoHandle: null,
|
|
915
|
+
nextRevisionId: previewResult.nextRevisionId
|
|
764
916
|
};
|
|
765
917
|
};
|
|
766
918
|
if (parsedBatch.dryRun === true) {
|
|
@@ -778,7 +930,8 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
|
|
|
778
930
|
story
|
|
779
931
|
}),
|
|
780
932
|
...previewResult.normalizations !== void 0 && { normalizations: previewResult.normalizations },
|
|
781
|
-
undoHandle: null
|
|
933
|
+
undoHandle: null,
|
|
934
|
+
nextRevisionId: previewResult.nextRevisionId
|
|
782
935
|
};
|
|
783
936
|
}
|
|
784
937
|
if (parsedBatch.atomic === true) {
|
|
@@ -4,6 +4,22 @@ type FolioDocumentStoryPair = {
|
|
|
4
4
|
baseStory: FolioDocumentStoryHandle | null;
|
|
5
5
|
revisedStory: FolioDocumentStoryHandle | null;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* Pair two packages' stories: by identity first, then, for headers and
|
|
9
|
+
* footers, by kind and document order.
|
|
10
|
+
*
|
|
11
|
+
* A relationship id names a part inside ONE package. Two revisions of one file
|
|
12
|
+
* carry it forward, so matching on it pairs the right header exactly and is
|
|
13
|
+
* tried first. Two independently authored files do not: `rId6` in one has
|
|
14
|
+
* nothing to do with `rId6` in the other, and matching on it alone left every
|
|
15
|
+
* header and footer of such a pair reported as present on one side only —
|
|
16
|
+
* a comparison of two contracts covered their bodies and nothing else.
|
|
17
|
+
*
|
|
18
|
+
* Order is what is left to pair on. It is the order the package lists its
|
|
19
|
+
* parts in, so the nth header of one document answers to the nth header of the
|
|
20
|
+
* other, and a document with more of them than the other leaves its surplus
|
|
21
|
+
* unpaired rather than pairing a first-page header against a default one.
|
|
22
|
+
*/
|
|
7
23
|
declare const pairFolioDocumentStories: (baseStories: readonly FolioDocumentStoryHandle[], revisedStories: readonly FolioDocumentStoryHandle[]) => FolioDocumentStoryPair[];
|
|
8
24
|
//#endregion
|
|
9
25
|
export { FolioDocumentStoryPair, pairFolioDocumentStories };
|
package/dist/document-stories.js
CHANGED
|
@@ -4,18 +4,62 @@ const documentStoryKey = (story) => {
|
|
|
4
4
|
if (story.type === "header" || story.type === "footer") return `${story.type}:${story.relationshipId}`;
|
|
5
5
|
return `${story.type}:${String(story.noteId)}`;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* Pair two packages' stories: by identity first, then, for headers and
|
|
9
|
+
* footers, by kind and document order.
|
|
10
|
+
*
|
|
11
|
+
* A relationship id names a part inside ONE package. Two revisions of one file
|
|
12
|
+
* carry it forward, so matching on it pairs the right header exactly and is
|
|
13
|
+
* tried first. Two independently authored files do not: `rId6` in one has
|
|
14
|
+
* nothing to do with `rId6` in the other, and matching on it alone left every
|
|
15
|
+
* header and footer of such a pair reported as present on one side only —
|
|
16
|
+
* a comparison of two contracts covered their bodies and nothing else.
|
|
17
|
+
*
|
|
18
|
+
* Order is what is left to pair on. It is the order the package lists its
|
|
19
|
+
* parts in, so the nth header of one document answers to the nth header of the
|
|
20
|
+
* other, and a document with more of them than the other leaves its surplus
|
|
21
|
+
* unpaired rather than pairing a first-page header against a default one.
|
|
22
|
+
*/
|
|
7
23
|
const pairFolioDocumentStories = (baseStories, revisedStories) => {
|
|
8
24
|
const revisedByKey = new Map(revisedStories.map((story) => [documentStoryKey(story), story]));
|
|
9
25
|
const pairedKeys = /* @__PURE__ */ new Set();
|
|
10
|
-
const
|
|
26
|
+
const pairedByIdentity = /* @__PURE__ */ new Map();
|
|
11
27
|
for (const baseStory of baseStories) {
|
|
12
28
|
const key = documentStoryKey(baseStory);
|
|
13
|
-
const revisedStory = revisedByKey.get(key)
|
|
29
|
+
const revisedStory = revisedByKey.get(key);
|
|
30
|
+
if (revisedStory) {
|
|
31
|
+
pairedKeys.add(key);
|
|
32
|
+
pairedByIdentity.set(baseStory, revisedStory);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const unpairedRevisedOfKind = (kind) => revisedStories.filter((story) => story.type === kind && !pairedKeys.has(documentStoryKey(story)));
|
|
36
|
+
const remaining = {
|
|
37
|
+
header: unpairedRevisedOfKind("header"),
|
|
38
|
+
footer: unpairedRevisedOfKind("footer")
|
|
39
|
+
};
|
|
40
|
+
const pairs = [];
|
|
41
|
+
for (const baseStory of baseStories) {
|
|
42
|
+
const identityMatch = pairedByIdentity.get(baseStory);
|
|
43
|
+
if (identityMatch) {
|
|
44
|
+
pairs.push({
|
|
45
|
+
baseStory,
|
|
46
|
+
revisedStory: identityMatch
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (baseStory.type !== "header" && baseStory.type !== "footer") {
|
|
51
|
+
pairs.push({
|
|
52
|
+
baseStory,
|
|
53
|
+
revisedStory: null
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const byOrder = remaining[baseStory.type].shift() ?? null;
|
|
58
|
+
if (byOrder) pairedKeys.add(documentStoryKey(byOrder));
|
|
14
59
|
pairs.push({
|
|
15
60
|
baseStory,
|
|
16
|
-
revisedStory
|
|
61
|
+
revisedStory: byOrder
|
|
17
62
|
});
|
|
18
|
-
if (revisedStory) pairedKeys.add(key);
|
|
19
63
|
}
|
|
20
64
|
for (const revisedStory of revisedStories) if (!pairedKeys.has(documentStoryKey(revisedStory))) pairs.push({
|
|
21
65
|
baseStory: null,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import JSZip from "jszip";
|
|
2
|
+
//#region src/docx/packageParts.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether an entry path would escape the package when a host writes the archive
|
|
5
|
+
* to disk: absolute, backslash-separated, or climbing out through `..`. This is
|
|
6
|
+
* the only thing a save refuses, and it is about the archive, not the document.
|
|
7
|
+
*
|
|
8
|
+
* The full repack removes such an entry along with its references; the
|
|
9
|
+
* selective path bails to the full repack rather than overlay one part on top
|
|
10
|
+
* of a package carrying one. Both read this predicate, so neither can drift
|
|
11
|
+
* into passing what the other blocks.
|
|
12
|
+
*/
|
|
13
|
+
declare const isUnsafePackagePath: (path: string) => boolean;
|
|
14
|
+
/** Drop the entries whose path a save refuses. Every other part survives. */
|
|
15
|
+
declare const removeUnsafeEntries: (zip: JSZip) => void;
|
|
16
|
+
/** What reconciliation removed to make the package internally consistent. */
|
|
17
|
+
type PackageReferenceRepair = {
|
|
18
|
+
/** Parts a relationship named that the package does not hold. */
|
|
19
|
+
danglingRelationships: string[];
|
|
20
|
+
/** Parts a content-type override named that the package does not hold. */
|
|
21
|
+
danglingOverrides: string[];
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Make the package internally consistent before it is written: every internal
|
|
25
|
+
* relationship target and every content-type override must name a part the
|
|
26
|
+
* output actually holds.
|
|
27
|
+
*
|
|
28
|
+
* A part is rewritten only when something was dropped from it, so a package
|
|
29
|
+
* that needs no repair leaves byte for byte as it arrived. Running this on an
|
|
30
|
+
* already-reconciled package therefore reports nothing and changes nothing,
|
|
31
|
+
* which is what the repack tests assert about their own output.
|
|
32
|
+
*/
|
|
33
|
+
declare const reconcilePackageReferences: (zip: JSZip, compressionLevel: number) => Promise<PackageReferenceRepair>;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { PackageReferenceRepair, isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { resolveRelativePath } from "./relsParser.js";
|
|
2
|
+
//#region src/docx/packageParts.ts
|
|
3
|
+
const CONTENT_TYPE_ELEMENT = /<(?:Default|Override)\b[^>]*?\/?>/giu;
|
|
4
|
+
const PART_NAME_ATTRIBUTE = /\bPartName\s*=\s*(?<quote>["'])(?<value>[^"']*)\k<quote>/u;
|
|
5
|
+
const RELATIONSHIP_ELEMENT = /<Relationship\b[^>]*?(?:\/>|>\s*<\/Relationship>)/giu;
|
|
6
|
+
const TARGET_ATTRIBUTE = /\bTarget\s*=\s*(?<quote>["'])(?<value>[^"']*)\k<quote>/u;
|
|
7
|
+
const TARGET_MODE_ATTRIBUTE = /\bTargetMode\s*=\s*(?<quote>["'])(?<value>[^"']*)\k<quote>/u;
|
|
8
|
+
const CONTENT_TYPES_PATTERN = /^\[Content_Types\]\.xml$/iu;
|
|
9
|
+
/** Normalize a package path for comparison: lower case, no leading slash. */
|
|
10
|
+
const normalizePartPath = (path) => (path.startsWith("/") ? path.slice(1) : path).toLowerCase();
|
|
11
|
+
/**
|
|
12
|
+
* Decode `%XX` escapes in a part name. `decodeURI` throws on a malformed
|
|
13
|
+
* escape, and a malformed target is exactly the input this has to survive, so
|
|
14
|
+
* the replacement is done by hand.
|
|
15
|
+
*/
|
|
16
|
+
const decodePartName = (name) => name.replaceAll(/%[0-9A-Fa-f]{2}/gu, (escape) => String.fromCharCode(Number.parseInt(escape.slice(1), 16)));
|
|
17
|
+
/**
|
|
18
|
+
* Whether an entry path would escape the package when a host writes the archive
|
|
19
|
+
* to disk: absolute, backslash-separated, or climbing out through `..`. This is
|
|
20
|
+
* the only thing a save refuses, and it is about the archive, not the document.
|
|
21
|
+
*
|
|
22
|
+
* The full repack removes such an entry along with its references; the
|
|
23
|
+
* selective path bails to the full repack rather than overlay one part on top
|
|
24
|
+
* of a package carrying one. Both read this predicate, so neither can drift
|
|
25
|
+
* into passing what the other blocks.
|
|
26
|
+
*/
|
|
27
|
+
const isUnsafePackagePath = (path) => {
|
|
28
|
+
if (!path || path.startsWith("/") || path.includes("\\")) return true;
|
|
29
|
+
return path.split("/").some((segment) => segment === "..");
|
|
30
|
+
};
|
|
31
|
+
/** Drop the entries whose path a save refuses. Every other part survives. */
|
|
32
|
+
const removeUnsafeEntries = (zip) => {
|
|
33
|
+
for (const [path, file] of Object.entries(zip.files)) if (!file.dir && isUnsafePackagePath(path)) zip.remove(path);
|
|
34
|
+
};
|
|
35
|
+
const presentPartPaths = (zip) => {
|
|
36
|
+
const present = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const [path, file] of Object.entries(zip.files)) if (!file.dir) present.add(normalizePartPath(path));
|
|
38
|
+
return present;
|
|
39
|
+
};
|
|
40
|
+
const isPresent = (present, partPath) => present.has(normalizePartPath(partPath)) || present.has(normalizePartPath(decodePartName(partPath)));
|
|
41
|
+
/** The part a relationship names, or null when it points outside the package. */
|
|
42
|
+
const relationshipTarget = (element, relsPath) => {
|
|
43
|
+
if (TARGET_MODE_ATTRIBUTE.exec(element)?.groups?.["value"] === "External") return null;
|
|
44
|
+
const target = TARGET_ATTRIBUTE.exec(element)?.groups?.["value"];
|
|
45
|
+
return target === void 0 ? null : resolveRelativePath(relsPath, target);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Every `.rels` part with its text. Read one at a time rather than through
|
|
49
|
+
* `Promise.all`: a package holds a handful of tiny relationship parts, and the
|
|
50
|
+
* sequential read keeps the output order stable.
|
|
51
|
+
*/
|
|
52
|
+
const readRelationshipParts = async (zip) => {
|
|
53
|
+
const parts = [];
|
|
54
|
+
for (const [path, file] of Object.entries(zip.files)) {
|
|
55
|
+
if (file.dir || !normalizePartPath(path).endsWith(".rels")) continue;
|
|
56
|
+
parts.push({
|
|
57
|
+
path,
|
|
58
|
+
xml: await file.async("text")
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return parts;
|
|
62
|
+
};
|
|
63
|
+
const writePart = ({ zip, path, xml, compressionLevel }) => {
|
|
64
|
+
zip.file(path, xml, {
|
|
65
|
+
compression: "DEFLATE",
|
|
66
|
+
compressionOptions: { level: compressionLevel }
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Make the package internally consistent before it is written: every internal
|
|
71
|
+
* relationship target and every content-type override must name a part the
|
|
72
|
+
* output actually holds.
|
|
73
|
+
*
|
|
74
|
+
* A part is rewritten only when something was dropped from it, so a package
|
|
75
|
+
* that needs no repair leaves byte for byte as it arrived. Running this on an
|
|
76
|
+
* already-reconciled package therefore reports nothing and changes nothing,
|
|
77
|
+
* which is what the repack tests assert about their own output.
|
|
78
|
+
*/
|
|
79
|
+
const reconcilePackageReferences = async (zip, compressionLevel) => {
|
|
80
|
+
const present = presentPartPaths(zip);
|
|
81
|
+
const repair = {
|
|
82
|
+
danglingRelationships: [],
|
|
83
|
+
danglingOverrides: []
|
|
84
|
+
};
|
|
85
|
+
for (const { path, xml } of await readRelationshipParts(zip)) {
|
|
86
|
+
let dropped = false;
|
|
87
|
+
const pruned = xml.replaceAll(RELATIONSHIP_ELEMENT, (element) => {
|
|
88
|
+
const target = relationshipTarget(element, path);
|
|
89
|
+
if (target === null || isPresent(present, target)) return element;
|
|
90
|
+
repair.danglingRelationships.push(target);
|
|
91
|
+
dropped = true;
|
|
92
|
+
return "";
|
|
93
|
+
});
|
|
94
|
+
if (dropped) writePart({
|
|
95
|
+
zip,
|
|
96
|
+
path,
|
|
97
|
+
xml: pruned,
|
|
98
|
+
compressionLevel
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const contentTypes = zip.file(CONTENT_TYPES_PATTERN)[0];
|
|
102
|
+
if (!contentTypes) return repair;
|
|
103
|
+
let droppedOverride = false;
|
|
104
|
+
const prunedContentTypes = (await contentTypes.async("text")).replaceAll(CONTENT_TYPE_ELEMENT, (element) => {
|
|
105
|
+
const partName = PART_NAME_ATTRIBUTE.exec(element)?.groups?.["value"];
|
|
106
|
+
if (partName === void 0 || isPresent(present, partName)) return element;
|
|
107
|
+
repair.danglingOverrides.push(normalizePartPath(decodePartName(partName)));
|
|
108
|
+
droppedOverride = true;
|
|
109
|
+
return "";
|
|
110
|
+
});
|
|
111
|
+
if (droppedOverride) writePart({
|
|
112
|
+
zip,
|
|
113
|
+
path: contentTypes.name,
|
|
114
|
+
xml: prunedContentTypes,
|
|
115
|
+
compressionLevel
|
|
116
|
+
});
|
|
117
|
+
return repair;
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
120
|
+
export { isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries };
|
|
@@ -9,9 +9,9 @@ import { consolidateParagraphContent } from "./runConsolidator.js";
|
|
|
9
9
|
import { parseRun, parseRunProperties } from "./runParser.js";
|
|
10
10
|
import { parseSdtProperties } from "./sdtProperties.js";
|
|
11
11
|
import { parseSectionProperties } from "./sectionParser.js";
|
|
12
|
-
import { elementToXml, findChild, findChildren, getAttribute, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
|
|
12
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, elementToXml, findChild, findChildByNamespaceUri, findChildren, getAttribute, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
|
|
13
13
|
import { panic } from "better-result";
|
|
14
|
-
import { normalizeRevisionId } from "@stll/docx-core/model";
|
|
14
|
+
import { PARAGRAPH_MARK_CHANGE_KINDS, normalizeRevisionId } from "@stll/docx-core/model";
|
|
15
15
|
//#region src/docx/paragraphParser.ts
|
|
16
16
|
/**
|
|
17
17
|
* Extract plain text from a math element (recursive text content extraction)
|
|
@@ -528,19 +528,16 @@ function parseParagraphMarkChange(pPr) {
|
|
|
528
528
|
if (!pPr) return;
|
|
529
529
|
const rPr = findChild(pPr, "w", "rPr");
|
|
530
530
|
if (!rPr) return;
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
kind: "del",
|
|
539
|
-
info: parseTrackedChangeInfo(del)
|
|
540
|
-
};
|
|
531
|
+
for (const kind of PARAGRAPH_MARK_CHANGE_KINDS) {
|
|
532
|
+
const element = findChildByNamespaceUri(rPr, WORDPROCESSINGML_NAMESPACE_URIS, kind);
|
|
533
|
+
if (element) return {
|
|
534
|
+
kind,
|
|
535
|
+
info: parseTrackedChangeInfo(element)
|
|
536
|
+
};
|
|
537
|
+
}
|
|
541
538
|
}
|
|
542
539
|
function isTrackedChangeWrapperChild(content) {
|
|
543
|
-
return content.type === "run" || content.type === "hyperlink";
|
|
540
|
+
return content.type === "run" || content.type === "hyperlink" || content.type === "bookmarkStart" || content.type === "bookmarkEnd" || content.type === "simpleField" || content.type === "complexField" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo";
|
|
544
541
|
}
|
|
545
542
|
function isInlineSdtContent(content) {
|
|
546
543
|
return content.type === "run" || content.type === "hyperlink" || content.type === "simpleField" || content.type === "complexField" || content.type === "inlineSdt" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo" || content.type === "mathEquation";
|
|
@@ -980,16 +977,19 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
|
|
|
980
977
|
if (sectPr) paragraph.sectionProperties = parseSectionProperties(sectPr);
|
|
981
978
|
}
|
|
982
979
|
paragraph.content = consolidateParagraphContent(parseParagraphContents(node, styles, theme, numbering, rels, media, "default", options?.rootXmlns ?? {}));
|
|
983
|
-
|
|
980
|
+
const paragraphFormatting = paragraph.formatting;
|
|
981
|
+
const directNumPr = paragraphFormatting?.numPr;
|
|
982
|
+
const styleNumPr = paragraphFormatting?.styleId && styles ? styles.get(paragraphFormatting.styleId)?.pPr?.numPr : void 0;
|
|
983
|
+
let effectiveNumPr = directNumPr;
|
|
984
984
|
let numPrFromStyle = false;
|
|
985
|
-
if (
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
985
|
+
if (paragraphFormatting && styleNumPr && directNumPr?.numId === void 0) {
|
|
986
|
+
effectiveNumPr = {
|
|
987
|
+
...styleNumPr,
|
|
988
|
+
...directNumPr
|
|
989
|
+
};
|
|
990
|
+
numPrFromStyle = true;
|
|
991
|
+
paragraphFormatting.numPr = effectiveNumPr;
|
|
992
|
+
paragraphFormatting.numPrFromStyle = styleNumPr;
|
|
993
993
|
}
|
|
994
994
|
if (effectiveNumPr && numbering) {
|
|
995
995
|
const { numId, ilvl = 0 } = effectiveNumPr;
|
|
@@ -37,7 +37,9 @@ const scanHyperlink = (hyperlink, scan) => {
|
|
|
37
37
|
};
|
|
38
38
|
const scanInlineContent = (content, scan) => {
|
|
39
39
|
for (const child of content) {
|
|
40
|
-
|
|
40
|
+
let result;
|
|
41
|
+
if (child.type === "run") result = scanRun(child, scan);
|
|
42
|
+
else if (child.type === "hyperlink") result = scanHyperlink(child, scan);
|
|
41
43
|
if (result !== void 0) return result;
|
|
42
44
|
}
|
|
43
45
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/docx/revisionIdNormalization.d.ts
|
|
2
|
+
declare const REVISION_ELEMENT_NAMES: Set<string>;
|
|
3
|
+
/**
|
|
4
|
+
* Keep physical tracked-change element ids unique across a package.
|
|
5
|
+
*
|
|
6
|
+
* Live editor marks and operation receipts keep their logical revision IDs.
|
|
7
|
+
* Saving assigns fresh IDs only where one logical change was split into
|
|
8
|
+
* multiple physical OOXML wrappers; reopening therefore exposes the physical
|
|
9
|
+
* wrapper IDs that the file format requires.
|
|
10
|
+
*/
|
|
11
|
+
declare const normalizeRevisionIdsInXmlParts: (parts: ReadonlyMap<string, string>) => Map<string, string>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { REVISION_ELEMENT_NAMES, normalizeRevisionIdsInXmlParts };
|