@stll/folio-core 0.8.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 (152) hide show
  1. package/README.md +39 -0
  2. package/dist/ai-edits/apply.js +18 -4
  3. package/dist/ai-edits/headless.d.ts +31 -4
  4. package/dist/ai-edits/headless.js +148 -35
  5. package/dist/ai-edits/index.d.ts +3 -3
  6. package/dist/ai-edits/index.js +2 -1
  7. package/dist/ai-edits/snapshot.js +40 -3
  8. package/dist/ai-edits/types.d.ts +2 -1
  9. package/dist/compat/eigenpal.d.ts +6 -4
  10. package/dist/compat/eigenpal.js +5 -3
  11. package/dist/controller/latestRequestGate.d.ts +13 -0
  12. package/dist/controller/latestRequestGate.js +16 -0
  13. package/dist/controller/layoutPipeline.js +12 -1
  14. package/dist/document-operations.d.ts +10 -3
  15. package/dist/document-operations.js +29 -14
  16. package/dist/docx/blockContentParser.js +2 -2
  17. package/dist/docx/commentParser.js +1 -1
  18. package/dist/docx/compatibility.d.ts +3 -7
  19. package/dist/docx/compatibility.js +0 -11
  20. package/dist/docx/encryption/agileDecryption.js +1 -1
  21. package/dist/docx/encryption/encryptionInfo.js +1 -1
  22. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  23. package/dist/docx/fontTableParser.d.ts +6 -0
  24. package/dist/docx/fontTableParser.js +72 -0
  25. package/dist/docx/groupDrawingParser.js +7 -2
  26. package/dist/docx/headerFooterParser.js +1 -1
  27. package/dist/docx/normalizeBaseDirection.js +1 -1
  28. package/dist/docx/numberingParser.js +1 -0
  29. package/dist/docx/paragraphParser.js +12 -1
  30. package/dist/docx/parser.d.ts +1 -1
  31. package/dist/docx/parser.js +12 -9
  32. package/dist/docx/rezip.js +70 -5
  33. package/dist/docx/runConsolidator.js +4 -0
  34. package/dist/docx/runParser.js +19 -1
  35. package/dist/docx/selectiveSave.js +3 -3
  36. package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
  37. package/dist/docx/serializer/fontTableSerializer.js +26 -0
  38. package/dist/docx/serializer/headerFooterSerializer.js +1 -1
  39. package/dist/docx/serializer/paragraphSerializer.js +4 -0
  40. package/dist/docx/serializer/runSerializer.js +7 -0
  41. package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
  42. package/dist/docx/serializer/settingsSerializer.js +16 -0
  43. package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
  44. package/dist/docx/serializer/stylesSerializer.js +56 -0
  45. package/dist/docx/serializer/tableSerializer.js +25 -26
  46. package/dist/docx/serializer/themeSerializer.d.ts +6 -0
  47. package/dist/docx/serializer/themeSerializer.js +36 -0
  48. package/dist/docx/settingsParser.js +46 -0
  49. package/dist/docx/styleParser.js +26 -1
  50. package/dist/docx/tableParser.js +11 -1
  51. package/dist/docx/vmlImageParser.js +1 -0
  52. package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
  53. package/dist/i18n/messages/catalogs.gen.js +5910 -5910
  54. package/dist/index.d.ts +6 -4
  55. package/dist/index.js +5 -3
  56. package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
  57. package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
  58. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
  59. package/dist/layout-bridge/convert/headerFooterLayout.js +6 -1
  60. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
  61. package/dist/layout-bridge/convert/toFlowBlocks.js +51 -14
  62. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  63. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  64. package/dist/layout-engine/index.js +36 -28
  65. package/dist/layout-engine/measure/cache.js +11 -3
  66. package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
  67. package/dist/layout-engine/measure/index.d.ts +2 -1
  68. package/dist/layout-engine/measure/index.js +2 -1
  69. package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
  70. package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
  71. package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
  72. package/dist/layout-engine/measure/lineBreaks.js +11 -18
  73. package/dist/layout-engine/measure/listMarkerWidth.d.ts +1 -0
  74. package/dist/layout-engine/measure/listMarkerWidth.js +12 -6
  75. package/dist/layout-engine/measure/measureBlocks.d.ts +1 -2
  76. package/dist/layout-engine/measure/measureBlocks.js +6 -36
  77. package/dist/layout-engine/measure/measureContainer.js +32 -9
  78. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  79. package/dist/layout-engine/measure/measureParagraph.js +335 -72
  80. package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
  81. package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
  82. package/dist/layout-engine/measure/measureWorker.js +8 -19
  83. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
  84. package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
  85. package/dist/layout-engine/measure/tableCellFloating.js +37 -29
  86. package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
  87. package/dist/layout-engine/measure/tableCellFlow.js +73 -0
  88. package/dist/layout-engine/measure/tableCellGrid.js +1 -1
  89. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
  90. package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
  91. package/dist/layout-engine/renderedBreakReconciliation.d.ts +6 -1
  92. package/dist/layout-engine/renderedBreakReconciliation.js +34 -11
  93. package/dist/layout-engine/tableRowBreak.d.ts +5 -2
  94. package/dist/layout-engine/tableRowBreak.js +62 -29
  95. package/dist/layout-engine/types.d.ts +40 -7
  96. package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
  97. package/dist/layout-painter/anchoredImagePosition.js +95 -0
  98. package/dist/layout-painter/borderStroke.d.ts +23 -0
  99. package/dist/layout-painter/borderStroke.js +39 -0
  100. package/dist/layout-painter/renderImage.d.ts +5 -4
  101. package/dist/layout-painter/renderImage.js +18 -4
  102. package/dist/layout-painter/renderPage.d.ts +2 -27
  103. package/dist/layout-painter/renderPage.js +13 -99
  104. package/dist/layout-painter/renderParagraph.js +41 -23
  105. package/dist/layout-painter/renderTable.d.ts +2 -0
  106. package/dist/layout-painter/renderTable.js +223 -43
  107. package/dist/managers/DocumentLoaderManager.d.ts +1 -1
  108. package/dist/managers/DocumentLoaderManager.js +2 -2
  109. package/dist/prosemirror/attrs/index.d.ts +4 -2
  110. package/dist/prosemirror/attrs/index.js +21 -2
  111. package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
  112. package/dist/prosemirror/conversion/fromProseDoc.js +33 -18
  113. package/dist/prosemirror/conversion/toProseDoc.js +50 -18
  114. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  115. package/dist/prosemirror/extensions/core/ParagraphExtension.js +7 -2
  116. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
  117. package/dist/prosemirror/extensions/features/ListExtension.js +1 -0
  118. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
  119. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  120. package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
  121. package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
  122. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  123. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  124. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
  125. package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
  126. package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
  127. package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
  128. package/dist/prosemirror/schema/index.d.ts +2 -2
  129. package/dist/prosemirror/schema/marks.d.ts +6 -1
  130. package/dist/prosemirror/schema/nodes.d.ts +13 -11
  131. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  132. package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
  133. package/dist/prosemirror/utils/tabCalculator.js +12 -10
  134. package/dist/prosemirror/validation.js +4 -1
  135. package/dist/redline.js +13 -3
  136. package/dist/server.d.ts +6 -3
  137. package/dist/server.js +5 -2
  138. package/dist/style-sets/extract.d.ts +35 -0
  139. package/dist/style-sets/extract.js +123 -0
  140. package/dist/style-sets/stellaStyle.d.ts +13 -0
  141. package/dist/style-sets/stellaStyle.js +516 -0
  142. package/dist/style-sets/types.d.ts +31 -0
  143. package/dist/style-sets/types.js +5 -0
  144. package/dist/utils/createDocument.d.ts +12 -2
  145. package/dist/utils/createDocument.js +42 -31
  146. package/dist/utils/fontResolver.js +6 -6
  147. package/dist/utils/formatToStyle.js +1 -1
  148. package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
  149. package/dist/utils/paragraphFormattingMerge.js +14 -4
  150. package/package.json +5 -1
  151. package/dist/docx/capabilities.d.ts +0 -41
  152. package/dist/docx/capabilities.js +0 -322
