@stll/folio-core 0.7.0 → 0.9.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 (193) hide show
  1. package/README.md +39 -0
  2. package/dist/ai-edits/apply.js +18 -4
  3. package/dist/ai-edits/blockRange.d.ts +13 -2
  4. package/dist/ai-edits/blockRange.js +23 -2
  5. package/dist/ai-edits/headless.d.ts +31 -4
  6. package/dist/ai-edits/headless.js +148 -35
  7. package/dist/ai-edits/index.d.ts +6 -4
  8. package/dist/ai-edits/index.js +4 -1
  9. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  10. package/dist/ai-edits/scoped-reading.js +60 -0
  11. package/dist/ai-edits/snapshot.js +53 -7
  12. package/dist/ai-edits/types.d.ts +46 -3
  13. package/dist/compat/eigenpal.d.ts +8 -3
  14. package/dist/compat/eigenpal.js +7 -2
  15. package/dist/controller/fontReadiness.d.ts +29 -0
  16. package/dist/controller/fontReadiness.js +139 -0
  17. package/dist/controller/latestRequestGate.d.ts +13 -0
  18. package/dist/controller/latestRequestGate.js +16 -0
  19. package/dist/controller/layoutPipeline.js +88 -10
  20. package/dist/document-operations.d.ts +10 -3
  21. package/dist/document-operations.js +29 -14
  22. package/dist/docx/blockContentParser.js +53 -10
  23. package/dist/docx/commentParser.js +1 -1
  24. package/dist/docx/compatibility.d.ts +30 -2
  25. package/dist/docx/compatibility.js +118 -39
  26. package/dist/docx/conformance.d.ts +6 -0
  27. package/dist/docx/conformance.js +18 -0
  28. package/dist/docx/encryption/agileDecryption.js +1 -1
  29. package/dist/docx/encryption/encryptionInfo.js +1 -1
  30. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  31. package/dist/docx/ensureParaIds.d.ts +29 -0
  32. package/dist/docx/ensureParaIds.js +423 -0
  33. package/dist/docx/fontTableParser.d.ts +6 -0
  34. package/dist/docx/fontTableParser.js +72 -0
  35. package/dist/docx/groupDrawingParser.js +7 -2
  36. package/dist/docx/headerFooterParser.js +1 -1
  37. package/dist/docx/normalizeBaseDirection.js +1 -1
  38. package/dist/docx/numberingParser.js +2 -0
  39. package/dist/docx/paragraphParser.js +21 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  41. package/dist/docx/parser.d.ts +1 -1
  42. package/dist/docx/parser.js +14 -9
  43. package/dist/docx/rezip.js +70 -5
  44. package/dist/docx/runConsolidator.js +4 -0
  45. package/dist/docx/runParser.js +19 -1
  46. package/dist/docx/selectiveSave.js +3 -3
  47. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  48. package/dist/docx/selectiveXmlPatch.js +1 -1
  49. package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
  50. package/dist/docx/serializer/fontTableSerializer.js +26 -0
  51. package/dist/docx/serializer/headerFooterSerializer.js +1 -1
  52. package/dist/docx/serializer/paragraphSerializer.js +8 -0
  53. package/dist/docx/serializer/runSerializer.js +14 -2
  54. package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
  55. package/dist/docx/serializer/settingsSerializer.js +16 -0
  56. package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
  57. package/dist/docx/serializer/stylesSerializer.js +56 -0
  58. package/dist/docx/serializer/tableSerializer.js +29 -26
  59. package/dist/docx/serializer/themeSerializer.d.ts +6 -0
  60. package/dist/docx/serializer/themeSerializer.js +36 -0
  61. package/dist/docx/settingsParser.js +46 -0
  62. package/dist/docx/styleParser.js +26 -1
  63. package/dist/docx/tableParser.js +19 -1
  64. package/dist/docx/textBoxParser.js +6 -1
  65. package/dist/docx/vmlImageParser.js +146 -1
  66. package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
  67. package/dist/i18n/messages/catalogs.gen.js +5910 -5910
  68. package/dist/index.d.ts +8 -3
  69. package/dist/index.js +7 -2
  70. package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
  71. package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
  72. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
  73. package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
  74. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  75. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  76. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
  77. package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
  78. package/dist/layout-bridge/engine/hitTest.js +2 -1
  79. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  80. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  81. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  82. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  83. package/dist/layout-engine/headerFooterRefs.js +40 -0
  84. package/dist/layout-engine/index.d.ts +3 -2
  85. package/dist/layout-engine/index.js +68 -106
  86. package/dist/layout-engine/measure/cache.js +11 -3
  87. package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
  88. package/dist/layout-engine/measure/index.d.ts +2 -1
  89. package/dist/layout-engine/measure/index.js +2 -1
  90. package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
  91. package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
  92. package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
  93. package/dist/layout-engine/measure/lineBreaks.js +11 -18
  94. package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
  95. package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
  96. package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
  97. package/dist/layout-engine/measure/measureBlocks.js +138 -86
  98. package/dist/layout-engine/measure/measureContainer.js +32 -9
  99. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  100. package/dist/layout-engine/measure/measureParagraph.js +346 -74
  101. package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
  102. package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
  103. package/dist/layout-engine/measure/measureWorker.js +8 -19
  104. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
  105. package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
  106. package/dist/layout-engine/measure/tableCellFloating.js +37 -29
  107. package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
  108. package/dist/layout-engine/measure/tableCellFlow.js +73 -0
  109. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  110. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  111. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  112. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  113. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
  114. package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
  115. package/dist/layout-engine/paginator.d.ts +2 -1
  116. package/dist/layout-engine/paginator.js +7 -6
  117. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  118. package/dist/layout-engine/paragraphFrame.js +17 -0
  119. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  120. package/dist/layout-engine/paragraphSequence.js +25 -0
  121. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  122. package/dist/layout-engine/paragraphSpacing.js +30 -0
  123. package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
  124. package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
  125. package/dist/layout-engine/tableRowBreak.d.ts +5 -2
  126. package/dist/layout-engine/tableRowBreak.js +62 -29
  127. package/dist/layout-engine/types.d.ts +54 -12
  128. package/dist/layout-engine/types.js +15 -2
  129. package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
  130. package/dist/layout-painter/anchoredImagePosition.js +95 -0
  131. package/dist/layout-painter/borderStroke.d.ts +23 -0
  132. package/dist/layout-painter/borderStroke.js +39 -0
  133. package/dist/layout-painter/renderImage.d.ts +5 -4
  134. package/dist/layout-painter/renderImage.js +18 -4
  135. package/dist/layout-painter/renderPage.d.ts +2 -27
  136. package/dist/layout-painter/renderPage.js +15 -101
  137. package/dist/layout-painter/renderParagraph.js +45 -26
  138. package/dist/layout-painter/renderTable.d.ts +2 -0
  139. package/dist/layout-painter/renderTable.js +257 -47
  140. package/dist/layout-painter/renderTextBox.js +17 -7
  141. package/dist/managers/DocumentLoaderManager.d.ts +1 -1
  142. package/dist/managers/DocumentLoaderManager.js +2 -2
  143. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  144. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  145. package/dist/paged-layout/sectionGeometry.js +1 -1
  146. package/dist/prosemirror/attrs/index.d.ts +4 -2
  147. package/dist/prosemirror/attrs/index.js +32 -2
  148. package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
  149. package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
  150. package/dist/prosemirror/conversion/toProseDoc.js +73 -37
  151. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
  153. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
  154. package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  156. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
  157. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  158. package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
  159. package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
  160. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  161. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  162. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
  163. package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
  164. package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
  166. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  167. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  168. package/dist/prosemirror/schema/index.d.ts +2 -2
  169. package/dist/prosemirror/schema/marks.d.ts +6 -1
  170. package/dist/prosemirror/schema/nodes.d.ts +16 -12
  171. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  172. package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
  173. package/dist/prosemirror/utils/tabCalculator.js +12 -10
  174. package/dist/prosemirror/validation.js +4 -1
  175. package/dist/redline.js +13 -3
  176. package/dist/server.d.ts +9 -4
  177. package/dist/server.js +7 -2
  178. package/dist/style-sets/extract.d.ts +35 -0
  179. package/dist/style-sets/extract.js +123 -0
  180. package/dist/style-sets/stellaStyle.d.ts +13 -0
  181. package/dist/style-sets/stellaStyle.js +516 -0
  182. package/dist/style-sets/types.d.ts +31 -0
  183. package/dist/style-sets/types.js +5 -0
  184. package/dist/types/index.d.ts +2 -1
  185. package/dist/utils/createDocument.d.ts +12 -2
  186. package/dist/utils/createDocument.js +55 -36
  187. package/dist/utils/fontResolver.js +6 -6
  188. package/dist/utils/formatToStyle.js +1 -1
  189. package/dist/utils/hexId.d.ts +8 -1
  190. package/dist/utils/hexId.js +11 -3
  191. package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
  192. package/dist/utils/paragraphFormattingMerge.js +14 -4
  193. package/package.json +5 -1
