@stll/folio-core 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/ai-edits/apply.js +59 -19
  2. package/dist/ai-edits/headless.js +1 -1
  3. package/dist/compare/compare.js +2 -5
  4. package/dist/compare/scenario.js +2 -5
  5. package/dist/compare/types.d.ts +9 -1
  6. package/dist/compare/types.js +12 -1
  7. package/dist/compat/eigenpal.d.ts +2 -1
  8. package/dist/compat/eigenpal.js +2 -1
  9. package/dist/content-controls/checkboxDisplay.d.ts +11 -0
  10. package/dist/content-controls/checkboxDisplay.js +40 -0
  11. package/dist/content-controls/findContentControls.js +3 -1
  12. package/dist/content-controls/mutateContentControls.js +17 -2
  13. package/dist/controller/contentControlWidgetController.d.ts +1 -0
  14. package/dist/controller/contentControlWidgetController.js +3 -0
  15. package/dist/controller/hiddenEditorApi.js +3 -1
  16. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  17. package/dist/controller/hiddenEditorManager.js +6 -3
  18. package/dist/display-list/build/buildDisplayList.js +20 -1
  19. package/dist/display-list/build/imagePrimitives.d.ts +1 -1
  20. package/dist/display-list/build/imagePrimitives.js +3 -1
  21. package/dist/display-list/build/paragraphPrimitives.js +21 -2
  22. package/dist/display-list/dom/renderDisplayListToDom.js +10 -2
  23. package/dist/display-list/types.d.ts +17 -0
  24. package/dist/docx/attributeRemainder.d.ts +43 -0
  25. package/dist/docx/attributeRemainder.js +65 -0
  26. package/dist/docx/blockContentParser.d.ts +1 -1
  27. package/dist/docx/blockContentParser.js +87 -63
  28. package/dist/docx/blockPlainText.d.ts +3 -4
  29. package/dist/docx/blockPlainText.js +1 -0
  30. package/dist/docx/bookmarkPlacement.js +2 -0
  31. package/dist/docx/commentAnchorIndex.d.ts +30 -0
  32. package/dist/docx/commentAnchorIndex.js +50 -0
  33. package/dist/docx/commentParser.d.ts +1 -1
  34. package/dist/docx/commentParser.js +54 -37
  35. package/dist/docx/commentRangeIntegrity.d.ts +16 -1
  36. package/dist/docx/commentRangeIntegrity.js +42 -1
  37. package/dist/docx/commentRangeJoin.d.ts +9 -0
  38. package/dist/docx/commentRangeJoin.js +33 -0
  39. package/dist/docx/commentReferenceCompletion.d.ts +9 -0
  40. package/dist/docx/commentReferenceCompletion.js +47 -0
  41. package/dist/docx/commentReferenceNormalization.js +1 -0
  42. package/dist/docx/commentReplyMarkers.js +11 -19
  43. package/dist/docx/compatibility.js +1 -0
  44. package/dist/docx/containerChildren.d.ts +111 -0
  45. package/dist/docx/containerChildren.gen.d.ts +28 -0
  46. package/dist/docx/containerChildren.gen.js +246 -0
  47. package/dist/docx/containerChildren.js +103 -0
  48. package/dist/docx/documentParser.d.ts +1 -1
  49. package/dist/docx/documentParser.js +3 -3
  50. package/dist/docx/ensureParaIds.js +17 -7
  51. package/dist/docx/fieldParser.d.ts +1 -1
  52. package/dist/docx/fieldParser.js +4 -4
  53. package/dist/docx/fieldState.d.ts +26 -0
  54. package/dist/docx/fieldState.js +53 -0
  55. package/dist/docx/footnoteParser.d.ts +1 -1
  56. package/dist/docx/graphicFrameLocks.d.ts +16 -4
  57. package/dist/docx/graphicFrameLocks.js +19 -6
  58. package/dist/docx/headerFooterParser.js +1 -1
  59. package/dist/docx/headerFooterReferenceNormalization.js +1 -0
  60. package/dist/docx/hyperlinkParser.d.ts +27 -8
  61. package/dist/docx/hyperlinkParser.js +76 -18
  62. package/dist/docx/imageParser.js +33 -8
  63. package/dist/docx/inlineWrapperContent.d.ts +85 -0
  64. package/dist/docx/inlineWrapperContent.js +84 -0
  65. package/dist/docx/normalizeBaseDirection.js +10 -1
  66. package/dist/docx/paraIdAttribute.d.ts +21 -0
  67. package/dist/docx/paraIdAttribute.js +64 -0
  68. package/dist/docx/paragraphParser.d.ts +1 -1
  69. package/dist/docx/paragraphParser.js +277 -168
  70. package/dist/docx/paragraphPropertySource.js +5 -1
  71. package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
  72. package/dist/docx/paragraphTextBoxEnrichment.js +6 -52
  73. package/dist/docx/paragraphTraversal.js +7 -4
  74. package/dist/docx/parseWarningMessage.js +2 -1
  75. package/dist/docx/preservedRunContent.d.ts +32 -0
  76. package/dist/docx/preservedRunContent.js +86 -0
  77. package/dist/docx/renderedPageBreakNormalization.js +2 -4
  78. package/dist/docx/rezip.js +74 -18
  79. package/dist/docx/runParser.d.ts +1 -1
  80. package/dist/docx/runParser.js +16 -12
  81. package/dist/docx/sectionParser.js +8 -1
  82. package/dist/docx/selectiveXmlPatch.d.ts +46 -2
  83. package/dist/docx/selectiveXmlPatch.js +86 -39
  84. package/dist/docx/serializer/borderSerializer.js +2 -2
  85. package/dist/docx/serializer/commentSerializer.js +5 -7
  86. package/dist/docx/serializer/documentSerializer.js +8 -11
  87. package/dist/docx/serializer/headerFooterSerializer.js +7 -9
  88. package/dist/docx/serializer/noteSerializer.js +8 -8
  89. package/dist/docx/serializer/paragraphSerializer.js +76 -56
  90. package/dist/docx/serializer/runSerializer.js +25 -16
  91. package/dist/docx/serializer/sectionPropertiesSerializer.js +8 -4
  92. package/dist/docx/serializer/tableSerializer.js +18 -8
  93. package/dist/docx/server/createBilingualDocument.js +3 -1
  94. package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
  95. package/dist/docx/server/materializeYjsDocx.js +9 -1
  96. package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
  97. package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
  98. package/dist/docx/shapeParser.js +5 -5
  99. package/dist/docx/tableParser.d.ts +1 -1
  100. package/dist/docx/tableParser.js +206 -85
  101. package/dist/docx/textBoxParser.d.ts +25 -2
  102. package/dist/docx/textBoxParser.js +52 -2
  103. package/dist/docx/unzip.js +5 -4
  104. package/dist/docx/vmlImageParser.d.ts +17 -1
  105. package/dist/docx/vmlImageParser.js +17 -1
  106. package/dist/docx/xmlEncoding.d.ts +5 -0
  107. package/dist/docx/xmlEncoding.js +12 -0
  108. package/dist/docx/xmlParser.d.ts +10 -1
  109. package/dist/docx/xmlParser.js +14 -1
  110. package/dist/headless-layout.js +10 -1
  111. package/dist/index.d.ts +2 -1
  112. package/dist/index.js +2 -1
  113. package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
  114. package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
  115. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  116. package/dist/layout-engine/measure/tableCellFloating.js +2 -0
  117. package/dist/layout-engine/types.d.ts +23 -1
  118. package/dist/layout-painter/renderImage.d.ts +5 -1
  119. package/dist/layout-painter/renderImage.js +10 -3
  120. package/dist/layout-painter/renderPage.d.ts +2 -0
  121. package/dist/layout-painter/renderPage.js +4 -0
  122. package/dist/layout-painter/renderParagraph.d.ts +13 -1
  123. package/dist/layout-painter/renderParagraph.js +22 -3
  124. package/dist/managers/autoSaveCodec.d.ts +9 -1
  125. package/dist/managers/autoSaveCodec.js +12 -4
  126. package/dist/markdown/renderBlock.js +1 -0
  127. package/dist/markdown/renderRuns.d.ts +1 -1
  128. package/dist/markdown/renderRuns.js +28 -5
  129. package/dist/markdown/renderTable.js +22 -5
  130. package/dist/markdown/trailers.js +4 -1
  131. package/dist/pdf/images.js +142 -6
  132. package/dist/prosemirror/attrs/index.d.ts +14 -3
  133. package/dist/prosemirror/attrs/index.js +155 -2
  134. package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
  135. package/dist/prosemirror/authoredTransformAttrs.js +64 -0
  136. package/dist/prosemirror/commands/contentControls.js +10 -2
  137. package/dist/prosemirror/commands/image.d.ts +9 -2
  138. package/dist/prosemirror/commands/image.js +42 -28
  139. package/dist/prosemirror/commands/pageBreak.js +1 -1
  140. package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
  141. package/dist/prosemirror/commentReferenceAttrs.js +33 -0
  142. package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
  143. package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
  144. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  145. package/dist/prosemirror/conversion/fromProseDoc.js +269 -70
  146. package/dist/prosemirror/conversion/index.d.ts +2 -2
  147. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
  148. package/dist/prosemirror/conversion/toProseDoc.js +468 -308
  149. package/dist/prosemirror/extensions/StarterKit.js +8 -58
  150. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  151. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
  152. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
  153. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
  154. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
  156. package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
  157. package/dist/prosemirror/extensions/markRegistry.js +75 -0
  158. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
  159. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
  160. package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
  161. package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
  162. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
  163. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
  164. package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
  165. package/dist/prosemirror/extensions/nodes/ImageExtension.js +16 -0
  166. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
  167. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
  168. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
  169. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
  170. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +3 -0
  171. package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
  172. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +5 -1
  173. package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
  174. package/dist/prosemirror/inlineWrapperStack.js +75 -0
  175. package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
  176. package/dist/prosemirror/pageBreakRunProjection.js +19 -9
  177. package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
  178. package/dist/prosemirror/replacedAnnotations.js +165 -0
  179. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
  180. package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
  181. package/dist/prosemirror/schema/index.d.ts +3 -3
  182. package/dist/prosemirror/schema/marks.d.ts +25 -1
  183. package/dist/prosemirror/schema/nodes.d.ts +90 -3
  184. package/dist/prosemirror/schema/nodes.js +16 -1
  185. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
  186. package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
  187. package/dist/prosemirror/validation.js +16 -2
  188. package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
  189. package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
  190. package/dist/prosemirror/zeroWidthAnchors.js +2 -0
  191. package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
  192. package/dist/render-dom/commentAnchorAttributes.js +54 -0
  193. package/dist/server.d.ts +3 -1
  194. package/dist/server.js +3 -1
  195. package/dist/types/content.d.ts +2 -2
  196. package/dist/utils/clipboard.d.ts +9 -1
  197. package/dist/utils/clipboard.js +27 -1
  198. package/dist/utils/findReplace.js +1 -1
  199. package/dist/utils/imageLuminance.d.ts +15 -0
  200. package/dist/utils/imageLuminance.js +32 -0
  201. package/dist/utils/mergeDocumentContent.js +7 -17
  202. package/dist/utils/replaceText.js +1 -0
  203. package/package.json +2 -2
  204. package/dist/docx/blockRangeMarkers.d.ts +0 -36
  205. package/dist/docx/blockRangeMarkers.js +0 -59
  206. package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
  207. package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