@@ -1,8 +1,9 @@
1
- import { applyReplyThreadMarkers } from "./commentReplyMarkers.js";
2
1
  import { RELATIONSHIP_TYPES, parseRelationships, resolveRelativePath } from "./relsParser.js";
2
+ import { parseNumbering } from "./numberingParser.js";
3
3
  import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
4
4
  import { assertValidFolioDocumentModel } from "./modelValidation.js";
5
- import { parseNumbering } from "./numberingParser.js";
5
+ import { isPreservableDocxEntry } from "./unzip.js";
6
+ import { applyReplyThreadMarkers } from "./commentReplyMarkers.js";
6
7
  import { buildPatchedNoteXml, buildPatchedNumberingXml, collectChangedNumberingDefs, collectParaIds, extractParagraphXml } from "./selectiveXmlPatch.js";
7
8
  import { escapeXml } from "./serializer/xmlUtils.js";
8
9
  import { ensureThreadedCommentParaIds, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
@@ -10,7 +11,10 @@ import { serializeDocument } from "./serializer/documentSerializer.js";
10
11
  import { serializeHeaderFooter } from "./serializer/headerFooterSerializer.js";
11
12
  import { serializeEndnotes, serializeFootnotes } from "./serializer/noteSerializer.js";
12
13
  import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
13
- import { isPreservableDocxEntry } from "./unzip.js";
14
+ import { serializeFontTableXml } from "./serializer/fontTableSerializer.js";
15
+ import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
16
+ import { serializeStylesXml } from "./serializer/stylesSerializer.js";
17
+ import { serializeThemeXml } from "./serializer/themeSerializer.js";
14
18
  import { panic } from "better-result";
15
19
  import JSZip from "jszip";
16
20
  //#region src/docx/rezip.ts
@@ -1169,7 +1173,7 @@ function createEmptyDocx() {
1169
1173
  zip.file("docProps/app.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1170
1174
  <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
1171
1175
  <Application>EigenPal DOCX Editor</Application>
1172
- <AppVersion>1.0.0</AppVersion>
1176
+ <AppVersion>1.0000</AppVersion>
1173
1177
  </Properties>`);
1174
1178
  return zip.generateAsync({
1175
1179
  type: "arraybuffer",
@@ -1184,11 +1188,72 @@ function createEmptyDocx() {
1184
1188
  * @returns Promise resolving to DOCX as ArrayBuffer
1185
1189
  */
1186
1190
  async function createDocx(doc) {
1187
- const emptyBuffer = await createEmptyDocx();
1191
+ const emptyBuffer = await createDocumentSeedDocx(doc);
1188
1192
  return repackDocx({
1189
1193
  ...doc,
1190
1194
  originalBuffer: emptyBuffer
1191
1195
  });
1192
1196
  }
1197
+ const createDocumentSeedDocx = async (doc) => {
1198
+ const buffer = await createEmptyDocx();
1199
+ const zip = await JSZip.loadAsync(buffer);
1200
+ const relationships = ["<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>"];
1201
+ const overrides = [];
1202
+ let nextRelationshipId = 2;
1203
+ if (doc.package.styles) {
1204
+ assertStyleNumberingReferences(doc);
1205
+ zip.file("word/styles.xml", serializeStylesXml(doc.package.styles));
1206
+ }
1207
+ const numbering = doc.package.numbering;
1208
+ if (numbering && (numbering.abstractNums.length > 0 || numbering.nums.length > 0)) {
1209
+ zip.file("word/numbering.xml", serializeNumberingXml(numbering));
1210
+ relationships.push(relationshipXml(nextRelationshipId, "numbering", "numbering.xml"));
1211
+ overrides.push(overrideXml("/word/numbering.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"));
1212
+ nextRelationshipId += 1;
1213
+ }
1214
+ if (doc.package.theme) {
1215
+ zip.file("word/theme/theme1.xml", serializeThemeXml(doc.package.theme));
1216
+ relationships.push(relationshipXml(nextRelationshipId, "theme", "theme/theme1.xml"));
1217
+ overrides.push(overrideXml("/word/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"));
1218
+ nextRelationshipId += 1;
1219
+ }
1220
+ if (doc.package.fontTable && doc.package.fontTable.fonts.length > 0) {
1221
+ zip.file("word/fontTable.xml", serializeFontTableXml(doc.package.fontTable));
1222
+ relationships.push(relationshipXml(nextRelationshipId, "fontTable", "fontTable.xml"));
1223
+ overrides.push(overrideXml("/word/fontTable.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"));
1224
+ nextRelationshipId += 1;
1225
+ }
1226
+ if (doc.package.settings) {
1227
+ zip.file("word/settings.xml", serializeSettingsXml(doc.package.settings));
1228
+ relationships.push(relationshipXml(nextRelationshipId, "settings", "settings.xml"));
1229
+ overrides.push(overrideXml("/word/settings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"));
1230
+ }
1231
+ zip.file("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`);
1232
+ if (overrides.length > 0) {
1233
+ const contentTypesFile = zip.file("[Content_Types].xml");
1234
+ if (!contentTypesFile) panic("Fresh DOCX seed is missing [Content_Types].xml");
1235
+ const contentTypes = await contentTypesFile.async("string");
1236
+ zip.file("[Content_Types].xml", contentTypes.replace("</Types>", `${overrides.join("")}\n</Types>`));
1237
+ }
1238
+ return zip.generateAsync({
1239
+ type: "arraybuffer",
1240
+ compression: "DEFLATE",
1241
+ compressionOptions: { level: 6 }
1242
+ });
1243
+ };
1244
+ const relationshipXml = (id, type, target) => `<Relationship Id="rId${id}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/${type}" Target="${target}"/>`;
1245
+ const overrideXml = (partName, contentType) => `<Override PartName="${partName}" ContentType="${contentType}"/>`;
1246
+ const assertStyleNumberingReferences = (doc) => {
1247
+ const referenced = /* @__PURE__ */ new Set();
1248
+ for (const style of doc.package.styles?.styles ?? []) {
1249
+ const numId = style.pPr?.numPr?.numId;
1250
+ if (numId !== void 0) referenced.add(numId);
1251
+ }
1252
+ if (referenced.size === 0) return;
1253
+ const available = new Set(doc.package.numbering?.nums.map((numbering) => numbering.numId) ?? []);
1254
+ for (const numId of referenced) if (!available.has(numId)) panic(`Style references missing numbering definition ${numId}`);
1255
+ const availableAbstract = new Set(doc.package.numbering?.abstractNums.map((numbering) => numbering.abstractNumId) ?? []);
1256
+ for (const numbering of doc.package.numbering?.nums ?? []) if (!availableAbstract.has(numbering.abstractNumId)) panic(`Numbering definition ${numbering.numId} references missing abstract numbering`);
1257
+ };
1193
1258
  //#endregion
1194
1259
  export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx };
