@stll/folio-core 0.37.5 → 0.39.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.
Files changed (144) hide show
  1. package/dist/ai-edits/apply.js +275 -88
  2. package/dist/ai-edits/clean-text.js +12 -1
  3. package/dist/ai-edits/headless.d.ts +110 -0
  4. package/dist/ai-edits/headless.js +569 -40
  5. package/dist/ai-edits/snapshot.d.ts +24 -1
  6. package/dist/ai-edits/snapshot.js +206 -9
  7. package/dist/ai-edits/table-geometry.d.ts +7 -7
  8. package/dist/ai-edits/table-geometry.js +43 -27
  9. package/dist/ai-edits/table-template.d.ts +2 -1
  10. package/dist/ai-edits/table-template.js +9 -3
  11. package/dist/ai-edits/types.d.ts +37 -15
  12. package/dist/compare/__fixtures__/body-sequence.d.ts +4 -0
  13. package/dist/compare/__fixtures__/body-sequence.js +5 -3
  14. package/dist/compare/compare.d.ts +26 -2
  15. package/dist/compare/compare.js +444 -16
  16. package/dist/compare/content-alignment.js +3 -2
  17. package/dist/compare/content-types.d.ts +34 -2
  18. package/dist/compare/content.d.ts +13 -10
  19. package/dist/compare/content.js +69 -23
  20. package/dist/compare/formatting.d.ts +4 -22
  21. package/dist/compare/formatting.js +8 -112
  22. package/dist/compare/inline-atom-resources.d.ts +6 -0
  23. package/dist/compare/inline-atom-resources.js +26 -0
  24. package/dist/compare/inline-atoms.d.ts +35 -0
  25. package/dist/compare/inline-atoms.js +383 -0
  26. package/dist/compare/inline-provenance.d.ts +36 -0
  27. package/dist/compare/inline-provenance.js +334 -0
  28. package/dist/compare/plan.d.ts +4 -1
  29. package/dist/compare/plan.js +32 -4
  30. package/dist/compare/scenario.d.ts +4 -6
  31. package/dist/compare/section-boundary-properties.d.ts +63 -0
  32. package/dist/compare/section-boundary-properties.js +235 -0
  33. package/dist/compare/style-resources.d.ts +30 -0
  34. package/dist/compare/style-resources.js +404 -0
  35. package/dist/compare/types.d.ts +50 -11
  36. package/dist/compare/types.js +3 -1
  37. package/dist/compare/verification.d.ts +3 -5
  38. package/dist/compare/verification.js +8 -39
  39. package/dist/compat/eigenpal.d.ts +4 -4
  40. package/dist/compat/eigenpal.js +3 -3
  41. package/dist/controller/layoutPipeline.js +4 -0
  42. package/dist/display-list/build/buildContext.d.ts +3 -1
  43. package/dist/display-list/build/buildDisplayList.d.ts +4 -1
  44. package/dist/display-list/build/buildDisplayList.js +38 -4
  45. package/dist/display-list/build/commentAnnotations.d.ts +10 -0
  46. package/dist/display-list/build/commentAnnotations.js +17 -0
  47. package/dist/display-list/build/paragraphPrimitives.js +39 -1
  48. package/dist/display-list/primitives.d.ts +1 -1
  49. package/dist/display-list/types.d.ts +16 -1
  50. package/dist/document-operations.d.ts +5 -3
  51. package/dist/document-operations.js +112 -5
  52. package/dist/docx/blockContentParser.js +14 -2
  53. package/dist/docx/diagramPreview.d.ts +7 -0
  54. package/dist/docx/diagramPreview.js +191 -0
  55. package/dist/docx/drawingIdNormalization.js +43 -2
  56. package/dist/docx/drawingRelationships.d.ts +10 -0
  57. package/dist/docx/drawingRelationships.js +28 -0
  58. package/dist/docx/fieldParser.js +1 -1
  59. package/dist/docx/hyperlinkParser.js +3 -5
  60. package/dist/docx/imageRawXml.d.ts +8 -0
  61. package/dist/docx/imageRawXml.js +13 -0
  62. package/dist/docx/newImage.d.ts +2 -2
  63. package/dist/docx/newImage.js +3 -3
  64. package/dist/docx/numberingParser.js +30 -27
  65. package/dist/docx/paragraphParser.js +11 -4
  66. package/dist/docx/paragraphTextBoxEnrichment.js +2 -1
  67. package/dist/docx/parser.js +13 -0
  68. package/dist/docx/removeHeaderFooterParts.d.ts +14 -0
  69. package/dist/docx/removeHeaderFooterParts.js +87 -0
  70. package/dist/docx/rezip.d.ts +3 -1
  71. package/dist/docx/rezip.js +143 -41
  72. package/dist/docx/runParser.js +24 -5
  73. package/dist/docx/sectionParser.js +3 -0
  74. package/dist/docx/sectionReferenceHistory.d.ts +17 -0
  75. package/dist/docx/sectionReferenceHistory.js +59 -0
  76. package/dist/docx/selectiveSave.js +11 -31
  77. package/dist/docx/selectiveXmlPatch.d.ts +8 -1
  78. package/dist/docx/selectiveXmlPatch.js +62 -2
  79. package/dist/docx/serializer/paragraphSerializer.js +2 -0
  80. package/dist/docx/serializer/partNamespaces.d.ts +1 -1
  81. package/dist/docx/serializer/partNamespaces.js +4 -0
  82. package/dist/docx/serializer/runSerializer.js +5 -2
  83. package/dist/docx/serializer/sectionPropertiesSerializer.js +5 -2
  84. package/dist/docx/serializer/textFormattingSerializer.d.ts +1 -1
  85. package/dist/docx/serializer/textFormattingSerializer.js +3 -1
  86. package/dist/docx/tableParser.js +21 -11
  87. package/dist/docx/textBoxParser.js +25 -5
  88. package/dist/docx/xmlParser.d.ts +10 -1
  89. package/dist/docx/xmlParser.js +23 -1
  90. package/dist/export-pdf.js +2 -0
  91. package/dist/headless-layout.js +1 -0
  92. package/dist/index.d.ts +4 -4
  93. package/dist/index.js +3 -3
  94. package/dist/internal/compare/inline-presentation.d.ts +20 -0
  95. package/dist/internal/compare/inline-presentation.js +222 -0
  96. package/dist/internal/paragraphFormattingSerialization.d.ts +3 -1
  97. package/dist/internal/paragraphFormattingSerialization.js +8 -1
  98. package/dist/internal/sectionReferenceResolution.d.ts +21 -0
  99. package/dist/internal/sectionReferenceResolution.js +72 -0
  100. package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
  101. package/dist/layout-engine/index.js +5 -5
  102. package/dist/layout-engine/sectionVerticalAlignment.d.ts +7 -0
  103. package/dist/layout-engine/sectionVerticalAlignment.js +52 -0
  104. package/dist/layout-engine/types.d.ts +3 -1
  105. package/dist/markdown/renderRuns.js +1 -0
  106. package/dist/markdown/renderTable.js +1 -0
  107. package/dist/pdf/writePdf.js +37 -2
  108. package/dist/prosemirror/attrs/index.js +55 -1
  109. package/dist/prosemirror/commands/comments.d.ts +11 -1
  110. package/dist/prosemirror/commands/comments.js +68 -6
  111. package/dist/prosemirror/commands/propertyChangeScope.d.ts +9 -3
  112. package/dist/prosemirror/commands/propertyChangeScope.js +4 -10
  113. package/dist/prosemirror/conversion/fromProseDoc.js +17 -8
  114. package/dist/prosemirror/conversion/toProseDoc.js +18 -57
  115. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
  116. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +3 -0
  117. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  118. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +39 -1
  119. package/dist/prosemirror/imageCommit.js +7 -9
  120. package/dist/prosemirror/paragraphIndentation.d.ts +21 -0
  121. package/dist/prosemirror/paragraphIndentation.js +72 -0
  122. package/dist/prosemirror/plugins/documentNumbering.d.ts +3 -1
  123. package/dist/prosemirror/plugins/documentNumbering.js +7 -1
  124. package/dist/prosemirror/plugins/documentStyles.d.ts +3 -1
  125. package/dist/prosemirror/plugins/documentStyles.js +7 -1
  126. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +3 -1
  127. package/dist/prosemirror/rebaseParagraphRunFormatting.js +2 -1
  128. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +15 -2
  129. package/dist/prosemirror/runFormattingInlineCarriers.js +33 -3
  130. package/dist/prosemirror/runStyleFormatting.d.ts +15 -2
  131. package/dist/prosemirror/runStyleFormatting.js +76 -2
  132. package/dist/prosemirror/schema/marks.d.ts +3 -1
  133. package/dist/prosemirror/schema/marks.js +2 -0
  134. package/dist/prosemirror/schema/nodes.d.ts +5 -0
  135. package/dist/prosemirror/tableCellRevisionVisibility.d.ts +6 -0
  136. package/dist/prosemirror/tableCellRevisionVisibility.js +14 -0
  137. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
  138. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
  139. package/dist/redline.js +15 -2
  140. package/dist/server.d.ts +2 -2
  141. package/dist/server.js +2 -2
  142. package/dist/version-comparison.d.ts +2 -0
  143. package/dist/version-comparison.js +7 -2
  144. package/package.json +2 -2