@@ -1,7 +1,8 @@
1
- import { attachPendingRangeMarkers, attachTrailingRangeMarkers, isBlockRangeMarker } from "./blockRangeMarkers.js";
1
+ import { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
2
2
  import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
3
3
  import { appendBookmarkMarkerToLastParagraphInBlocks, appendBookmarkMarkerToLastParagraphInCells, prependBookmarkMarkersToFirstParagraphInBlocks, prependBookmarkMarkersToFirstParagraphInCell } from "./bookmarkPlacement.js";
4
4
  import { parseBorderSpec } from "./borderParser.js";
5
+ import { CAPTURE, OWNED_ELSEWHERE, dispatchChildren, withPreservedChildren } from "./containerChildren.js";
5
6
  import { parseParagraph } from "./paragraphParser.js";
6
7
  import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
7
8
  import { FloatingTableXSpecSchema, FloatingTableYSpecSchema, TableCellTextDirectionSchema, narrowEnum } from "./parserEnums.js";
@@ -10,7 +11,7 @@ import { TABLE_LOOK_FLAGS } from "./tableLook.js";
10
11
  import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChangeInfo.js";
11
12
  import { percentageSpelling, transitionalSlotEncoding } from "./transitionalSpelling.js";
12
13
  import { captureVerbatimXml } from "./verbatimCapture.js";
13
- import { cloneElement, findChild, findChildByLocalName, findChildren, getAttribute, getChildElements, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
14
+ import { cloneElement, findChild, findChildByLocalName, findChildren, getAttribute, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute, parseTableMeasurementValue, selectAlternateContentBranch } from "./xmlParser.js";
14
15
  //#region src/docx/tableParser.ts
15
16
  /**
16
17
  * Sanity cap on `w:gridSpan` (and the derived table column count). Word's
@@ -472,59 +473,87 @@ function withContainerXmlns(options, element) {
472
473
  };
473
474
  }
474
475
  function parseCellContent(tcElement, styles, theme, numbering, rels, media, options) {
475
- const content = [];
476
+ const modelled = [];
476
477
  const pendingBookmarkMarkers = [];
477
- const pendingRangeMarkers = [];
478
- const elements = getChildElements(tcElement);
479
- const parseCellChild = (child, childOptions = options) => {
480
- if (!child.name) return;
481
- const localName = getLocalName(child.name);
482
- if (localName === "p") {
483
- const para = parseParagraph(child, styles, theme, numbering, rels, media, childOptions);
484
- enrichParagraphTextBoxes(para, child, styles, theme, numbering, rels, media, parseTable);
485
- prependPendingBookmarkMarkers(para, pendingBookmarkMarkers);
486
- attachPendingRangeMarkers(para, pendingRangeMarkers);
487
- content.push(para);
488
- return;
489
- }
490
- if (localName === "tbl") {
491
- const table = parseTable(child, styles, theme, numbering, rels, media, childOptions);
492
- if (!table) return;
493
- if (prependBookmarkMarkersToFirstParagraphInBlocks([table], pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
494
- attachPendingRangeMarkers(table, pendingRangeMarkers);
495
- content.push(table);
496
- return;
497
- }
498
- if (localName === "AlternateContent") {
499
- const selectedBranch = selectAlternateContentBranch(child);
500
- if (!selectedBranch) return;
501
- const branchOptions = withContainerXmlns(withContainerXmlns(childOptions, child), selectedBranch);
502
- for (const selectedChild of getChildElements(selectedBranch)) parseCellChild(selectedChild, branchOptions);
503
- return;
504
- }
505
- if (localName === "sdt") {
506
- const sdtContent = findChildByLocalName(child, "sdtContent");
507
- if (!sdtContent) return;
508
- const sdtContentOptions = withContainerXmlns(withContainerXmlns(childOptions, child), sdtContent);
509
- for (const sdtChild of getChildElements(sdtContent)) parseCellChild(sdtChild, sdtContentOptions);
510
- return;
511
- }
512
- if (localName === "bookmarkStart" || localName === "bookmarkEnd") {
513
- const marker = parseBookmarkMarker(child, localName);
514
- if (!appendBookmarkMarkerToLastParagraphInBlocks(content, marker)) pendingBookmarkMarkers.push(marker);
515
- return;
516
- }
517
- if (isBlockRangeMarker(localName)) pendingRangeMarkers.push(captureVerbatimXml(child));
478
+ const captured = [];
479
+ const dispatchCellChildren = (element, childOptions) => {
480
+ const preserved = dispatchChildren({
481
+ element,
482
+ container: "block-content",
483
+ modelledCount: () => modelled.length,
484
+ undeclared: { AlternateContent: (child) => {
485
+ const selectedBranch = selectAlternateContentBranch(child);
486
+ if (!selectedBranch) return;
487
+ dispatchCellChildren(selectedBranch, withContainerXmlns(withContainerXmlns(childOptions, child), selectedBranch));
488
+ } },
489
+ handlers: {
490
+ p: (child) => {
491
+ const para = parseParagraph(child, styles, theme, numbering, rels, media, childOptions);
492
+ enrichParagraphTextBoxes(para, child, styles, theme, numbering, rels, media, parseTable);
493
+ prependPendingBookmarkMarkers(para, pendingBookmarkMarkers);
494
+ modelled.push(para);
495
+ },
496
+ tbl: (child) => {
497
+ const table = parseTable(child, styles, theme, numbering, rels, media, childOptions);
498
+ if (!table) return;
499
+ if (prependBookmarkMarkersToFirstParagraphInBlocks([table], pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
500
+ modelled.push(table);
501
+ },
502
+ sdt: (child) => {
503
+ const sdtContent = findChildByLocalName(child, "sdtContent");
504
+ if (!sdtContent) return;
505
+ dispatchCellChildren(sdtContent, withContainerXmlns(withContainerXmlns(childOptions, child), sdtContent));
506
+ },
507
+ bookmarkStart: (child) => {
508
+ collectCellBookmarkMarker(child, "bookmarkStart", modelled, pendingBookmarkMarkers);
509
+ },
510
+ bookmarkEnd: (child) => {
511
+ collectCellBookmarkMarker(child, "bookmarkEnd", modelled, pendingBookmarkMarkers);
512
+ },
513
+ tcPr: OWNED_ELSEWHERE,
514
+ sectPr: CAPTURE,
515
+ altChunk: CAPTURE,
516
+ commentRangeEnd: CAPTURE,
517
+ commentRangeStart: CAPTURE,
518
+ customXml: CAPTURE,
519
+ customXmlDelRangeEnd: CAPTURE,
520
+ customXmlDelRangeStart: CAPTURE,
521
+ customXmlInsRangeEnd: CAPTURE,
522
+ customXmlInsRangeStart: CAPTURE,
523
+ customXmlMoveFromRangeEnd: CAPTURE,
524
+ customXmlMoveFromRangeStart: CAPTURE,
525
+ customXmlMoveToRangeEnd: CAPTURE,
526
+ customXmlMoveToRangeStart: CAPTURE,
527
+ del: CAPTURE,
528
+ ins: CAPTURE,
529
+ moveFrom: CAPTURE,
530
+ moveFromRangeEnd: CAPTURE,
531
+ moveFromRangeStart: CAPTURE,
532
+ moveTo: CAPTURE,
533
+ moveToRangeEnd: CAPTURE,
534
+ moveToRangeStart: CAPTURE,
535
+ permEnd: CAPTURE,
536
+ permStart: CAPTURE,
537
+ proofErr: CAPTURE
538
+ }
539
+ });
540
+ if (preserved?.children) captured.push(...preserved.children);
518
541
  };
519
- for (const child of elements) parseCellChild(child);
520
- if (content.length === 0) content.push({
542
+ dispatchCellChildren(tcElement, options);
543
+ if (modelled.length === 0) modelled.push({
521
544
  type: "paragraph",
522
545
  content: [...pendingBookmarkMarkers]
523
546
  });
524
- else if (pendingBookmarkMarkers.length > 0) appendBookmarkMarkersToLastParagraphInBlocks(content, pendingBookmarkMarkers);
525
- attachTrailingRangeMarkers(content, pendingRangeMarkers);
526
- return content;
527
- }
547
+ else if (pendingBookmarkMarkers.length > 0) appendBookmarkMarkersToLastParagraphInBlocks(modelled, pendingBookmarkMarkers);
548
+ return withPreservedChildren(modelled, { children: captured }, (xml) => ({
549
+ type: "preservedBlock",
550
+ xml
551
+ }));
552
+ }
553
+ const collectCellBookmarkMarker = (child, localName, content, pending) => {
554
+ const marker = parseBookmarkMarker(child, localName);
555
+ if (!appendBookmarkMarkerToLastParagraphInBlocks(content, marker)) pending.push(marker);
556
+ };
528
557
  /**
529
558
  * Parse a table cell (w:tc)
530
559
  *
@@ -576,29 +605,79 @@ function parseTableRow(trElement, styles, theme, numbering, rels, media, options
576
605
  if (rowStructChange !== void 0) row.structuralChange = rowStructChange;
577
606
  const rowOptions = withContainerXmlns(options, trElement);
578
607
  const pendingBookmarkMarkers = [];
579
- const parseRowChild = (child, childOptions = rowOptions) => {
580
- const localName = getLocalName(child.name);
581
- if (localName === "tc") {
582
- const cell = parseTableCell(child, styles, theme, numbering, rels, media, childOptions);
583
- if (pendingBookmarkMarkers.length > 0) {
584
- prependBookmarkMarkersToFirstParagraphInCell(cell, pendingBookmarkMarkers);
585
- pendingBookmarkMarkers.length = 0;
608
+ const preservedChildren = [];
609
+ /**
610
+ * One row's children, or a row-level content control's.
611
+ *
612
+ * folio unwraps `w:sdt` here and splices its rows' content into the row, so
613
+ * the recursion walks the control's content with the same map; the sink is
614
+ * the row's either way, because the control keeps no wrapper to hold one.
615
+ */
616
+ const dispatchRowChildren = (element, childOptions) => {
617
+ const captured = dispatchChildren({
618
+ element,
619
+ container: "row-content",
620
+ modelledCount: () => row.cells.length,
621
+ handlers: {
622
+ tc: (child) => {
623
+ const cell = parseTableCell(child, styles, theme, numbering, rels, media, childOptions);
624
+ if (pendingBookmarkMarkers.length > 0) {
625
+ prependBookmarkMarkersToFirstParagraphInCell(cell, pendingBookmarkMarkers);
626
+ pendingBookmarkMarkers.length = 0;
627
+ }
628
+ row.cells.push(cell);
629
+ },
630
+ sdt: (child) => {
631
+ const sdtContent = findChildByLocalName(child, "sdtContent");
632
+ if (!sdtContent) return;
633
+ const sdtOptions = withContainerXmlns(childOptions, child);
634
+ dispatchRowChildren(sdtContent, withContainerXmlns(sdtOptions, sdtContent));
635
+ },
636
+ bookmarkStart: (child) => {
637
+ placeBookmarkMarker(parseBookmarkStart(child));
638
+ },
639
+ bookmarkEnd: (child) => {
640
+ placeBookmarkMarker(parseBookmarkEnd(child));
641
+ },
642
+ trPr: OWNED_ELSEWHERE,
643
+ tblPrEx: OWNED_ELSEWHERE,
644
+ commentRangeEnd: CAPTURE,
645
+ commentRangeStart: CAPTURE,
646
+ customXml: CAPTURE,
647
+ customXmlDelRangeEnd: CAPTURE,
648
+ customXmlDelRangeStart: CAPTURE,
649
+ customXmlInsRangeEnd: CAPTURE,
650
+ customXmlInsRangeStart: CAPTURE,
651
+ customXmlMoveFromRangeEnd: CAPTURE,
652
+ customXmlMoveFromRangeStart: CAPTURE,
653
+ customXmlMoveToRangeEnd: CAPTURE,
654
+ customXmlMoveToRangeStart: CAPTURE,
655
+ del: CAPTURE,
656
+ ins: CAPTURE,
657
+ moveFrom: CAPTURE,
658
+ moveFromRangeEnd: CAPTURE,
659
+ moveFromRangeStart: CAPTURE,
660
+ moveTo: CAPTURE,
661
+ moveToRangeEnd: CAPTURE,
662
+ moveToRangeStart: CAPTURE,
663
+ permEnd: CAPTURE,
664
+ permStart: CAPTURE,
665
+ proofErr: CAPTURE,
666
+ tr: CAPTURE
586
667
  }
587
- row.cells.push(cell);
588
- return;
589
- }
590
- if (localName === "sdt") {
591
- const sdtContent = findChildByLocalName(child, "sdtContent");
592
- if (!sdtContent) return;
593
- const sdtContentOptions = withContainerXmlns(withContainerXmlns(childOptions, child), sdtContent);
594
- for (const sdtChild of getChildElements(sdtContent)) parseRowChild(sdtChild, sdtContentOptions);
595
- return;
596
- }
597
- if (localName !== "bookmarkStart" && localName !== "bookmarkEnd") return;
598
- const marker = parseBookmarkMarker(child, localName);
668
+ });
669
+ if (captured?.children) preservedChildren.push(...captured.children);
670
+ };
671
+ const placeBookmarkMarker = (marker) => {
599
672
  if (!appendBookmarkMarkerToLastParagraphInCells(row.cells, marker)) pendingBookmarkMarkers.push(marker);
600
673
  };
601
- for (const child of getChildElements(trElement)) parseRowChild(child);
674
+ dispatchRowChildren(trElement, rowOptions);
675
+ if (preservedChildren.length > 0) row.preserved = { children: preservedChildren };
676
+ const remainingAttributes = attributeRemainder({
677
+ element: trElement,
678
+ modelled: NO_MODELLED_ATTRIBUTES
679
+ });
680
+ if (remainingAttributes) row.preservedAttributes = remainingAttributes;
602
681
  if (pendingBookmarkMarkers.length > 0) appendBookmarkMarkersToLastCell(row, pendingBookmarkMarkers);
603
682
  return row;
604
683
  }