@@ -38,8 +38,12 @@ function formattingEquals(a, b) {
38
38
  if (!colorEquals(a.color, b.color)) return false;
39
39
  if (!shadingEquals(a.shading, b.shading)) return false;
40
40
  if (!fontFamilyEquals(a.fontFamily, b.fontFamily)) return false;
41
+ if (!languageEquals(a.language, b.language)) return false;
41
42
  return true;
42
43
  }
44
+ function languageEquals(a, b) {
45
+ return a?.val === b?.val && a?.eastAsia === b?.eastAsia && a?.bidi === b?.bidi;
46
+ }
43
47
  /**
44
48
  * Compare underline settings
45
49
  */
@@ -89,6 +89,9 @@ function collectFirstRunPropertyChildren(rPr) {
89
89
  case "kern":
90
90
  children.kern ??= child;
91
91
  break;
92
+ case "lang":
93
+ children.lang ??= child;
94
+ break;
92
95
  case "outline":
93
96
  children.outline ??= child;
94
97
  break;
@@ -262,6 +265,17 @@ function parseRunProperties(rPr, theme, _styles) {
262
265
  }
263
266
  formatting.fontFamily = fontFamily;
264
267
  }
268
+ const lang = propertyChildren.lang;
269
+ if (lang) {
270
+ const val = getAttribute(lang, "w", "val") || void 0;
271
+ const eastAsia = getAttribute(lang, "w", "eastAsia") || void 0;
272
+ const bidi = getAttribute(lang, "w", "bidi") || void 0;
273
+ if (val || eastAsia || bidi) formatting.language = {
274
+ ...val ? { val } : {},
275
+ ...eastAsia ? { eastAsia } : {},
276
+ ...bidi ? { bidi } : {}
277
+ };
278
+ }
265
279
  const spacing = propertyChildren.spacing;