@@ -0,0 +1,56 @@
1
+ import { escapeXml } from "./xmlUtils.js";
2
+ import { serializeTextFormatting } from "./runSerializer.js";
3
+ import { serializeParagraphFormatting } from "./paragraphSerializer.js";
4
+ import { serializeTableCellFormatting, serializeTableFormatting, serializeTableRowFormatting } from "./tableSerializer.js";
5
+ //#region src/docx/serializer/stylesSerializer.ts
6
+ const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
7
+ const serializeStylesXml = (definitions) => {
8
+ const docDefaults = serializeDocumentDefaults(definitions);
9
+ const latentStyles = serializeLatentStyles(definitions);
10
+ const styles = definitions.styles.map(serializeStyle).join("");
11
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:styles xmlns:w="${W_NS}">${docDefaults}${latentStyles}${styles}</w:styles>`;
12
+ };
13
+ const serializeDocumentDefaults = (definitions) => {
14
+ return `<w:docDefaults><w:rPrDefault>${serializeTextFormatting(definitions.docDefaults?.rPr) || "<w:rPr/>"}</w:rPrDefault><w:pPrDefault>${serializeParagraphFormatting(definitions.docDefaults?.pPr) || "<w:pPr/>"}</w:pPrDefault></w:docDefaults>`;
15
+ };
16
+ const serializeLatentStyles = (definitions) => {
17
+ const latent = definitions.latentStyles;
18
+ if (!latent) return "";
19
+ const attrs = [];
20
+ pushBooleanAttr(attrs, "w:defLockedState", latent.defLockedState);
21
+ if (latent.defUIPriority !== void 0) attrs.push(`w:defUIPriority="${latent.defUIPriority}"`);
22
+ pushBooleanAttr(attrs, "w:defSemiHidden", latent.defSemiHidden);
23
+ pushBooleanAttr(attrs, "w:defUnhideWhenUsed", latent.defUnhideWhenUsed);
24
+ pushBooleanAttr(attrs, "w:defQFormat", latent.defQFormat);
25
+ return `<w:latentStyles${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}/>`;
26
+ };
27
+ const serializeStyle = (style) => {
28
+ const attrs = [`w:type="${style.type}"`, `w:styleId="${escapeXml(style.styleId)}"`];
29
+ if (style.default) attrs.push("w:default=\"1\"");
30
+ const parts = [`<w:name w:val="${escapeXml(style.name ?? style.styleId)}"/>`];
31
+ if (style.basedOn) parts.push(`<w:basedOn w:val="${escapeXml(style.basedOn)}"/>`);
32
+ if (style.next) parts.push(`<w:next w:val="${escapeXml(style.next)}"/>`);
33
+ if (style.link) parts.push(`<w:link w:val="${escapeXml(style.link)}"/>`);
34
+ if (style.uiPriority !== void 0) parts.push(`<w:uiPriority w:val="${style.uiPriority}"/>`);
35
+ pushBooleanElement(parts, "hidden", style.hidden);
36
+ pushBooleanElement(parts, "semiHidden", style.semiHidden);
37
+ pushBooleanElement(parts, "unhideWhenUsed", style.unhideWhenUsed);
38
+ pushBooleanElement(parts, "qFormat", style.qFormat);
39
+ pushBooleanElement(parts, "personal", style.personal);
40
+ parts.push(serializeParagraphFormatting(style.pPr));
41
+ parts.push(serializeTextFormatting(style.rPr));
42
+ parts.push(serializeTableFormatting(style.tblPr));
43
+ parts.push(serializeTableRowFormatting(style.trPr));
44
+ parts.push(serializeTableCellFormatting(style.tcPr));
45
+ for (const conditional of style.tblStylePr ?? []) parts.push(`<w:tblStylePr w:type="${conditional.type}">${serializeParagraphFormatting(conditional.pPr)}${serializeTextFormatting(conditional.rPr)}${serializeTableFormatting(conditional.tblPr)}${serializeTableRowFormatting(conditional.trPr)}${serializeTableCellFormatting(conditional.tcPr)}</w:tblStylePr>`);
46
+ return `<w:style ${attrs.join(" ")}>${parts.join("")}</w:style>`;
47
+ };
48
+ const pushBooleanElement = (parts, name, value) => {
49
+ if (value === true) parts.push(`<w:${name}/>`);
50
+ else if (value === false) parts.push(`<w:${name} w:val="0"/>`);
51
+ };
52
+ const pushBooleanAttr = (attrs, name, value) => {
53
+ if (value !== void 0) attrs.push(`${name}="${value ? 1 : 0}"`);
54
+ };
55
+ //#endregion
56
+ export { serializeStylesXml };
@@ -30,36 +30,35 @@ function serializeMeasurement(measurement, elementName) {
30
30
  /**
31
31
  * Serialize table borders (w:tblBorders or w:tcBorders)
32
32
  */
33
+ function serializeTableBorderParts(borders) {
34
+ const parts = [];
35
+ const appendBorder = (border, name) => {
36
+ const xml = serializeBorder(border, name);
37
+ if (xml) parts.push(xml);
38
+ };
39
+ appendBorder(borders.top, "top");
40
+ appendBorder(borders.left, "left");
41
+ appendBorder(borders.bottom, "bottom");
42
+ appendBorder(borders.right, "right");
43
+ appendBorder(borders.insideH, "insideH");
44
+ appendBorder(borders.insideV, "insideV");
45
+ return parts;
46
+ }
33
47
  function serializeTableBorders(borders, elementName) {
34
48
  if (!borders) return "";
35
- const parts = [];
36
- if (borders.top) {
37
- const topXml = serializeBorder(borders.top, "top");
38
- if (topXml) parts.push(topXml);
39
- }
40
- if (borders.left) {
41
- const leftXml = serializeBorder(borders.left, "left");
42
- if (leftXml) parts.push(leftXml);
43
- }
44
- if (borders.bottom) {
45
- const bottomXml = serializeBorder(borders.bottom, "bottom");
46
- if (bottomXml) parts.push(bottomXml);
47
- }
48
- if (borders.right) {
49
- const rightXml = serializeBorder(borders.right, "right");
50
- if (rightXml) parts.push(rightXml);
51
- }
52
- if (borders.insideH) {
53
- const insideHXml = serializeBorder(borders.insideH, "insideH");
54
- if (insideHXml) parts.push(insideHXml);
55
- }
56
- if (borders.insideV) {
57
- const insideVXml = serializeBorder(borders.insideV, "insideV");
58
- if (insideVXml) parts.push(insideVXml);
59
- }
49
+ const parts = serializeTableBorderParts(borders);
60
50
  if (parts.length === 0) return "";
61
51
  return `<w:${elementName}>${parts.join("")}</w:${elementName}>`;
62
52
  }
53
+ function serializeTableCellBorders(borders) {
54
+ if (!borders) return "";
55
+ const parts = serializeTableBorderParts(borders);
56
+ const topLeftToBottomRight = serializeBorder(borders.topLeftToBottomRight, "tl2br");
57
+ if (topLeftToBottomRight) parts.push(topLeftToBottomRight);
58
+ const topRightToBottomLeft = serializeBorder(borders.topRightToBottomLeft, "tr2bl");
59
+ if (topRightToBottomLeft) parts.push(topRightToBottomLeft);
60
+ return parts.length > 0 ? `<w:tcBorders>${parts.join("")}</w:tcBorders>` : "";
61
+ }
63
62
  /**
64
63
  * Serialize cell margins (w:tblCellMar or w:tcMar)
65
64
  */
@@ -172,6 +171,10 @@ function serializeTablePropertyChange(change) {
172
171
  function serializeTableRowFormatting(formatting, propertyChanges, structuralChange) {
173
172
  const parts = [];
174
173
  if (formatting) {
174
+ if (formatting.gridBefore) parts.push(`<w:gridBefore w:val="${intAttr(formatting.gridBefore)}"/>`);
175
+ if (formatting.widthBefore) parts.push(`<w:wBefore w:w="${intAttr(formatting.widthBefore.value)}" w:type="${formatting.widthBefore.type}"/>`);
176
+ if (formatting.gridAfter) parts.push(`<w:gridAfter w:val="${intAttr(formatting.gridAfter)}"/>`);
177
+ if (formatting.widthAfter) parts.push(`<w:wAfter w:w="${intAttr(formatting.widthAfter.value)}" w:type="${formatting.widthAfter.type}"/>`);
175
178
  if (formatting.cantSplit) parts.push("<w:cantSplit/>");
176
179
  if (formatting.header) parts.push("<w:tblHeader/>");
177
180
  if (formatting.height) {
@@ -234,7 +237,7 @@ function serializeTableCellFormatting(formatting, propertyChanges, structuralCha
234
237
  if (formatting.gridSpan && formatting.gridSpan > 1) parts.push(`<w:gridSpan w:val="${intAttr(formatting.gridSpan)}"/>`);
235
238
  if (formatting.vMerge) if (formatting.vMerge === "restart") parts.push("<w:vMerge w:val=\"restart\"/>");
236
239
  else parts.push("<w:vMerge/>");
237
- const bordersXml = serializeTableBorders(formatting.borders, "tcBorders");
240
+ const bordersXml = serializeTableCellBorders(formatting.borders);
238
241
  if (bordersXml) parts.push(bordersXml);
239
242
  const shadingXml = serializeShading(formatting.shading);
240
243
  if (shadingXml) parts.push(shadingXml);
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+
3
+ //#region src/docx/serializer/themeSerializer.d.ts
4
+ declare const serializeThemeXml: (theme: document_d_exports.Theme) => string;
5
+ //#endregion
6
+ export { serializeThemeXml };
@@ -0,0 +1,36 @@
1
+ import { escapeXml } from "./xmlUtils.js";
2
+ //#region src/docx/serializer/themeSerializer.ts
3
+ const A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
4
+ const serializeThemeXml = (theme) => {
5
+ const name = escapeXml(theme.name ?? "Folio Theme");
6
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<a:theme xmlns:a="${A_NS}" name="${name}"><a:themeElements>${serializeColorScheme(theme.colorScheme)}${serializeFontScheme(theme)}${serializeFormatScheme(theme)}</a:themeElements></a:theme>`;
7
+ };
8
+ const serializeColorScheme = (colors) => {
9
+ const values = {
10
+ dk1: colors?.dk1 ?? "000000",
11
+ lt1: colors?.lt1 ?? "FFFFFF",
12
+ dk2: colors?.dk2 ?? "44546A",
13
+ lt2: colors?.lt2 ?? "E7E6E6",
14
+ accent1: colors?.accent1 ?? "4472C4",
15
+ accent2: colors?.accent2 ?? "ED7D31",
16
+ accent3: colors?.accent3 ?? "A5A5A5",
17
+ accent4: colors?.accent4 ?? "FFC000",
18
+ accent5: colors?.accent5 ?? "5B9BD5",
19
+ accent6: colors?.accent6 ?? "70AD47",
20
+ hlink: colors?.hlink ?? "0563C1",
21
+ folHlink: colors?.folHlink ?? "954F72"
22
+ };
23
+ return `<a:clrScheme name="Folio">${Object.entries(values).map(([slot, value]) => `<a:${slot}><a:srgbClr val="${escapeXml(value)}"/></a:${slot}>`).join("")}</a:clrScheme>`;
24
+ };
25
+ const serializeFontScheme = (theme) => {
26
+ return `<a:fontScheme name="Folio">${serializeThemeFont(theme.fontScheme?.majorFont, "majorFont", "Arial")}${serializeThemeFont(theme.fontScheme?.minorFont, "minorFont", "Arial")}</a:fontScheme>`;
27
+ };
28
+ const serializeThemeFont = (font, element, fallback) => {
29
+ const scriptFonts = Object.entries(font?.fonts ?? {}).map(([script, typeface]) => `<a:font script="${escapeXml(script)}" typeface="${escapeXml(typeface)}"/>`).join("");
30
+ return `<a:${element}><a:latin typeface="${escapeXml(font?.latin ?? fallback)}"/><a:ea typeface="${escapeXml(font?.ea ?? "")}"/><a:cs typeface="${escapeXml(font?.cs ?? "")}"/>${scriptFonts}</a:${element}>`;
31
+ };
32
+ const serializeFormatScheme = (theme) => {
33
+ return `<a:fmtScheme name="${escapeXml(theme.formatScheme?.name ?? "Folio")}"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:fillStyleLst><a:lnStyleLst><a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:bgFillStyleLst></a:fmtScheme>`;
34
+ };
35
+ //#endregion
36
+ export { serializeThemeXml };
@@ -8,6 +8,8 @@ const DEFAULT_TAB_STOP_TWIPS = 720;
8
8
  * we substitute the OOXML default instead.