@@ -707,23 +786,65 @@ function parseTable(tblElement, styles, theme, numbering, rels, media, options)
707
786
  }
708
787
  const tableOptions = withContainerXmlns(options, tblElement);
709
788
  const rowsWithGridOffsets = /* @__PURE__ */ new Set();
710
- const parseTableChild = (child, childOptions = tableOptions) => {
711
- const localName = getLocalName(child.name);
712
- if (localName === "tr") {
713
- const rowIndex = table.rows.length;
714
- const row = parseTableRow(child, styles, theme, numbering, rels, media, childOptions);
715
- table.rows.push(row);
716
- if (hasRowGridOffsets(child)) rowsWithGridOffsets.add(rowIndex);
717
- return;
718
- }
719
- if (localName !== "sdt") return;
720
- const sdtContent = findChildByLocalName(child, "sdtContent");
721
- if (!sdtContent) return;
722
- const sdtContentOptions = withContainerXmlns(withContainerXmlns(childOptions, child), sdtContent);
723
- for (const sdtChild of getChildElements(sdtContent)) parseTableChild(sdtChild, sdtContentOptions);
789
+ const preservedChildren = [];
790
+ /**
791
+ * One table's children, or a row-level content control's.
792
+ *
793
+ * folio unwraps `w:sdt` here and splices its rows into the table, so the
794
+ * recursion walks the control's content with the same map; the sink is the
795
+ * table's either way, because the control keeps no wrapper to hold one.
796
+ */
797
+ const dispatchTableChildren = (element, childOptions) => {
798
+ const captured = dispatchChildren({
799
+ element,
800
+ container: "table-content",
801
+ modelledCount: () => table.rows.length,
802
+ handlers: {
803
+ tr: (child) => {
804
+ const rowIndex = table.rows.length;
805
+ table.rows.push(parseTableRow(child, styles, theme, numbering, rels, media, childOptions));
806
+ if (hasRowGridOffsets(child)) rowsWithGridOffsets.add(rowIndex);
807
+ },
808
+ sdt: (child) => {
809
+ const sdtContent = findChildByLocalName(child, "sdtContent");
810
+ if (!sdtContent) return;
811
+ const sdtOptions = withContainerXmlns(childOptions, child);
812
+ dispatchTableChildren(sdtContent, withContainerXmlns(sdtOptions, sdtContent));
813
+ },
814
+ tblPr: OWNED_ELSEWHERE,
815
+ tblGrid: OWNED_ELSEWHERE,
816
+ bookmarkEnd: CAPTURE,
817
+ bookmarkStart: CAPTURE,
818
+ commentRangeEnd: CAPTURE,
819
+ commentRangeStart: CAPTURE,
820
+ customXml: CAPTURE,
821
+ customXmlPr: CAPTURE,
822
+ customXmlDelRangeEnd: CAPTURE,
823
+ customXmlDelRangeStart: CAPTURE,
824
+ customXmlInsRangeEnd: CAPTURE,
825
+ customXmlInsRangeStart: CAPTURE,
826
+ customXmlMoveFromRangeEnd: CAPTURE,
827
+ customXmlMoveFromRangeStart: CAPTURE,
828
+ customXmlMoveToRangeEnd: CAPTURE,
829
+ customXmlMoveToRangeStart: CAPTURE,
830
+ del: CAPTURE,
831
+ ins: CAPTURE,
832
+ moveFrom: CAPTURE,
833
+ moveFromRangeEnd: CAPTURE,
834
+ moveFromRangeStart: CAPTURE,
835
+ moveTo: CAPTURE,
836
+ moveToRangeEnd: CAPTURE,
837
+ moveToRangeStart: CAPTURE,
838
+ permEnd: CAPTURE,
839
+ permStart: CAPTURE,
840
+ proofErr: CAPTURE
841
+ }
842
+ });
843
+ if (captured?.children) preservedChildren.push(...captured.children);
724
844
  };
725
- for (const child of getChildElements(tblElement)) parseTableChild(child);
845
+ dispatchTableChildren(tblElement, tableOptions);
726
846
  if (table.rows.length === 0) return;
847
+ if (preservedChildren.length > 0) table.preserved = { children: preservedChildren };
727
848
  inferImplicitSingleCellRowSpans(table, rowsWithGridOffsets);
728
849
  return table;
729
850
  }
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { NumberingMap } from "./numberingParser.js";
3
- import { StyleMap } from "./styleParser.js";
4
3
  import { XmlElement } from "./xmlParser.js";