266
280
  if (spacing) {
267
281
  const val = parseNumericAttribute(spacing, "w", "val");
@@ -531,7 +545,11 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
531
545
  if (vmlDrawing) contents.push(vmlDrawing);
532
546
  break;
533
547
  }
534
- case "object": break;
548
+ case "object": {
549
+ const objectPreview = parseVmlImageContent(child, rels, media, rootXmlns);
550
+ if (objectPreview) contents.push(objectPreview);
551
+ break;
552
+ }
535
553
  case "rPr": break;
536
554
  case "lastRenderedPageBreak": break;
537
555
  case "cr":
@@ -1,14 +1,14 @@
1
- import { hasUnsynthesizedReplyRanges } from "./commentReplyMarkers.js";
2
1
  import { RELATIONSHIP_TYPES } from "./relsParser.js";
3
- import { validateFolioDocumentModel } from "./modelValidation.js";
2
+ import { parseCommentsExtended } from "./commentParser.js";
4
3
  import { parseNumbering } from "./numberingParser.js";
4
+ import { validateFolioDocumentModel } from "./modelValidation.js";
5
+ import { hasUnsynthesizedReplyRanges } from "./commentReplyMarkers.js";
5
6
  import { buildPatchedDocumentXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectChangedNumberingDefs, collectParaIds } from "./selectiveXmlPatch.js";