@@ -357,6 +357,19 @@ type DisplayLinkTarget = {
357
357
  readonly pageIndex: number;
358
358
  readonly yPx: number;
359
359
  };
360
+ /**
361
+ * One DOCX comment whose anchor reaches this page.
362
+ *
363
+ * A range may cross run and line boundaries, so every painted rectangle is
364
+ * retained. Raster backends use those rectangles for the highlight; PDF uses
365
+ * the final one as the location of its single text annotation.
366
+ */
367
+ type DisplayCommentAnnotation = {
368
+ readonly commentId: number;
369
+ readonly rects: readonly DisplayRect[];
370
+ readonly contents: string;
371
+ readonly author: string;
372
+ };
360
373
  /**
361
374
  * What kind of thing a click landed in.
362
375
  *
@@ -437,6 +450,8 @@ type DisplayPage = {
437
450
  */
438
451
  readonly regions: readonly DisplayHitRegion[];
439
452
  readonly links: readonly DisplayLink[];
453
+ /** Native review comments anchored on this page. */
454
+ readonly comments: readonly DisplayCommentAnnotation[];
440
455
  };
441
456
  /** One heading, for a document outline. `pageIndex` is 0-based. */
442
457
  type DisplayOutlineEntry = {
@@ -473,4 +488,4 @@ type DisplayList = {
473
488
  readonly unsupported: readonly DisplayUnsupported[];
474
489
  };
475
490
  //#endregion
476
- export { DisplayClipGroup, DisplayColor, DisplayEmbeddedFont, DisplayFontFace, DisplayFontRef, DisplayGlyphRun, DisplayHitRegion, DisplayHitRegionKind, DisplayHitRegionModel, DisplayImagePrimitive, DisplayImageRef, DisplayImageSource, DisplayLine, DisplayLink, DisplayLinkTarget, DisplayList, DisplayMetadata, DisplayModelRange, DisplayOpacityGroup, DisplayOutlineEntry, DisplayPage, DisplayPrimitive, DisplayRect, DisplayRectPrimitive, DisplayRotateGroup, DisplayRunAdjustments, DisplayStoryRef, DisplayStroke, DisplayStrokePattern, DisplayUnsupported };
491
+ export { DisplayClipGroup, DisplayColor, DisplayCommentAnnotation, DisplayEmbeddedFont, DisplayFontFace, DisplayFontRef, DisplayGlyphRun, DisplayHitRegion, DisplayHitRegionKind, DisplayHitRegionModel, DisplayImagePrimitive, DisplayImageRef, DisplayImageSource, DisplayLine, DisplayLink, DisplayLinkTarget, DisplayList, DisplayMetadata, DisplayModelRange, DisplayOpacityGroup, DisplayOutlineEntry, DisplayPage, DisplayPrimitive, DisplayRect, DisplayRectPrimitive, DisplayRotateGroup, DisplayRunAdjustments, DisplayStoryRef, DisplayStroke, DisplayStrokePattern, DisplayUnsupported };
@@ -7,6 +7,8 @@ declare const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION: 1;
7
7
  declare const FOLIO_PARAGRAPH_ALIGNMENT_VALUES: readonly ("left" | "center" | "right" | "both" | "distribute" | "mediumKashida" | "highKashida" | "lowKashida" | "thaiDistribute")[];
8
8
  /** `w:spacing/@w:lineRule` values accepted by the operation contract. */
9
9
  declare const FOLIO_LINE_SPACING_RULE_VALUES: readonly ("auto" | "exact" | "atLeast")[];
10
+ /** `w:br/@w:clear` values accepted on an authored hard page break. */
11
+ declare const FOLIO_PAGE_BREAK_CLEAR_VALUES: readonly ["none", "left", "right", "all"];
10
12
  declare const FOLIO_DOCUMENT_OPERATION_TYPES: readonly ["replaceInBlock", "replaceRange", "commentOnRange", "formatRange", "insertAfterBlock", "insertBeforeBlock", "replaceBlock", "deleteBlock", "splitBlock", "mergeBlockWithNext", "setBlockParagraphProperties", "insertTable", "deleteTable", "commentOnBlock", "insertSignatureTable", "insertTableRow", "deleteTableRow", "insertTableColumn", "deleteTableColumn", "mergeTableCells", "splitTableCell"];
11
13
  declare const FOLIO_DOCUMENT_OPERATION_MODES: readonly ["direct", "tracked-changes", "suggested"];
12
14
  declare const FOLIO_DOCUMENT_OPERATION_STORIES: readonly ["main", "header", "footer", "footnote", "endnote"];
@@ -97,8 +99,8 @@ declare const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE: Readonly<{
97
99
  readonly replaceRange: readonly ["id", "type", "range", "severity", "area", "precondition", "suggestionId", "replace", "comment"];
98
100
  readonly commentOnRange: readonly ["id", "type", "range", "severity", "area", "precondition", "comment"];
99
101
  readonly formatRange: readonly ["id", "type", "range", "severity", "area", "precondition", "suggestionId", "formatting"];
100
- readonly insertAfterBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
101
- readonly insertBeforeBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
102
+ readonly insertAfterBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "indentation", "lineBreakMode", "listLevel", "numbering", "moveId", "pageBreakBefore", "hardPageBreak", "styleId", "comment"];
103
+ readonly insertBeforeBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "indentation", "lineBreakMode", "listLevel", "numbering", "moveId", "pageBreakBefore", "hardPageBreak", "styleId", "comment"];
102
104
  readonly replaceBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "preserveFormatting", "styleId", "comment"];