4
+ import { StyleMap } from "./styleParser.js";
5
5
  //#region src/docx/textBoxParser.d.ts
6
6
  /**
7
7
  * Extract raw paragraph elements from w:txbxContent
@@ -112,5 +112,28 @@ declare function resolveTextBoxOutlineColor(textBox: document_d_exports.TextBox)
112
112
  * Get outline width in pixels
113
113
  */
114
114
  declare function getTextBoxOutlineWidthPx(textBox: document_d_exports.TextBox): number;
115
+ type TextBoxRunScan = {
116
+ textBoxDrawings: XmlElement[];
117
+ vmlTextBoxes: XmlElement[];
118
+ hasNonTextBoxContent: boolean;
119
+ };
120
+ /**
121
+ * What a run's markup holds for the text-box pass, by kind.
122
+ *
123
+ * Two readers need this answer and they must not drift: the enrichment pass
124
+ * claims these elements, and `parseParagraphContents` has to keep the run they
125
+ * sit in even though the run parses to nothing until the pass fills it. It
126
+ * lives here, with `isTextBoxDrawing` and `parseTextBox`, so there is one.
127
+ */
128
+ type ScanRunForTextBoxDrawingsOptions = {
129
+ xmlRun: XmlElement;
130
+ /**
131
+ * `isVmlPictParsedByRunParser`, passed rather than imported: the run parser
132
+ * reaches this module through the image parsers, so reading it from here
133
+ * would close a cycle.
134
+ */
135
+ claimedByRunParser: (pictElement: XmlElement) => boolean;
136
+ };
137
+ declare const scanRunForTextBoxDrawings: ({ xmlRun, claimedByRunParser }: ScanRunForTextBoxDrawingsOptions) => TextBoxRunScan;
115
138
  //#endregion