9
9
  */
10
10
  const MAX_TAB_STOP_TWIPS = 31680;
11
+ const MAX_HYPHENATION_ZONE_TWIPS = 31680;
12
+ const MAX_CONSECUTIVE_HYPHEN_LIMIT = 255;
11
13
  function parseSettings(xml) {
12
14
  const root = xml ? parseXmlDocument(xml) : null;
13
15
  const settings = { defaultTabStop: parseDefaultTabStop(root) };
@@ -22,8 +24,52 @@ function parseSettings(xml) {
22
24
  ...eastAsiaLang ? { eastAsia: eastAsiaLang } : {},
23
25
  ...bidiLang ? { bidi: bidiLang } : {}
24
26
  };
27
+ const autoHyphenation = parseOnOffSetting(root, "autoHyphenation");
28
+ if (autoHyphenation !== void 0) settings.autoHyphenation = autoHyphenation;
29
+ const doNotHyphenateCaps = parseOnOffSetting(root, "doNotHyphenateCaps");
30
+ if (doNotHyphenateCaps !== void 0) settings.doNotHyphenateCaps = doNotHyphenateCaps;
31
+ const consecutiveHyphenLimit = parseIntegerSetting(root, "consecutiveHyphenLimit", MAX_CONSECUTIVE_HYPHEN_LIMIT);
32
+ if (consecutiveHyphenLimit !== void 0) settings.consecutiveHyphenLimit = consecutiveHyphenLimit;
33
+ const hyphenationZoneTwips = parseIntegerSetting(root, "hyphenationZone", MAX_HYPHENATION_ZONE_TWIPS);
34
+ if (hyphenationZoneTwips !== void 0) settings.hyphenationZoneTwips = hyphenationZoneTwips;
35
+ const noLineBreaksBefore = parseKinsokuOverride(root, "noLineBreaksBefore");
36
+ const noLineBreaksAfter = parseKinsokuOverride(root, "noLineBreaksAfter");
37
+ const compat = root ? findChild(root, "w", "compat") : null;
38
+ const splitPageBreakAndParagraphMark = compat ? findChild(compat, "w", "splitPgBreakAndParaMark") : null;
39
+ if (splitPageBreakAndParagraphMark && parseBooleanElement(splitPageBreakAndParagraphMark)) settings.splitPageBreakAndParagraphMark = true;
40
+ const applyBreakingRules = compat ? findChild(compat, "w", "applyBreakingRules") : null;
41
+ const useLegacyEthiopicAmharicRules = applyBreakingRules !== null && parseBooleanElement(applyBreakingRules);
42
+ if (noLineBreaksBefore || noLineBreaksAfter || useLegacyEthiopicAmharicRules) settings.lineBreakRules = {
43
+ ...noLineBreaksBefore ? { noLineBreaksBefore } : {},
44
+ ...noLineBreaksAfter ? { noLineBreaksAfter } : {},
45
+ ...useLegacyEthiopicAmharicRules ? { useLegacyEthiopicAmharicRules: true } : {}
46
+ };
25
47
  return settings;
26
48
  }