103
105
  readonly deleteBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "moveId", "comment"];
104
106
  readonly splitBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "offset", "separator", "firstParagraphProperties", "secondParagraphProperties"];
@@ -282,4 +284,4 @@ type ApplyFolioDocumentOperationsOptions = {
282
284
  };
283
285
  declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
284
286
  //#endregion
285
- export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
287
+ export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -7,6 +7,13 @@ const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION = 1;
7
7
  const FOLIO_PARAGRAPH_ALIGNMENT_VALUES = Object.freeze([...PARAGRAPH_ALIGNMENT_VALUES]);
8
8
  /** `w:spacing/@w:lineRule` values accepted by the operation contract. */
9
9
  const FOLIO_LINE_SPACING_RULE_VALUES = Object.freeze([...LINE_SPACING_RULE_VALUES]);
10
+ /** `w:br/@w:clear` values accepted on an authored hard page break. */
11
+ const FOLIO_PAGE_BREAK_CLEAR_VALUES = Object.freeze([
12
+ "none",
13
+ "left",
14
+ "right",
15
+ "all"
16
+ ]);
10
17
  const FOLIO_DOCUMENT_OPERATION_TYPES = Object.freeze([
11
18
  "replaceInBlock",
12
19
  "replaceRange",
@@ -242,6 +249,51 @@ const readClearableParagraphSpacing = ({ value, key, path }) => {
242
249
  }
243
250
  return spacing;
244
251
  };
252
+ const PARAGRAPH_INDENTATION_KEYS = [
253
+ "indentLeft",
254
+ "indentRight",
255
+ "indentFirstLine",
256
+ "hangingIndent"
257
+ ];
258
+ /** Read one complete direct `w:ind` cluster, preserving zero and false. */
259
+ const readClearableParagraphIndentation = ({ value, key, path }) => {
260
+ const candidate = value[key];
261
+ if (candidate === void 0 || candidate === null) return candidate;
262
+ const indentationPath = `${path}.${key}`;
263
+ if (!isPlainObject(candidate)) return invalidBatch(indentationPath, "expected an object or null when provided");
264
+ assertAllowedKeys(candidate, indentationPath, PARAGRAPH_INDENTATION_KEYS);
265
+ if (Object.keys(candidate).length === 0) return invalidBatch(indentationPath, "expected at least one indentation property");
266
+ const indentation = {};
267
+ for (const numberKey of [
268
+ "indentLeft",
269
+ "indentRight",
270
+ "indentFirstLine"
271
+ ]) {
272
+ const number = candidate[numberKey];
273
+ if (number === void 0) continue;
274
+ if (typeof number !== "number" || !Number.isSafeInteger(number)) return invalidBatch(`${indentationPath}.${numberKey}`, "expected a safe integer when provided");
275
+ indentation[numberKey] = number;
276
+ }
277
+ const hangingIndent = candidate["hangingIndent"];
278
+ if (hangingIndent !== void 0) {
279
+ if (typeof hangingIndent !== "boolean") return invalidBatch(`${indentationPath}.hangingIndent`, "expected a boolean when provided");
280
+ indentation.hangingIndent = hangingIndent;
281
+ }
282
+ return indentation;
283
+ };
284
+ const readClearableNumbering = ({ value, key, path }) => {
285
+ const candidate = value[key];
286
+ if (candidate === void 0 || candidate === null) return candidate;
287
+ const numberingPath = `${path}.${key}`;
288
+ if (!isPlainObject(candidate)) return invalidBatch(numberingPath, "expected an object or null when provided");
289
+ assertAllowedKeys(candidate, numberingPath, ["numId", "level"]);
290
+ const numId = readNonNegativeInteger(candidate, "numId", numberingPath);
291
+ if (numId === 0) return invalidBatch(`${numberingPath}.numId`, "expected a positive integer");
292
+ return {
293
+ numId,
294
+ level: readNonNegativeInteger(candidate, "level", numberingPath)
295
+ };
296
+ };
245
297
  /**
246
298
  * A rectangular grid of cell texts. Rectangular because a table whose rows
247
299
  * hold different cell counts is not a table any consumer can lay out, and the
@@ -269,11 +321,18 @@ const readParagraphProperties = ({ value, key, path }) => {
269
321
  assertAllowedKeys(candidate, propertiesPath, [
270
322
  "styleId",
271
323
  "listLevel",
324
+ "numbering",
272
325
  "alignment",
273
- "spacing"
326
+ "spacing",
327
+ "indentation"
274
328
  ]);
275
329
  const styleId = candidate["styleId"] === null ? null : readOptionalString(candidate, "styleId", propertiesPath);
276
330
  const listLevel = readClearableNonNegativeInteger(candidate, "listLevel", propertiesPath);
331
+ const numbering = readClearableNumbering({
332
+ value: candidate,
333
+ key: "numbering",
334
+ path: propertiesPath
335
+ });
277
336
  const alignment = readClearableParagraphAlignment({
278
337
  value: candidate,
279
338
  key: "alignment",
@@ -284,12 +343,19 @@ const readParagraphProperties = ({ value, key, path }) => {
284
343
  key: "spacing",
285
344
  path: propertiesPath
286
345
  });
287
- if (styleId === void 0 && listLevel === void 0 && alignment === void 0 && spacing === void 0) return invalidBatch(propertiesPath, "expected at least one property to set");
346
+ const indentation = readClearableParagraphIndentation({
347
+ value: candidate,
348
+ key: "indentation",
349
+ path: propertiesPath
350
+ });
351
+ if (styleId === void 0 && listLevel === void 0 && numbering === void 0 && alignment === void 0 && spacing === void 0 && indentation === void 0) return invalidBatch(propertiesPath, "expected at least one property to set");
288
352
  return {
289
353
  ...styleId !== void 0 && { styleId },
290
354
  ...listLevel !== void 0 && { listLevel },
355
+ ...numbering !== void 0 && { numbering },
291
356
  ...alignment !== void 0 && { alignment },
292
- ...spacing !== void 0 && { spacing }
357
+ ...spacing !== void 0 && { spacing },
358
+ ...indentation !== void 0 && { indentation }
293
359
  };
294
360
  };
295
361
  const readTextRange = (value, path) => {
@@ -430,9 +496,13 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
430
496
  "inheritFormatting",
431
497
  "alignment",
432
498
  "spacing",
499
+ "indentation",
500
+ "lineBreakMode",
433
501
  "listLevel",
502
+ "numbering",
434
503
  "moveId",
435
504
  "pageBreakBefore",
505
+ "hardPageBreak",
436
506
  "styleId",
437
507
  "comment"
438
508
  ],
@@ -442,9 +512,13 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
442
512
  "inheritFormatting",
443
513
  "alignment",
444
514
  "spacing",
515
+ "indentation",
516
+ "lineBreakMode",
445
517
  "listLevel",
518
+ "numbering",
446
519
  "moveId",
447
520
  "pageBreakBefore",
521
+ "hardPageBreak",
448
522
  "styleId",
449
523
  "comment"
450
524
  ],
@@ -529,6 +603,18 @@ const parseSignatureParties = (value, path) => {
529
603
  return parsedParty;
530
604
  });
531
605
  };
606
+ const readOptionalHardPageBreak = (value, path) => {
607
+ const candidate = value["hardPageBreak"];
608
+ if (candidate === void 0) return;
609
+ if (!isPlainObject(candidate)) return invalidBatch(`${path}.hardPageBreak`, "expected an object when provided");
610
+ const hardPageBreakPath = `${path}.hardPageBreak`;
611
+ assertAllowedKeys(candidate, hardPageBreakPath, ["clear"]);
612
+ const clear = candidate["clear"];
613
+ if (clear === void 0) return {};
614
+ const validatedClear = FOLIO_PAGE_BREAK_CLEAR_VALUES.find((supported) => supported === clear);
615
+ if (validatedClear === void 0) return invalidBatch(`${hardPageBreakPath}.clear`, "expected a supported break clear value");
616
+ return { clear: validatedClear };
617
+ };
532
618
  const parseDocumentOperation = (value, index) => {
533
619
  const path = `$.operations[${index}]`;
534
620
  if (!isPlainObject(value)) return invalidBatch(path, "expected an object");
@@ -650,9 +736,15 @@ const parseDocumentOperation = (value, index) => {
650
736
  if (type === "insertAfterBlock" || type === "insertBeforeBlock") {
651
737
  const inheritFormatting = readOptionalBoolean(value, "inheritFormatting", path);
652
738
  const pageBreakBefore = readOptionalBoolean(value, "pageBreakBefore", path);
739
+ const hardPageBreak = readOptionalHardPageBreak(value, path);
653
740
  const styleId = value["styleId"] === null ? null : readOptionalString(value, "styleId", path);
654
741
  const moveId = readOptionalString(value, "moveId", path);
655
742
  const listLevel = readClearableNonNegativeInteger(value, "listLevel", path);
743
+ const numbering = readClearableNumbering({
744
+ value,
745
+ key: "numbering",
746
+ path
747
+ });
656
748
  const alignment = readClearableParagraphAlignment({
657
749
  value,
658
750
  key: "alignment",
@@ -663,18 +755,33 @@ const parseDocumentOperation = (value, index) => {
663
755
  key: "spacing",
664
756
  path
665
757
  });
758
+ const indentation = readClearableParagraphIndentation({
759
+ value,
760
+ key: "indentation",
761
+ path
762
+ });
763
+ const lineBreakMode = value["lineBreakMode"];
764
+ if (lineBreakMode !== void 0 && lineBreakMode !== "paragraph" && lineBreakMode !== "inline") return invalidBatch(`${path}.lineBreakMode`, "expected \"paragraph\" or \"inline\" when provided");
765
+ const text = readString(value, "text", path);
766
+ if (hardPageBreak !== void 0 && text.length > 0) return invalidBatch(`${path}.text`, "expected empty text with hardPageBreak");
767
+ if (hardPageBreak !== void 0 && pageBreakBefore !== void 0) return invalidBatch(`${path}.pageBreakBefore`, "cannot combine hardPageBreak with pageBreakBefore");
768
+ if (hardPageBreak !== void 0 && lineBreakMode !== void 0) return invalidBatch(`${path}.lineBreakMode`, "cannot combine hardPageBreak with lineBreakMode");
666
769
  return {
667
770
  ...operationMeta,
668
771
  id,
669
772
  type,
670
773
  blockId,
671
- text: readString(value, "text", path),
774
+ text,
672
775
  ...inheritFormatting !== void 0 && { inheritFormatting },
673
776
  ...alignment !== void 0 && { alignment },
674
777
  ...spacing !== void 0 && { spacing },
778
+ ...indentation !== void 0 && { indentation },
779
+ ...lineBreakMode !== void 0 && { lineBreakMode },
675
780
  ...listLevel !== void 0 && { listLevel },
781
+ ...numbering !== void 0 && { numbering },
676
782
  ...moveId !== void 0 && { moveId },
677
783
  ...pageBreakBefore !== void 0 && { pageBreakBefore },
784
+ ...hardPageBreak !== void 0 && { hardPageBreak },
678
785
  ...styleId !== void 0 && { styleId },
679
786
  ...comment !== void 0 && { comment }
680
787
  };
@@ -1103,4 +1210,4 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
1103
1210
  };
1104
1211
  };
1105
1212
  //#endregion
1106
- export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
1213
+ export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PAGE_BREAK_CLEAR_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -7,7 +7,7 @@ import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
7
7
  import { parseSdtProperties } from "./sdtProperties.js";
8
8
  import { parseTable } from "./tableParser.js";
9
9
  import { captureVerbatimXml } from "./verbatimCapture.js";
10
- import { findChild, getChildElements, getLocalName, mergeXmlnsDeclarations } from "./xmlParser.js";
10
+ import { findChild, getChildElements, getLocalName, mergeXmlnsDeclarations, selectAlternateContentBranch } from "./xmlParser.js";
11
11
  //#region src/docx/blockContentParser.ts
12
12
  const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
13
13
  const listRendering = paragraph.listRendering;
@@ -80,6 +80,13 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
80
80
  }
81
81
  listRendering.marker = computedMarker;
82
82
  };
83
+ const withContainerXmlns = (state, element) => ({
84
+ ...state,
85
+ options: {
86
+ ...state.options,
87
+ rootXmlns: mergeXmlnsDeclarations(state.options?.rootXmlns ?? {}, element)
88
+ }
89
+ });
83
90
  const parseBlockContent = (parent, styles, theme, numbering, rels, media, options) => parseBlockContentWithState(parent, styles, theme, numbering, rels, media, {
84
91
  listCounters: /* @__PURE__ */ new Map(),
85
92
  abstractCounters: /* @__PURE__ */ new Map(),
@@ -132,12 +139,17 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
132
139
  const blockSdt = {
133
140
  type: "blockSdt",
134
141
  properties,
135
- content: sdtContent ? parseBlockContentWithState(sdtContent, styles, theme, numbering, rels, media, state) : []
142
+ content: sdtContent ? parseBlockContentWithState(sdtContent, styles, theme, numbering, rels, media, withContainerXmlns(withContainerXmlns(state, child), sdtContent)) : []
136
143
  };
137
144
  if (prependBookmarkMarkersToFirstParagraphInBlocks(blockSdt.content, pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
138
145
  content.push(blockSdt);
139
146
  continue;
140
147
  }
148
+ if (localName === "AlternateContent") {
149
+ const selectedBranch = selectAlternateContentBranch(child);
150
+ if (selectedBranch) content.push(...parseBlockContentWithState(selectedBranch, styles, theme, numbering, rels, media, withContainerXmlns(withContainerXmlns(state, child), selectedBranch)));
151
+ continue;
152
+ }
141
153
  if (localName === "bookmarkStart" || localName === "bookmarkEnd") {
142
154
  const marker = parseBookmarkMarker(child, localName);
143
155
  if (!appendBookmarkMarkerToLastParagraphInBlocks(content, marker)) pendingBookmarkMarkers.push(marker);
@@ -0,0 +1,7 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { XmlElement } from "./xmlParser.js";
3
+ //#region src/docx/diagramPreview.d.ts
4
+ /** Create a deliberately simple, bounded preview; it is never an editable diagram projection. */
5
+ declare const parseDiagramPreview: (drawing: XmlElement, rels: document_d_exports.RelationshipMap | undefined, media: Map<string, document_d_exports.MediaFile> | undefined) => document_d_exports.Image | null;
6
+ //#endregion
7
+ export { parseDiagramPreview };
@@ -0,0 +1,191 @@
1
+ import { resolveRelativePath } from "./relsParser.js";
2
+ import { findChildByNamespaceUri, getAttribute, getLocalName, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
3
+ //#region src/docx/diagramPreview.ts
4
+ const MAX_PREVIEW_SHAPES = 128;
5
+ const MAX_PREVIEW_PIXELS = 144e4;
6
+ const MAX_PREVIEW_PAINT_PIXELS = MAX_PREVIEW_PIXELS * 4;
7
+ const DRAWINGML_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/drawingml/2006/main", "http://purl.oclc.org/ooxml/drawingml/main"]);
8
+ const DIAGRAM_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/drawingml/2006/diagram", "http://purl.oclc.org/ooxml/drawingml/diagram"]);
9
+ const DIAGRAM_DRAWING_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.microsoft.com/office/drawing/2008/diagram"]);
10
+ const WORD_DRAWING_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", "http://purl.oclc.org/ooxml/drawingml/wordprocessingDrawing"]);
11
+ const crc32 = (bytes) => {
12
+ let crc = 4294967295;
13
+ for (const byte of bytes) {
14
+ crc ^= byte;
15
+ for (let bit = 0; bit < 8; bit += 1) crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
16
+ }
17
+ return (crc ^ 4294967295) >>> 0;
18
+ };
19
+ const adler32 = (bytes) => {
20
+ let a = 1;
21
+ let b = 0;
22
+ for (const byte of bytes) {
23
+ a = (a + byte) % 65521;
24
+ b = (b + a) % 65521;
25
+ }
26
+ return b << 16 | a;
27
+ };
28
+ const pngChunk = (type, data) => {
29
+ const name = new TextEncoder().encode(type);
30
+ const output = new Uint8Array(12 + data.length);
31
+ const view = new DataView(output.buffer);
32
+ view.setUint32(0, data.length);
33
+ output.set(name, 4);
34
+ output.set(data, 8);
35
+ view.setUint32(8 + data.length, crc32(output.subarray(4, 8 + data.length)));
36
+ return output;
37
+ };
38
+ const previewPng = (width, height, shapes) => {
39
+ const scale = Math.min(1, Math.sqrt(MAX_PREVIEW_PIXELS / (width * height)));
40
+ const w = Math.max(1, Math.round(width * scale));
41
+ const h = Math.max(1, Math.round(height * scale));
42
+ const pixels = new Uint8Array((w * 4 + 1) * h);
43
+ for (let y = 0; y < h; y += 1) {
44
+ const row = y * (w * 4 + 1);
45
+ pixels[row] = 0;
46
+ for (let x = 0; x < w; x += 1) {
47
+ const index = row + 1 + x * 4;
48
+ pixels[index] = 238;
49
+ pixels[index + 1] = 242;
50
+ pixels[index + 2] = 247;
51
+ pixels[index + 3] = 255;
52
+ }
53
+ }
54
+ let paintedPixels = 0;
55
+ for (const shape of shapes) {
56
+ const sx = Math.max(0, Math.round(shape.x / width * w));
57
+ const sy = Math.max(0, Math.round(shape.y / height * h));
58
+ const ex = Math.min(w, Math.round((shape.x + shape.width) / width * w));
59
+ const ey = Math.min(h, Math.round((shape.y + shape.height) / height * h));
60
+ const red = Number.parseInt(shape.color.slice(0, 2), 16) || 232;
61
+ const green = Number.parseInt(shape.color.slice(2, 4), 16) || 238;
62
+ const blue = Number.parseInt(shape.color.slice(4, 6), 16) || 247;
63
+ const shapePixels = Math.max(0, ex - sx) * Math.max(0, ey - sy);
64
+ if (paintedPixels + shapePixels > MAX_PREVIEW_PAINT_PIXELS) break;
65
+ paintedPixels += shapePixels;
66
+ for (let y = sy; y < ey; y += 1) {
67
+ const row = y * (w * 4 + 1);
68
+ for (let x = sx; x < ex; x += 1) {
69
+ const index = row + 1 + x * 4;
70
+ pixels[index] = red;
71
+ pixels[index + 1] = green;
72
+ pixels[index + 2] = blue;
73
+ }
74
+ }
75
+ }
76
+ const compressed = new Uint8Array(2 + pixels.length + Math.ceil(pixels.length / 65535) * 5 + 4);
77
+ compressed[0] = 120;
78
+ compressed[1] = 1;
79
+ let cursor = 2;
80
+ for (let offset = 0; offset < pixels.length;) {
81
+ const length = Math.min(65535, pixels.length - offset);
82
+ compressed[cursor++] = offset + length === pixels.length ? 1 : 0;
83
+ compressed[cursor++] = length & 255;
84
+ compressed[cursor++] = length >>> 8;
85
+ compressed[cursor++] = ~length & 255;
86
+ compressed[cursor++] = ~length >>> 8 & 255;
87
+ compressed.set(pixels.subarray(offset, offset + length), cursor);
88
+ cursor += length;
89
+ offset += length;
90
+ }
91
+ const output = new Uint8Array(cursor + 4);
92
+ output.set(compressed.subarray(0, cursor));
93
+ new DataView(output.buffer).setUint32(cursor, adler32(pixels));
94
+ const signature = new Uint8Array([
95
+ 137,
96
+ 80,
97
+ 78,
98
+ 71,
99
+ 13,
100
+ 10,
101
+ 26,
102
+ 10
103
+ ]);
104
+ const header = /* @__PURE__ */ new Uint8Array(13);
105
+ new DataView(header.buffer).setUint32(0, w);
106
+ new DataView(header.buffer).setUint32(4, h);
107
+ header[8] = 8;
108
+ header[9] = 6;
109
+ const chunks = [
110
+ signature,
111
+ pngChunk("IHDR", header),
112
+ pngChunk("IDAT", output),
113
+ pngChunk("IEND", /* @__PURE__ */ new Uint8Array())
114
+ ];
115
+ const png = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.length, 0));
116
+ let position = 0;
117
+ for (const chunk of chunks) {
118
+ png.set(chunk, position);
119
+ position += chunk.length;
120
+ }
121
+ return png;
122
+ };
123
+ const extent = (drawing) => {
124
+ const value = findChildByNamespaceUri(findChildByNamespaceUri(drawing, WORD_DRAWING_NAMESPACE_URIS, "inline") ?? findChildByNamespaceUri(drawing, WORD_DRAWING_NAMESPACE_URIS, "anchor"), WORD_DRAWING_NAMESPACE_URIS, "extent");
125
+ return {
126
+ width: parseNumericAttribute(value, null, "cx") ?? 0,
127
+ height: parseNumericAttribute(value, null, "cy") ?? 0
128
+ };
129
+ };
130
+ const cachedDiagramShapes = (rels, media) => {
131
+ const matches = [...rels.values()].filter((relationship) => relationship.type === "http://schemas.microsoft.com/office/2007/relationships/diagramDrawing");
132
+ if (matches.length !== 1 || !matches[0]?.target) return [];
133
+ const path = resolveRelativePath("word/document.xml", matches[0].target);
134
+ const file = media.get(path);
135
+ if (!file?.data) return [];
136
+ const root = parseXmlDocument(new TextDecoder().decode(file.data));
137
+ if (!root) return [];
138
+ const shapes = [];
139
+ for (const shape of descendantsByNamespace(root, DIAGRAM_DRAWING_NAMESPACE_URIS, "sp").slice(0, MAX_PREVIEW_SHAPES)) {
140
+ const properties = findChildByNamespaceUri(shape, DIAGRAM_DRAWING_NAMESPACE_URIS, "spPr");
141
+ const transform = findChildByNamespaceUri(properties, DRAWINGML_NAMESPACE_URIS, "xfrm");
142
+ const offset = findChildByNamespaceUri(transform, DRAWINGML_NAMESPACE_URIS, "off");
143
+ const size = findChildByNamespaceUri(transform, DRAWINGML_NAMESPACE_URIS, "ext");
144
+ const width = parseNumericAttribute(size, null, "cx") ?? 0;
145
+ const height = parseNumericAttribute(size, null, "cy") ?? 0;
146
+ if (width <= 0 || height <= 0) continue;
147
+ const color = findChildByNamespaceUri(findChildByNamespaceUri(properties, DRAWINGML_NAMESPACE_URIS, "solidFill"), DRAWINGML_NAMESPACE_URIS, "srgbClr");
148
+ shapes.push({
149
+ x: parseNumericAttribute(offset, null, "x") ?? 0,
150
+ y: parseNumericAttribute(offset, null, "y") ?? 0,
151
+ width,
152
+ height,
153
+ color: getAttribute(color, null, "val") ?? "E8EEF7"
154
+ });
155
+ }
156
+ return shapes;
157
+ };
158
+ const descendantsByNamespace = (root, namespaceUris, localName) => {
159
+ const result = [];
160
+ const visit = (element) => {
161
+ if (element.namespaceUri && namespaceUris.has(element.namespaceUri) && getLocalName(element.name ?? "") === localName) result.push(element);
162
+ for (const child of element.elements ?? []) if (child.type === "element") visit(child);
163
+ };
164
+ visit(root);
165
+ return result;
166
+ };
167
+ /** Create a deliberately simple, bounded preview; it is never an editable diagram projection. */
168
+ const parseDiagramPreview = (drawing, rels, media) => {
169
+ if (!rels || !media) return null;
170
+ const graphicData = descendantsByNamespace(drawing, DRAWINGML_NAMESPACE_URIS, "graphicData").at(0);
171
+ if (!graphicData || !DIAGRAM_NAMESPACE_URIS.has(getAttribute(graphicData, null, "uri") ?? "")) return null;
172
+ const { width, height } = extent(drawing);
173
+ if (width <= 0 || height <= 0) return null;
174
+ const png = previewPng(width, height, cachedDiagramShapes(rels, media));
175
+ let binary = "";
176
+ for (let offset = 0; offset < png.length; offset += 32768) binary += String.fromCodePoint(...png.subarray(offset, offset + 32768));
177
+ return {
178
+ type: "image",
179
+ rId: "",
180
+ src: `data:image/png;base64,${btoa(binary)}`,
181
+ mimeType: "image/png",
182
+ filename: "smartart-preview.png",
183
+ size: {
184
+ width,
185
+ height
186
+ },
187
+ wrap: { type: "inline" }
188
+ };
189
+ };
190
+ //#endregion
191
+ export { parseDiagramPreview };
@@ -1,6 +1,27 @@
1
+ import { isNewDataUrlDrawing } from "./newImage.js";
1
2
  import { visitDocxParagraphs, visitParagraphRuns } from "./paragraphTraversal.js";
3
+ import { toTransitionalNamespaceUri } from "./transitionalSpelling.js";
4
+ import { captureVerbatimXml } from "./verbatimCapture.js";
5
+ import { NAMESPACES, OOXML_NAMESPACE_SCOPE, getLocalName, getNamespaceUri, parseXml } from "./xmlParser.js";
6
+ import { panic } from "better-result";
2
7
  //#region src/docx/drawingIdNormalization.ts
3
8
  const GENERATED_DRAWING_ID_START = 1e5;
9
+ const isDetachedRawDrawing = (drawing) => drawing?.rawXml !== void 0 && isNewDataUrlDrawing(drawing);
10
+ const reassignRawDrawingId = ({ xml, id }) => {
11
+ const root = parseXml(xml, OOXML_NAMESPACE_SCOPE);
12
+ let foundDocPr = false;
13
+ const visit = (element) => {
14
+ if (toTransitionalNamespaceUri(getNamespaceUri(element) ?? "") === NAMESPACES.wp && getLocalName(element.name) === "docPr" || toTransitionalNamespaceUri(getNamespaceUri(element) ?? "") === NAMESPACES.pic && getLocalName(element.name) === "cNvPr") {
15
+ for (const name of Object.keys(element.attributes ?? {})) if (getLocalName(name) === "id" && element.attributes) {
16
+ element.attributes[name] = id;
17
+ if (toTransitionalNamespaceUri(getNamespaceUri(element) ?? "") === NAMESPACES.wp && getLocalName(element.name) === "docPr") foundDocPr = true;
18
+ }
19
+ }
20
+ for (const child of element.elements ?? []) visit(child);
21
+ };
22
+ visit(root);
23
+ return foundDocPr ? (root.elements ?? []).map(captureVerbatimXml).join("") : null;
24
+ };
4
25
  const needsGeneratedId = ({ id }) => id === void 0 || id === "" || id === "0";
5
26
  const normalizeDrawingIds = (surfaces) => {
6
27
  const entries = [];
@@ -16,12 +37,13 @@ const normalizeDrawingIds = (surfaces) => {
16
37
  }
17
38
  if (content.type === "drawing") entries.push({
18
39
  drawing: content.image,
19
- generateWhenMissing: content.rawXml === void 0
40
+ generateWhenMissing: content.rawXml === void 0,
41
+ ...content.rawXml !== void 0 ? { rawDrawing: content } : {}
20
42
  });
21
43
  }
22
44
  });