116
- export { ParagraphParserFn, TableParserFn, extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor };
139
+ export { ParagraphParserFn, ScanRunForTextBoxDrawingsOptions, TableParserFn, TextBoxRunScan, extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
@@ -1,7 +1,7 @@
1
1
  import { emuToPixels } from "../utils/units.js";
2
2
  import { parseAnchorPosition, parseAnchorWrap, parseFill, parseOutline, resolveColorValueToHex } from "./drawingUtils.js";
3
3
  import { parseNonVisualDrawingNames } from "./nonVisualDrawingProps.js";
4
- import { findByFullName, findChildByLocalName, findChildByNamespaceUri, findChildrenByLocalName, findChildrenByNamespaceUri, getAttribute, getChildElements, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
4
+ import { findByFullName, findChildByLocalName, findChildByNamespaceUri, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, getAttribute, getChildElements, getLocalName, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
5
5
  //#region src/docx/textBoxParser.ts
6
6
  const DRAWINGML_MAIN_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/drawingml/2006/main", "http://purl.oclc.org/ooxml/drawingml/main"]);
7
7
  const WORDPROCESSING_SHAPE_NAMESPACE_URIS = /* @__PURE__ */ new Set(["http://schemas.microsoft.com/office/word/2010/wordprocessingShape"]);
@@ -290,6 +290,7 @@ const getTextBoxBlockText = (block) => {
290
290
  return runTexts.join("");
291
291
  }
292
292
  if (block.type === "table") return block.rows.map((row) => row.cells.map((cell) => cell.content.map(getTextBoxBlockText).join("\n")).join(" ")).join("\n");
293
+ if (block.type === "preservedBlock") return "";
293
294
  return block.content.map(getTextBoxBlockText).join("\n");
294
295
  };
295
296
  /**
@@ -313,5 +314,54 @@ function getTextBoxOutlineWidthPx(textBox) {
313
314
  if (!textBox.outline?.width) return 0;
314
315
  return emuToPixels(textBox.outline.width);
315
316
  }
317
+ const scanRunForTextBoxDrawings = ({ xmlRun, claimedByRunParser }) => {
318
+ const textBoxDrawings = [];
319
+ const vmlTextBoxes = [];
320
+ let hasNonTextBoxContent = false;
321
+ const visitDrawing = (drawingEl) => {
322
+ if (isTextBoxDrawing(drawingEl)) {
323
+ textBoxDrawings.push(drawingEl);
324
+ return;
325
+ }
326
+ hasNonTextBoxContent = true;
327
+ };
328
+ for (const el of getChildElements(xmlRun)) {
329
+ const name = getLocalName(el.name ?? "");
330
+ if (name === "rPr") continue;
331
+ if (name === "drawing") {
332
+ visitDrawing(el);
333
+ continue;
334
+ }
335
+ if (name === "pict") {
336
+ if (findDeep(el, "v", "textbox") && !claimedByRunParser(el)) vmlTextBoxes.push(el);
337
+ else hasNonTextBoxContent = true;
338
+ continue;
339
+ }
340
+ if (name === "AlternateContent") {
341
+ const branches = getChildElements(el);
342
+ const choice = branches.find((branch) => getLocalName(branch.name ?? "") === "Choice");
343
+ const fallback = branches.find((branch) => getLocalName(branch.name ?? "") === "Fallback");
344
+ const tryBranch = (branch) => {
345
+ if (!branch) return false;
346
+ let found = false;
347
+ for (const innerEl of getChildElements(branch)) if (getLocalName(innerEl.name ?? "") === "drawing") {
348
+ visitDrawing(innerEl);
349
+ found = true;
350
+ }
351
+ return found;
352
+ };
353
+ let foundInBranch = tryBranch(choice);
354
+ if (!foundInBranch) foundInBranch = tryBranch(fallback);
355
+ if (!foundInBranch) hasNonTextBoxContent = true;
356
+ continue;
357
+ }
358
+ hasNonTextBoxContent = true;
359
+ }
360
+ return {
361
+ textBoxDrawings,
362
+ vmlTextBoxes,
363
+ hasNonTextBoxContent
364
+ };
365
+ };
316
366
  //#endregion
317
- export { extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor };
367
+ export { extractTextBoxContentElements, getTextBoxContentElement, getTextBoxDimensionsPx, getTextBoxHeightPx, getTextBoxMarginsPx, getTextBoxOutlineWidthPx, getTextBoxText, getTextBoxWidthPx, hasTextBoxContent, hasTextBoxFill, hasTextBoxOutline, isFloatingTextBox, isShapeTextBox, isTextBoxDrawing, parseTextBox, parseTextBoxContent, parseTextBoxFromShape, resolveTextBoxFillColor, resolveTextBoxOutlineColor, scanRunForTextBoxDrawings };
@@ -1,6 +1,7 @@
1
1
  import { bytesToDataUrl } from "../utils/base64.js";
2
2
  import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
3
3
  import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
4
+ import { decodeXmlBytes } from "./xmlEncoding.js";
4
5
  import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits, createXmlPackageBudget } from "./xmlResourceLimits.js";
5
6
  import JSZip from "jszip";
6
7
  //#region src/docx/unzip.ts
@@ -161,13 +162,13 @@ async function unzipDocx(buffer, options = {}) {
161
162
  if (lowerPath.endsWith(".xml") || lowerPath.endsWith(".rels")) {
162
163
  assertEntrySize(path, declaredSize, limits.maxXmlBytes);
163
164
  if (options.extractAllXml === false && !shouldExtractXmlPart(lowerPath)) continue;
164
- extractionTasks.push(() => file.async("text").then((xmlContent) => {
165
- assertExtractedSize(path, xmlContent.length, limits.maxXmlBytes);
165
+ extractionTasks.push(() => file.async("uint8array").then((xmlBytes) => {
166
+ assertExtractedSize(path, xmlBytes.byteLength, limits.maxXmlBytes);
166
167
  return {
167
168
  type: "xml",
168
169
  path,
169
170
  lowerPath,
170
- content: xmlContent
171
+ content: decodeXmlBytes(xmlBytes)
171
172
  };
172
173
  }));
173
174
  } else if (lowerPath.startsWith("word/media/")) {
@@ -456,7 +457,7 @@ function extractFile(content, path) {
456
457
  const file = content.originalZip.file(path);
457
458
  if (!file || !isParsedDocxEntry(path)) return Promise.resolve(null);
458
459
  const lowerPath = path.toLowerCase();
459
- if (lowerPath.endsWith(".xml") || lowerPath.endsWith(".rels")) return file.async("text");
460
+ if (lowerPath.endsWith(".xml") || lowerPath.endsWith(".rels")) return file.async("uint8array").then(decodeXmlBytes);
460
461
  return file.async("arraybuffer");
461
462
  }
462
463
  /**
@@ -11,6 +11,22 @@ import { XmlElement } from "./xmlParser.js";
11
11
  * same artwork twice.
12
12
  */
13
13
  declare function shouldPreserveRawVmlPict(pictElement: XmlElement): boolean;
14
+ /**
15
+ * Whether the run parser turns this `w:pict` into run content of its own.
16
+ *
17
+ * Exactly one owner may represent a `w:pict`: the run parser, whose drawing
18
+ * carries the whole element as captured XML, or the text-box enrichment pass,
19
+ * which rebuilds one `v:textbox` as an editable shape. Two owners write the
20
+ * same artwork twice, and when the pict holds text, the saved document says it
21
+ * twice.
22
+ *
23
+ * The enrichment asks this rather than re-deriving the answer from the markup:
24
+ * its own reading (`v:imagedata` present) named only the picture path, so a
25
+ * `v:group` claimed by the preview path was represented by both owners. The
26
+ * result is a pure function of the element, so asking it a second time costs
27
+ * only the work the run parser already did.
28
+ */
29
+ declare const isVmlPictParsedByRunParser: (pictElement: XmlElement, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null) => boolean;
14
30
  /**
15
31
  * Parse a `w:pict` element into an inline image, or null when it carries no
16
32
  * ordinary VML picture (no resolvable `<v:imagedata>`, or a watermark shape).
@@ -22,4 +38,4 @@ declare function shouldPreserveRawVmlPict(pictElement: XmlElement): boolean;
22
38
  */
23
39
  declare function parseVmlImageContent(pictElement: XmlElement, rels: document_d_exports.RelationshipMap | null, media: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>): document_d_exports.DrawingContent | null;
24
40
  //#endregion
25
- export { parseVmlImageContent, shouldPreserveRawVmlPict };
41
+ export { isVmlPictParsedByRunParser, parseVmlImageContent, shouldPreserveRawVmlPict };
@@ -131,6 +131,22 @@ function shouldPreserveRawVmlPict(pictElement) {
131
131
  return shapes.length > 0 && !shapes.some((shape) => isWatermarkShape(shape));
132
132
  }
133
133
  /**
134
+ * Whether the run parser turns this `w:pict` into run content of its own.
135
+ *
136
+ * Exactly one owner may represent a `w:pict`: the run parser, whose drawing
137
+ * carries the whole element as captured XML, or the text-box enrichment pass,
138
+ * which rebuilds one `v:textbox` as an editable shape. Two owners write the
139
+ * same artwork twice, and when the pict holds text, the saved document says it
140
+ * twice.
141
+ *
142
+ * The enrichment asks this rather than re-deriving the answer from the markup:
143
+ * its own reading (`v:imagedata` present) named only the picture path, so a
144
+ * `v:group` claimed by the preview path was represented by both owners. The
145
+ * result is a pure function of the element, so asking it a second time costs
146
+ * only the work the run parser already did.
147
+ */
148
+ const isVmlPictParsedByRunParser = (pictElement, rels, media) => parseVmlImageContent(pictElement, rels, media) !== null || shouldPreserveRawVmlPict(pictElement);
149
+ /**
134
150
  * Read the relationship id off a `v:imagedata` element. Word writes `r:id`;
135
151
  * some legacy / third-party generators use `r:embed` or the office-namespace
136
152
  * `o:relid` instead, so fall back through those before the bare `id`.
@@ -201,4 +217,4 @@ function parseVmlImageContent(pictElement, rels, media, rootXmlns = {}) {
201
217
  return null;
202
218
  }
203
219
  //#endregion
204
- export { parseVmlImageContent, shouldPreserveRawVmlPict };
220
+ export { isVmlPictParsedByRunParser, parseVmlImageContent, shouldPreserveRawVmlPict };
@@ -0,0 +1,5 @@
1
+ //#region src/docx/xmlEncoding.d.ts
2
+ /** Decode the UTF-8 and UTF-16 encodings XML processors must recognize. */
3
+ declare const decodeXmlBytes: (bytes: Uint8Array) => string;
4
+ //#endregion
5
+ export { decodeXmlBytes };
@@ -0,0 +1,12 @@
1
+ //#region src/docx/xmlEncoding.ts
2
+ /** Decode the UTF-8 and UTF-16 encodings XML processors must recognize. */
3
+ const decodeXmlBytes = (bytes) => {
4
+ if (bytes[0] === 255 && bytes[1] === 254) return new TextDecoder("utf-16le").decode(bytes.subarray(2));
5
+ if (bytes[0] === 254 && bytes[1] === 255) return new TextDecoder("utf-16be").decode(bytes.subarray(2));
6
+ if (bytes[0] === 60 && bytes[1] === 0 && bytes[2] === 63 && bytes[3] === 0) return new TextDecoder("utf-16le").decode(bytes);
7
+ if (bytes[0] === 0 && bytes[1] === 60 && bytes[2] === 0 && bytes[3] === 63) return new TextDecoder("utf-16be").decode(bytes);
8
+ const utf8Offset = bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191 ? 3 : 0;
9
+ return new TextDecoder("utf-8").decode(bytes.subarray(utf8Offset));
10
+ };
11
+ //#endregion
12
+ export { decodeXmlBytes };
@@ -105,6 +105,15 @@ declare function getLocalName(name: string | undefined): string;
105
105
  declare function getNamespacePrefix(name: string): string | null;
106
106
  /** Namespace URI resolved from the element's in-scope XML declarations. */
107
107
  declare const getNamespaceUri: (element: XmlElement) => string | undefined;
108
+ /**
109
+ * Namespace URI of one of the element's attributes, by its source spelling.
110
+ *
111
+ * An unprefixed attribute has no namespace — it is not in the element's,
112
+ * which is why this is not {@link getNamespaceUri} with a different argument —
113
+ * and `undefined` is that answer as well as "the prefix resolves to nothing".
114
+ * A caller that must tell the two apart checks the spelling for a colon.
115
+ */
116
+ declare const resolveAttributeNamespaceUri: (element: XmlElement, attributeName: string) => string | undefined;
108
117
  /** WordprocessingML main namespace, Transitional and Strict (ECMA-376 Parts 1 and 4). */
109
118
  declare const WORDPROCESSINGML_NAMESPACE_URIS: ReadonlySet<string>;
110
119
  /** Office document relationship attributes, Transitional and Strict. */
@@ -383,4 +392,4 @@ declare function cloneWithXmlnsDeclarations(element: XmlElement, xmlnsDecls: Rec
383
392
  */
384
393
  declare function cloneElement(element: XmlElement, overrides: Partial<XmlElement>): XmlElement;
385
394
  //#endregion
386
- export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, XmlAttributeMatch, XmlElement, XmlNamespaceScope, attachXmlNamespaceContext, cloneElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument, selectAlternateContentBranch };
395
+ export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, XmlAttributeMatch, XmlElement, XmlNamespaceScope, attachXmlNamespaceContext, cloneElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument, resolveAttributeNamespaceUri, selectAlternateContentBranch };
@@ -246,6 +246,19 @@ function getNamespacePrefix(name) {
246
246
  }
247
247
  /** Namespace URI resolved from the element's in-scope XML declarations. */
248
248
  const getNamespaceUri = (element) => element.namespaceUri;
249
+ /**
250
+ * Namespace URI of one of the element's attributes, by its source spelling.
251
+ *
252
+ * An unprefixed attribute has no namespace — it is not in the element's,
253
+ * which is why this is not {@link getNamespaceUri} with a different argument —
254
+ * and `undefined` is that answer as well as "the prefix resolves to nothing".
255
+ * A caller that must tell the two apart checks the spelling for a colon.
256
+ */
257
+ const resolveAttributeNamespaceUri = (element, attributeName) => {
258
+ const colonIndex = attributeName.indexOf(":");
259
+ if (colonIndex === -1) return;
260
+ return resolveNamespaceUri(element.namespaceScope, attributeName.slice(0, colonIndex));
261
+ };
249
262
  /** WordprocessingML main namespace, Transitional and Strict (ECMA-376 Parts 1 and 4). */
250
263
  const WORDPROCESSINGML_NAMESPACE_URIS = /* @__PURE__ */ new Set([NAMESPACES.w, "http://purl.oclc.org/ooxml/wordprocessingml/main"]);
251
264
  /** Office document relationship attributes, Transitional and Strict. */
@@ -855,4 +868,4 @@ function cloneElement(element, overrides) {
855
868
  return clone;
856
869
  }
857
870
  //#endregion
858
- export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, attachXmlNamespaceContext, cloneElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument, selectAlternateContentBranch };
871
+ export { NAMESPACES, OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, attachXmlNamespaceContext, cloneElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findChildrenByNamespaceUri, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument, resolveAttributeNamespaceUri, selectAlternateContentBranch };
@@ -1,5 +1,7 @@
1
1
  import { resolveDocumentGridLinePitch } from "./docx/documentGrid.js";