49
+ function parseOnOffSetting(root, name) {
50
+ if (!root) return;
51
+ const element = findChild(root, "w", name);
52
+ return element ? parseBooleanElement(element) : void 0;
53
+ }
54
+ function parseIntegerSetting(root, name, maximum) {
55
+ if (!root) return;
56
+ const element = findChild(root, "w", name);
57
+ const raw = element ? getAttribute(element, "w", "val") : null;
58
+ if (raw === null || !/^\d+$/u.test(raw)) return;
59
+ const parsed = Number.parseInt(raw, 10);
60
+ return Number.isSafeInteger(parsed) && parsed <= maximum ? parsed : void 0;
61
+ }
62
+ function parseKinsokuOverride(root, name) {
63
+ if (!root) return;
64
+ const element = findChild(root, "w", name);
65
+ const characters = element ? getAttribute(element, "w", "val") : null;
66
+ if (!characters) return;
67
+ const language = getAttribute(element, "w", "lang") || void 0;
68
+ return {
69
+ characters,
70
+ ...language ? { language } : {}
71
+ };
72
+ }
27
73
  function parseDefaultTabStop(root) {
28
74
  if (!root) return 720;
29
75
  const el = findChild(root, "w", "defaultTabStop");
@@ -110,6 +110,17 @@ function parseRunProperties(rPr, theme) {
110
110
  }
111
111
  formatting.fontFamily = fontFamily;
112
112
  }