23
45
  });
24
- const usedIds = new Set(entries.flatMap(({ drawing }) => needsGeneratedId(drawing) ? [] : [drawing.id]));
46
+ const usedIds = new Set(entries.flatMap(({ drawing, rawDrawing }) => needsGeneratedId(drawing) || isDetachedRawDrawing(rawDrawing) ? [] : [drawing.id]));
25
47
  let nextId = GENERATED_DRAWING_ID_START;
26
48
  for (const { drawing, generateWhenMissing } of entries) {
27
49
  if (!generateWhenMissing || !needsGeneratedId(drawing)) continue;
@@ -30,6 +52,25 @@ const normalizeDrawingIds = (surfaces) => {
30
52
  usedIds.add(drawing.id);
31
53
  nextId += 1;
32
54
  }
55
+ for (const { drawing, rawDrawing } of entries) {
56
+ if (!isDetachedRawDrawing(rawDrawing)) continue;
57
+ if (!needsGeneratedId(drawing) && !usedIds.has(drawing.id)) {
58
+ usedIds.add(drawing.id);
59
+ continue;
60
+ }
61
+ while (usedIds.has(String(nextId))) nextId += 1;
62
+ const sourceXml = rawDrawing.rawXml;
63
+ if (sourceXml === void 0) panic("Detached raw drawing must retain its XML.");
64
+ const rawXml = reassignRawDrawingId({
65
+ xml: sourceXml,
66
+ id: String(nextId)
67
+ });
68
+ if (rawXml === null) panic("Detached raw drawing must contain wp:docPr.");
69
+ drawing.id = String(nextId);
70
+ rawDrawing.rawXml = rawXml;
71
+ usedIds.add(drawing.id);
72
+ nextId += 1;
73
+ }
33
74
  };