2
2
  import { formatOoxmlCounter } from "./docx/ooxmlCounterFormatter.js";
3
+ import { createParseWarningCollector } from "./docx/parseContext.js";
4
+ import { formatParseWarnings } from "./docx/parseWarningMessage.js";
3
5
  import { parseDocx } from "./docx/parser.js";
4
6
  import { buildHeaderFooterFieldValues } from "./fields/resolveFieldValues.js";
5
7
  import { extractEmbeddedFonts } from "./fonts/embeddedFonts.js";
@@ -209,14 +211,21 @@ const layoutDocxHeadless = async (input, options = {}) => {
209
211
  });
210
212
  if (parsed.isErr()) return Result.err(parsed.error);
211
213
  const document = parsed.value;
214
+ const { context: projectionContext, warnings: projectionWarnings } = createParseWarningCollector("word/document.xml");
212
215
  const projected = Result.try(() => toProseDoc(document, {
213
216
  ...document.package.styles ? { styles: document.package.styles } : {},
214
- ...document.package.theme ? { theme: document.package.theme } : {}
217
+ ...document.package.theme ? { theme: document.package.theme } : {},
218
+ warn: projectionContext.warn
215
219
  }));
216
220
  if (projected.isErr()) return Result.err(new HeadlessLayoutError({
217
221
  message: "The document could not be projected to the editable model.",
218
222
  cause: projected.error
219
223
  }));
224
+ const recorded = projectionWarnings();
225
+ if (recorded.length > 0) {
226
+ document.parseWarnings = [...document.parseWarnings ?? [], ...recorded];
227
+ document.warnings = [...document.warnings ?? [], ...formatParseWarnings(recorded)];
228
+ }
220
229
  if (packageNeedsShaping(document.package)) {
221
230
  const loaded = await Result.tryPromise({
222
231
  try: () => getShaper(),