113
+ const lang = findChild(rPr, "w", "lang");
114
+ if (lang) {
115
+ const val = getAttribute(lang, "w", "val") || void 0;
116
+ const eastAsia = getAttribute(lang, "w", "eastAsia") || void 0;
117
+ const bidi = getAttribute(lang, "w", "bidi") || void 0;
118
+ if (val || eastAsia || bidi) formatting.language = {
119
+ ...val ? { val } : {},
120
+ ...eastAsia ? { eastAsia } : {},
121
+ ...bidi ? { bidi } : {}
122
+ };
123
+ }
113
124
  const spacing = findChild(rPr, "w", "spacing");
114
125
  if (spacing) {
115
126
  const val = parseNumericAttribute(spacing, "w", "val");
@@ -348,6 +359,10 @@ function parseParagraphProperties(pPr, theme) {
348
359
  if (suppressLineNumbers) formatting.suppressLineNumbers = parseBooleanElement(suppressLineNumbers);
349
360
  const suppressAutoHyphens = findChild(pPr, "w", "suppressAutoHyphens");
350
361
  if (suppressAutoHyphens) formatting.suppressAutoHyphens = parseBooleanElement(suppressAutoHyphens);
362
+ const kinsoku = findChild(pPr, "w", "kinsoku");
363
+ if (kinsoku) formatting.kinsoku = parseBooleanElement(kinsoku);
364
+ const overflowPunct = findChild(pPr, "w", "overflowPunct");
365
+ if (overflowPunct) formatting.overflowPunctuation = parseBooleanElement(overflowPunct);
351
366
  const rPr = findChild(pPr, "w", "rPr");
352
367
  if (rPr) {
353
368
  const runProps = parseRunProperties(rPr, theme);
@@ -388,6 +403,16 @@ function parseTableBorders(tblBorders) {
388
403
  if (insideV) borders.insideV = insideV;
389
404
  return Object.keys(borders).length > 0 ? borders : void 0;
390
405
  }
406
+ /** Parse cell-only diagonal borders in addition to the shared table sides. */
407
+ function parseTableCellBorders(tcBorders) {
408
+ if (!tcBorders) return;
409
+ const borders = { ...parseTableBorders(tcBorders) };
410
+ const topLeftToBottomRight = parseBorderSpec(findChild(tcBorders, "w", "tl2br"));
411
+ if (topLeftToBottomRight) borders.topLeftToBottomRight = topLeftToBottomRight;
412
+ const topRightToBottomLeft = parseBorderSpec(findChild(tcBorders, "w", "tr2bl"));
413
+ if (topRightToBottomLeft) borders.topRightToBottomLeft = topRightToBottomLeft;
414
+ return Object.keys(borders).length > 0 ? borders : void 0;
415
+ }
391
416
  /**
392
417
  * Parse cell margins
393
418
  */
@@ -535,7 +560,7 @@ function parseTableCellProperties(tcPr, _theme) {
535
560
  }
536
561
  const tcBorders = findChild(tcPr, "w", "tcBorders");
537
562
  if (tcBorders) {
538
- const bordersResult = parseTableBorders(tcBorders);
563
+ const bordersResult = parseTableCellBorders(tcBorders);
539
564
  if (bordersResult) formatting.borders = bordersResult;
540
565
  }
541
566
  const tcMar = findChild(tcPr, "w", "tcMar");
@@ -105,6 +105,16 @@ function parseTableBorders(bordersElement) {
105
105
  if (Object.keys(borders).length === 0) return;
106
106
  return borders;
107
107
  }
108
+ /** Parse cell-only diagonal borders in addition to the shared table sides. */
109
+ function parseTableCellBorders(bordersElement) {
110
+ if (!bordersElement) return;
111
+ const borders = { ...parseTableBorders(bordersElement) };
112
+ const topLeftToBottomRight = parseBorderSpec(findChild(bordersElement, "w", "tl2br"));
113
+ if (topLeftToBottomRight) borders.topLeftToBottomRight = topLeftToBottomRight;
114
+ const topRightToBottomLeft = parseBorderSpec(findChild(bordersElement, "w", "tr2bl"));
115
+ if (topRightToBottomLeft) borders.topRightToBottomLeft = topRightToBottomLeft;
116
+ return Object.keys(borders).length > 0 ? borders : void 0;
117
+ }
108
118
  /**
109
119
  * Parse cell margins (w:tblCellMar or w:tcMar)
110
120
  *
@@ -353,6 +363,14 @@ function parseTableCellStructuralChange(tcPrElement) {
353
363
  function parseTableRowProperties(trPrElement) {
354
364
  if (!trPrElement) return;
355
365
  const formatting = {};
366
+ const gridBefore = parseNumericAttribute(findChild(trPrElement, "w", "gridBefore"), "w", "val");
367
+ if (gridBefore !== void 0 && gridBefore > 0) formatting.gridBefore = gridBefore;
368
+ const widthBefore = parseTableMeasurement(findChild(trPrElement, "w", "wBefore"));
369
+ if (widthBefore) formatting.widthBefore = widthBefore;
370
+ const gridAfter = parseNumericAttribute(findChild(trPrElement, "w", "gridAfter"), "w", "val");
371
+ if (gridAfter !== void 0 && gridAfter > 0) formatting.gridAfter = gridAfter;
372
+ const widthAfter = parseTableMeasurement(findChild(trPrElement, "w", "wAfter"));
373
+ if (widthAfter) formatting.widthAfter = widthAfter;
356
374
  const heightElement = findChild(trPrElement, "w", "trHeight");
357
375
  if (heightElement) {
358
376
  const heightVal = parseNumericAttribute(heightElement, "w", "val");
@@ -438,7 +456,7 @@ function parseTableCellProperties(tcPrElement) {
438
456
  const formatting = {};
439
457
  const width = parseWidth(findChild(tcPrElement, "w", "tcW"));
440
458
  if (width) formatting.width = width;
441
- const borders = parseTableBorders(findChild(tcPrElement, "w", "tcBorders"));
459
+ const borders = parseTableCellBorders(findChild(tcPrElement, "w", "tcBorders"));
442
460
  if (borders) formatting.borders = borders;
443
461
  const margins = parseCellMargins(findChild(tcPrElement, "w", "tcMar"));
444
462
  if (margins) formatting.margins = margins;
@@ -1,4 +1,4 @@
1
- import { findByFullName, findChildrenByLocalName, getAttribute, getChildElements, parseNumericAttribute } from "./xmlParser.js";
1
+ import { findByFullName, findChildByLocalName, findChildrenByLocalName, getAttribute, getChildElements, parseNumericAttribute } from "./xmlParser.js";
2
2
  import { emuToPixels } from "../utils/units.js";
3
3
  import { parseAnchorPosition, parseAnchorWrap, parseFill, parseOutline, resolveColorValueToHex } from "./drawingUtils.js";
4
4
  //#region src/docx/textBoxParser.ts
@@ -11,6 +11,9 @@ const DEFAULT_MARGIN_EMU = 91440;
11
11
  function parseBodyProperties(bodyPr) {
12
12
  if (!bodyPr) return {};
13
13
  const result = {};
14
+ if (findChildByLocalName(bodyPr, "spAutoFit")) result.autoFit = "shape";
15
+ else if (findChildByLocalName(bodyPr, "normAutofit")) result.autoFit = "normal";
16
+ else if (findChildByLocalName(bodyPr, "noAutofit")) result.autoFit = "none";
14
17
  const lIns = parseNumericAttribute(bodyPr, null, "lIns");
15
18
  const rIns = parseNumericAttribute(bodyPr, null, "rIns");
16
19
  const tIns = parseNumericAttribute(bodyPr, null, "tIns");
@@ -121,6 +124,7 @@ function parseTextBox(drawingEl) {
121
124
  if (fill) textBox.fill = fill;
122
125
  if (outline) textBox.outline = outline;
123
126
  if (bodyProps.margins) textBox.margins = bodyProps.margins;
127
+ if (bodyProps.autoFit) textBox.autoFit = bodyProps.autoFit;
124
128
  if (isAnchor) {
125
129
  const position = parseAnchorPosition(container);
126
130
  if (position) textBox.position = position;
@@ -162,6 +166,7 @@ function parseTextBoxFromShape(wsp, size, position, wrap) {
162
166
  if (fill) textBox.fill = fill;
163
167
  if (outline) textBox.outline = outline;
164
168
  if (bodyProps.margins) textBox.margins = bodyProps.margins;
169
+ if (bodyProps.autoFit) textBox.autoFit = bodyProps.autoFit;
165
170
  if (position) textBox.position = position;
166
171
  if (wrap) textBox.wrap = wrap;
167
172
  return textBox;
@@ -1,8 +1,30 @@
1
- import { cloneWithXmlnsDeclarations, elementToXml, findAllDeep, findChild, getAttribute } from "./xmlParser.js";
1
+ import { cloneWithXmlnsDeclarations, elementToXml, findAllDeep, findChild, getAttribute, getChildElements, getLocalName } from "./xmlParser.js";
2
2
  import { pixelsToEmu } from "../utils/units.js";
3
3
  import { resolveImageData } from "./imageParser.js";
4
4
  import { isWatermarkShape } from "./watermarkParser.js";
5
5
  //#region src/docx/vmlImageParser.ts
6
+ const MAX_VML_PREVIEW_SHAPES = 256;
7
+ const MAX_VML_PREVIEW_DIMENSION_PX = 2e4;
8
+ const MAX_VML_SVG_CHARACTERS = 1e6;
9
+ const SAFE_VML_COLORS = /* @__PURE__ */ new Set([
10
+ "black",
11
+ "white",
12
+ "red",
13
+ "green",
14
+ "blue",
15
+ "yellow",
16
+ "gray",
17
+ "grey",
18
+ "silver",
19
+ "maroon",
20
+ "purple",
21
+ "fuchsia",
22
+ "lime",
23
+ "olive",
24
+ "navy",
25
+ "teal",
26
+ "aqua"
27
+ ]);
6
28
  /**
7
29
  * Convert a CSS length (pt/in/px/cm/mm/pc, default px) to pixels. Units are
8
30
  * matched case-insensitively (`2IN`, `2Pt` are valid VML). The numeric part
@@ -40,6 +62,116 @@ function parseStyleAttr(style) {
40
62
  }
41
63
  return out;
42
64
  }
65
+ const finiteNumber = (raw) => {
66
+ if (!raw || !/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(raw.trim())) return;
67
+ const value = Number.parseFloat(raw);
68
+ return Number.isFinite(value) ? value : void 0;
69
+ };
70
+ const coordinatePair = (raw) => {
71
+ const [firstRaw, secondRaw, extra] = raw?.split(",").map((part) => part.trim()) ?? [];
72
+ if (extra !== void 0) return;
73
+ const first = finiteNumber(firstRaw);
74
+ const second = finiteNumber(secondRaw);
75
+ return first === void 0 || second === void 0 ? void 0 : {
76
+ first,
77
+ second
78
+ };
79
+ };
80
+ const safeColor = (raw, fallback) => {
81
+ const normalized = raw?.trim().toLowerCase();
82
+ if (!normalized) return fallback;
83
+ if (SAFE_VML_COLORS.has(normalized)) return normalized;
84
+ if (/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/u.test(normalized)) return normalized;
85
+ return /^[0-9a-f]{6}$/u.test(normalized) ? `#${normalized}` : fallback;
86
+ };
87
+ const validPreviewDimension = (value) => value !== void 0 && value > 0 && value <= MAX_VML_PREVIEW_DIMENSION_PX;
88
+ const svgDataUrl = (svg) => svg.length <= MAX_VML_SVG_CHARACTERS ? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}` : void 0;
89
+ const vmlShapeSvg = (shape, width, height, x = 0, y = 0) => {
90
+ const fill = safeColor(getAttribute(shape, null, "fillcolor"), "white");
91
+ const stroke = getAttribute(shape, null, "stroked")?.toLowerCase() !== "f" ? safeColor(getAttribute(shape, null, "strokecolor"), "black") : "none";
92
+ const localName = getLocalName(shape.name ?? "");
93
+ if (localName === "oval") return `<ellipse cx="${x + width / 2}" cy="${y + height / 2}" rx="${width / 2}" ry="${height / 2}" fill="${fill}" stroke="${stroke}"/>`;
94
+ return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${localName === "roundrect" ? Math.min(width, height) * .1 : 0}" fill="${fill}" stroke="${stroke}"/>`;
95
+ };
96
+ const previewImage = (pictElement, svg, widthPx, heightPx, style, rootXmlns) => {
97
+ const src = svgDataUrl(svg);
98
+ if (!src) return null;
99
+ const leftPx = cssLengthToPx(style["margin-left"] ?? style["left"]) ?? 0;
100
+ const topPx = cssLengthToPx(style["margin-top"] ?? style["top"]) ?? 0;
101
+ const horizontalRelative = style["mso-position-horizontal-relative"] === "page";
102
+ const verticalRelative = style["mso-position-vertical-relative"] === "page";
103
+ const zIndex = finiteNumber(style["z-index"]);
104
+ return {
105
+ type: "drawing",
106
+ image: {
107
+ type: "image",
108
+ rId: "",
109
+ src,
110
+ mimeType: "image/svg+xml",
111
+ filename: "vml-shape-preview.svg",
112
+ size: {
113
+ width: pixelsToEmu(widthPx),
114
+ height: pixelsToEmu(heightPx)
115
+ },
116
+ wrap: { type: zIndex !== void 0 && zIndex >= 0 ? "inFront" : "behind" },
117
+ position: {
118
+ horizontal: {
119
+ relativeTo: horizontalRelative ? "page" : "character",
120
+ posOffset: pixelsToEmu(leftPx)
121
+ },
122
+ vertical: {
123
+ relativeTo: verticalRelative ? "page" : "paragraph",
124
+ posOffset: pixelsToEmu(topPx)
125
+ }
126
+ }
127
+ },
128
+ rawXml: elementToXml(cloneWithXmlnsDeclarations(pictElement, rootXmlns))
129
+ };
130
+ };
131
+ const parseStandaloneShapePreview = (pictElement, shape, rootXmlns) => {
132
+ const style = parseStyleAttr(getAttribute(shape, null, "style"));
133
+ const widthPx = cssLengthToPx(style["width"]);
134
+ const heightPx = cssLengthToPx(style["height"]);
135
+ if (!validPreviewDimension(widthPx) || !validPreviewDimension(heightPx)) return null;
136
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${widthPx} ${heightPx}" width="${widthPx}" height="${heightPx}">${vmlShapeSvg(shape, widthPx, heightPx)}</svg>`;
137
+ return previewImage(pictElement, svg, widthPx, heightPx, style, rootXmlns);
138
+ };
139
+ const parseGroupPreview = (pictElement, group, rootXmlns) => {
140
+ const style = parseStyleAttr(getAttribute(group, null, "style"));
141
+ const widthPx = cssLengthToPx(style["width"]);
142
+ const heightPx = cssLengthToPx(style["height"]);
143
+ if (!validPreviewDimension(widthPx) || !validPreviewDimension(heightPx)) return null;
144
+ const origin = coordinatePair(getAttribute(group, null, "coordorigin")) ?? {
145
+ first: 0,
146
+ second: 0
147
+ };
148
+ const size = coordinatePair(getAttribute(group, null, "coordsize")) ?? {
149
+ first: widthPx,
150
+ second: heightPx
151
+ };
152
+ if (size.first <= 0 || size.second <= 0) return null;
153
+ const content = getChildElements(group).slice(0, MAX_VML_PREVIEW_SHAPES).map((child) => {
154
+ const localName = getLocalName(child.name ?? "");
155
+ if (localName === "line") {
156
+ const from = coordinatePair(getAttribute(child, null, "from"));
157
+ const to = coordinatePair(getAttribute(child, null, "to"));
158
+ if (!from || !to) return "";
159
+ const stroke = safeColor(getAttribute(child, null, "strokecolor"), "black");
160
+ return `<line x1="${from.first}" y1="${from.second}" x2="${to.first}" y2="${to.second}" stroke="${stroke}"/>`;
161
+ }
162
+ if (localName !== "rect" && localName !== "roundrect" && localName !== "oval") return "";
163
+ const childStyle = parseStyleAttr(getAttribute(child, null, "style"));
164
+ const x = finiteNumber(childStyle["left"]);
165
+ const y = finiteNumber(childStyle["top"]);
166
+ const width = finiteNumber(childStyle["width"]);
167
+ const height = finiteNumber(childStyle["height"]);
168
+ if (x === void 0 || y === void 0 || width === void 0 || height === void 0) return "";
169
+ return vmlShapeSvg(child, width, height, x, y);
170
+ }).join("");
171
+ if (!content) return null;
172
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${origin.first} ${origin.second} ${size.first} ${size.second}" width="${widthPx}" height="${heightPx}">${content}</svg>`;
173
+ return previewImage(pictElement, svg, widthPx, heightPx, style, rootXmlns);
174
+ };
43
175
  /**
44
176
  * Read the relationship id off a `v:imagedata` element. Word writes `r:id`;
45
177
  * some legacy / third-party generators use `r:embed` or the office-namespace
@@ -74,6 +206,7 @@ function parseVmlImageContent(pictElement, rels, media, rootXmlns = {}) {
74
206
  const shapeStyle = parseStyleAttr(getAttribute(shape, null, "style"));
75
207
  const widthPx = cssLengthToPx(shapeStyle["width"]);
76
208
  const heightPx = cssLengthToPx(shapeStyle["height"]);
209
+ if (getLocalName(pictElement.name ?? "") === "object" && (widthPx !== void 0 && !validPreviewDimension(widthPx) || heightPx !== void 0 && !validPreviewDimension(heightPx))) continue;
77
210
  const image = {
78
211
  type: "image",
79
212
  rId,
@@ -94,6 +227,18 @@ function parseVmlImageContent(pictElement, rels, media, rootXmlns = {}) {
94
227
  rawXml: elementToXml(cloneWithXmlnsDeclarations(pictElement, rootXmlns))
95
228
  };
96
229
  }
230
+ for (const child of getChildElements(pictElement)) {
231
+ const localName = getLocalName(child.name ?? "");
232
+ if (localName === "group") {
233
+ const preview = parseGroupPreview(pictElement, child, rootXmlns);
234
+ if (preview) return preview;
235
+ continue;
236
+ }
237
+ if (localName === "rect" || localName === "roundrect" || localName === "oval") {
238
+ const preview = parseStandaloneShapePreview(pictElement, child, rootXmlns);
239
+ if (preview) return preview;
240
+ }
241
+ }
97
242
  return null;
98
243
  }
99
244
  //#endregion