34
75
  //#endregion
35
76
  export { normalizeDrawingIds };
@@ -0,0 +1,10 @@
1
+ //#region src/docx/drawingRelationships.d.ts
2
+ type RebindDrawingImageRelationshipOptions = {
3
+ xml: string;
4
+ previousId: string;
5
+ nextId: string;
6
+ };
7
+ /** Only a single embedded image can travel without importing other package resources. */
8
+ declare const rebindDrawingImageRelationship: ({ xml, previousId, nextId }: RebindDrawingImageRelationshipOptions) => string | null;
9
+ //#endregion
10
+ export { rebindDrawingImageRelationship };
@@ -0,0 +1,28 @@
1
+ import { captureVerbatimXml } from "./verbatimCapture.js";
2
+ import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, findAttributeByNamespaceUri, getLocalName, getNamespaceUri, parseXml } from "./xmlParser.js";
3
+ //#region src/docx/drawingRelationships.ts
4
+ /** Only a single embedded image can travel without importing other package resources. */
5
+ const rebindDrawingImageRelationship = ({ xml, previousId, nextId }) => {
6
+ const root = parseXml(xml, OOXML_NAMESPACE_SCOPE);
7
+ let embeddedCount = 0;
8
+ let unsupported = false;
9
+ const visit = (element) => {
10
+ for (const name of Object.keys(element.attributes ?? {})) {
11
+ const localName = getLocalName(name);
12
+ const attribute = findAttributeByNamespaceUri(element, OFFICE_RELATIONSHIP_NAMESPACE_URIS, localName);
13
+ if (!attribute || attribute.name !== name) continue;
14
+ if (!(localName === "embed" || localName === "id" && getLocalName(element.name) === "imagedata" && getNamespaceUri(element) === "urn:schemas-microsoft-com:vml") || attribute.value !== previousId) {
15
+ unsupported = true;
16
+ continue;
17
+ }
18
+ embeddedCount++;
19
+ if (element.attributes) element.attributes[name] = nextId;
20
+ }
21
+ for (const child of element.elements ?? []) visit(child);
22
+ };
23
+ visit(root);
24
+ if (unsupported || embeddedCount !== 1) return null;
25
+ return (root.elements ?? []).map(captureVerbatimXml).join("");
26
+ };
27
+ //#endregion
28
+ export { rebindDrawingImageRelationship };
@@ -209,7 +209,7 @@ function resetComplexFieldContext(ctx) {
209
209
  function finalizeComplexField(ctx) {
210
210
  return {
211
211
  type: "complexField",
212
- instruction: ctx.instruction.trim(),
212
+ instruction: ctx.instruction,
213
213
  fieldType: parseFieldType(ctx.instruction),
214
214
  fieldCode: ctx.codeRuns,
215
215
  fieldResult: ctx.resultRuns,