6
7
  import { ensureThreadedCommentParaIds, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
7
8
  import { serializeDocument } from "./serializer/documentSerializer.js";
8
9
  import { serializeEndnotes, serializeFootnotes } from "./serializer/noteSerializer.js";
9
10
  import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
10
11
  import { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_PART_LOWER, addCommentsExtendedOverride, addCommentsExtendedRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, updateCoreProperties } from "./rezip.js";
11
- import { parseCommentsExtended } from "./commentParser.js";
12
12
  import "./selectiveSaveFlags.js";
13
13
  //#region src/docx/selectiveSave.ts
14
14
  const SYNTHETIC_IMAGE_RID_PREFIX = "rId_img_";
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+
3
+ //#region src/docx/serializer/fontTableSerializer.d.ts
4
+ declare const serializeFontTableXml: (fontTable: document_d_exports.FontTable) => string;
5
+ //#endregion
6
+ export { serializeFontTableXml };
@@ -0,0 +1,26 @@
1
+ import { escapeXml } from "./xmlUtils.js";
2
+ //#region src/docx/serializer/fontTableSerializer.ts
3
+ const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
4
+ const serializeFontTableXml = (fontTable) => {
5
+ const fonts = fontTable.fonts.map(serializeFont).join("");
6
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:fonts xmlns:w="${W_NS}">${fonts}</w:fonts>`;
7
+ };
8
+ const serializeFont = (font) => {
9
+ const parts = [];
10
+ if (font.altName) parts.push(`<w:altName w:val="${escapeXml(font.altName)}"/>`);
11
+ if (font.panose1) parts.push(`<w:panose1 w:val="${escapeXml(font.panose1)}"/>`);
12
+ if (font.charset) parts.push(`<w:charset w:val="${escapeXml(font.charset)}"/>`);
13
+ if (font.family) parts.push(`<w:family w:val="${font.family}"/>`);
14
+ if (font.pitch) parts.push(`<w:pitch w:val="${font.pitch}"/>`);
15
+ if (font.sig) {
16
+ const attrs = [];
17
+ for (const [key, value] of Object.entries(font.sig)) {
18
+ if (value === void 0) continue;
19
+ attrs.push(`w:${key}="${escapeXml(value)}"`);
20
+ }
21
+ if (attrs.length > 0) parts.push(`<w:sig ${attrs.join(" ")}/>`);
22
+ }
23
+ return `<w:font w:name="${escapeXml(font.name)}">${parts.join("")}</w:font>`;
24
+ };
25
+ //#endregion
26
+ export { serializeFontTableXml };
@@ -1,8 +1,8 @@
1
+ import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
1
2
  import { escapeXml } from "./xmlUtils.js";
2
3
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
3
4
  import { serializeParagraph } from "./paragraphSerializer.js";
4
5
  import { serializeTable } from "./tableSerializer.js";
5
- import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
6
6
  //#region src/docx/serializer/headerFooterSerializer.ts
7
7
  const NAMESPACES = {
8
8
  wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
@@ -165,6 +165,8 @@ function serializeParagraphFormatting(formatting, propertyChanges, pPrMark) {
165
165
  if (tabsXml) parts.push(tabsXml);
166
166
  pushToggle("suppressLineNumbers", formatting.suppressLineNumbers);
167
167
  pushToggle("suppressAutoHyphens", formatting.suppressAutoHyphens);
168
+ pushToggle("kinsoku", formatting.kinsoku);
169
+ pushToggle("overflowPunct", formatting.overflowPunctuation);
168
170
  const spacingXml = serializeSpacing(formatting);
169
171
  if (spacingXml) parts.push(spacingXml);
170
172
  const indXml = serializeIndentation(formatting);
@@ -350,6 +352,8 @@ function serializeInlineSdt(sdt) {
350
352
  case "simpleField": return serializeSimpleField(item);
351
353
  case "complexField": return serializeComplexField(item);
352
354
  case "inlineSdt": return serializeInlineSdt(item);
355
+ case "insertion": return serializeTrackedChange("ins", item);
356
+ case "deletion": return serializeTrackedChange("del", item);
353
357
  case "mathEquation": return item.ommlXml || "";
354
358
  default: return item;
355
359
  }
@@ -85,6 +85,13 @@ function serializeTextFormatting(formatting) {
85
85
  if (formatting.fontFamily.csTheme) fontAttrs.push(`w:cstheme="${formatting.fontFamily.csTheme}"`);
86
86
  if (fontAttrs.length > 0) parts.push(`<w:rFonts ${fontAttrs.join(" ")}/>`);
87
87
  }
88
+ if (formatting.language) {
89
+ const languageAttrs = [];
90
+ if (formatting.language.val) languageAttrs.push(`w:val="${escapeXml(formatting.language.val)}"`);
91
+ if (formatting.language.eastAsia) languageAttrs.push(`w:eastAsia="${escapeXml(formatting.language.eastAsia)}"`);
92
+ if (formatting.language.bidi) languageAttrs.push(`w:bidi="${escapeXml(formatting.language.bidi)}"`);
93
+ if (languageAttrs.length > 0) parts.push(`<w:lang ${languageAttrs.join(" ")}/>`);
94
+ }
88
95
  if (formatting.bold === true) parts.push("<w:b/>");
89
96
  else if (formatting.bold === false) parts.push("<w:b w:val=\"0\"/>");
90
97
  if (formatting.boldCs === true) parts.push("<w:bCs/>");
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+
3
+ //#region src/docx/serializer/settingsSerializer.d.ts
4
+ declare const serializeSettingsXml: (settings: document_d_exports.DocumentSettings) => string;
5
+ //#endregion
6
+ export { serializeSettingsXml };
@@ -0,0 +1,16 @@
1
+ import { escapeXml, intAttr } from "./xmlUtils.js";
2
+ //#region src/docx/serializer/settingsSerializer.ts
3
+ const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
4
+ const serializeSettingsXml = (settings) => {
5
+ const parts = [`<w:defaultTabStop w:val="${intAttr(settings.defaultTabStop)}"/>`];
6
+ if (settings.evenAndOddHeaders) parts.push("<w:evenAndOddHeaders/>");
7
+ if (settings.themeFontLang) {
8
+ const attrs = [];
9
+ if (settings.themeFontLang.eastAsia) attrs.push(`w:eastAsia="${escapeXml(settings.themeFontLang.eastAsia)}"`);
10
+ if (settings.themeFontLang.bidi) attrs.push(`w:bidi="${escapeXml(settings.themeFontLang.bidi)}"`);
11
+ if (attrs.length > 0) parts.push(`<w:themeFontLang ${attrs.join(" ")}/>`);
12
+ }
13
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:settings xmlns:w="${W_NS}">${parts.join("")}</w:settings>`;
14
+ };
15
+ //#endregion
16
+ export { serializeSettingsXml };
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+
3
+ //#region src/docx/serializer/stylesSerializer.d.ts
4
+ declare const serializeStylesXml: (definitions: document_d_exports.StyleDefinitions) => string;
5
+ //#endregion
6
+ export { serializeStylesXml };
@@ -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
  */
@@ -238,7 +237,7 @@ function serializeTableCellFormatting(formatting, propertyChanges, structuralCha
238
237
  if (formatting.gridSpan && formatting.gridSpan > 1) parts.push(`<w:gridSpan w:val="${intAttr(formatting.gridSpan)}"/>`);
239
238
  if (formatting.vMerge) if (formatting.vMerge === "restart") parts.push("<w:vMerge w:val=\"restart\"/>");
240
239
  else parts.push("<w:vMerge/>");
241
- const bordersXml = serializeTableBorders(formatting.borders, "tcBorders");
240
+ const bordersXml = serializeTableCellBorders(formatting.borders);
242
241
  if (bordersXml) parts.push(bordersXml);
243
242
  const shadingXml = serializeShading(formatting.shading);
244
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
  *
@@ -446,7 +456,7 @@ function parseTableCellProperties(tcPrElement) {
446
456
  const formatting = {};
447
457
  const width = parseWidth(findChild(tcPrElement, "w", "tcW"));
448
458
  if (width) formatting.width = width;
449
- const borders = parseTableBorders(findChild(tcPrElement, "w", "tcBorders"));
459
+ const borders = parseTableCellBorders(findChild(tcPrElement, "w", "tcBorders"));
450
460
  if (borders) formatting.borders = borders;
451
461
  const margins = parseCellMargins(findChild(tcPrElement, "w", "tcMar"));
452
462
  if (margins) formatting.margins = margins;
@@ -206,6 +206,7 @@ function parseVmlImageContent(pictElement, rels, media, rootXmlns = {}) {
206
206
  const shapeStyle = parseStyleAttr(getAttribute(shape, null, "style"));
207
207
  const widthPx = cssLengthToPx(shapeStyle["width"]);
208
208
  const heightPx = cssLengthToPx(shapeStyle["height"]);
209
+ if (getLocalName(pictElement.name ?? "") === "object" && (widthPx !== void 0 && !validPreviewDimension(widthPx) || heightPx !== void 0 && !validPreviewDimension(heightPx))) continue;
209
210
  const image = {
210
211
  type: "image",
211
212
  rId,