@bendyline/squisq-formats 2.0.1 → 2.2.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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +20 -0
  3. package/README.md +1 -1
  4. package/dist/{chunk-CRAVSMPZ.js → chunk-26ISNJ7Y.js} +356 -114
  5. package/dist/{chunk-NKAJPJ4G.js → chunk-2JJ5RFDZ.js} +0 -1
  6. package/dist/{chunk-HTW2M27H.js → chunk-3NKXBZSR.js} +193 -42
  7. package/dist/{chunk-U32AG3G3.js → chunk-4V3KCHAP.js} +3 -4
  8. package/dist/{chunk-MLX2BOJC.js → chunk-6RQOV3B3.js} +1 -2
  9. package/dist/{chunk-QRVN6A6E.js → chunk-6S6GU3ZG.js} +5 -6
  10. package/dist/{chunk-FE6OJV6O.js → chunk-7AWFHP5U.js} +1 -1
  11. package/dist/{chunk-RFAPOKHJ.js → chunk-AD2WT564.js} +59 -9
  12. package/dist/{chunk-O3GVVND4.js → chunk-AONELFLA.js} +0 -1
  13. package/dist/{chunk-XKUMNGBW.js → chunk-EJTNGKEA.js} +5 -8
  14. package/dist/chunk-GX7RAUME.js +121 -0
  15. package/dist/{chunk-SSUPBUF5.js → chunk-IIQYS2YH.js} +0 -1
  16. package/dist/{chunk-ABVI556T.js → chunk-IPN56VLW.js} +83 -58
  17. package/dist/{chunk-LXYLOOST.js → chunk-JE6LSIHE.js} +83 -22
  18. package/dist/{chunk-U4MRIFKL.js → chunk-JU2RHXUB.js} +0 -1
  19. package/dist/{chunk-4VUWTSGM.js → chunk-K6XRMVPW.js} +64 -31
  20. package/dist/{chunk-ODL3SSPT.js → chunk-KXOZMWBS.js} +0 -1
  21. package/dist/chunk-OGS5VCGJ.js +446 -0
  22. package/dist/{chunk-GVS2XXV6.js → chunk-PJXJI2LY.js} +449 -57
  23. package/dist/{chunk-2KPARF2P.js → chunk-PU7REGWV.js} +5 -8
  24. package/dist/{chunk-PN52A5AA.js → chunk-SBUW7NHR.js} +0 -1
  25. package/dist/{chunk-VSYHZECT.js → chunk-TAAENIRB.js} +5 -8
  26. package/dist/{chunk-WC7WULGV.js → chunk-X2DEAXNK.js} +62 -2
  27. package/dist/container/index.js +1 -2
  28. package/dist/csv/index.d.ts +27 -2
  29. package/dist/csv/index.js +1 -2
  30. package/dist/docx/index.d.ts +5 -1
  31. package/dist/docx/index.js +9 -10
  32. package/dist/epub/index.d.ts +2 -0
  33. package/dist/epub/index.js +5 -6
  34. package/dist/{export-D2NkylDT.d.ts → export-D9msROJS.d.ts} +18 -6
  35. package/dist/extract-MN7LA3NL.js +13 -0
  36. package/dist/html/index.d.ts +11 -4
  37. package/dist/html/index.js +3 -4
  38. package/dist/images-ESPQKVTW.js +6 -0
  39. package/dist/{import-K8mfc0fz.d.ts → import-C3htUTss.d.ts} +5 -1
  40. package/dist/{import-DTkDxHmZ.d.ts → import-C8whCC7_.d.ts} +6 -0
  41. package/dist/index.d.ts +7 -7
  42. package/dist/index.js +28 -26
  43. package/dist/infer/index.d.ts +3 -3
  44. package/dist/infer/index.js +7 -9
  45. package/dist/{layouts-BHrgZ5FS.d.ts → layouts-CTdPlB-u.d.ts} +1 -1
  46. package/dist/layouts-DRWZGSPD.js +10 -0
  47. package/dist/{mapTheme-IR27S6IV.js → mapTheme-4TWH25FT.js} +1 -2
  48. package/dist/ooxml/index.d.ts +3 -3
  49. package/dist/ooxml/index.js +14 -13
  50. package/dist/pdf/index.d.ts +18 -0
  51. package/dist/pdf/index.js +2 -3
  52. package/dist/pptx/index.d.ts +4 -4
  53. package/dist/pptx/index.js +11 -13
  54. package/dist/{reader-B9L8Ucbj.d.ts → reader-B_m1aKZC.d.ts} +30 -1
  55. package/dist/registry/index.d.ts +21 -5
  56. package/dist/registry/index.js +9 -6
  57. package/dist/{themeReader-DJKErl_j.d.ts → themeReader-DCtwC83Q.d.ts} +1 -1
  58. package/dist/xlsx/index.d.ts +3 -3
  59. package/dist/xlsx/index.js +6 -7
  60. package/package.json +6 -3
  61. package/dist/chunk-2KPARF2P.js.map +0 -1
  62. package/dist/chunk-4VUWTSGM.js.map +0 -1
  63. package/dist/chunk-6M7Z25LA.js +0 -46
  64. package/dist/chunk-6M7Z25LA.js.map +0 -1
  65. package/dist/chunk-ABVI556T.js.map +0 -1
  66. package/dist/chunk-CRAVSMPZ.js.map +0 -1
  67. package/dist/chunk-FE6OJV6O.js.map +0 -1
  68. package/dist/chunk-GVS2XXV6.js.map +0 -1
  69. package/dist/chunk-HTW2M27H.js.map +0 -1
  70. package/dist/chunk-LXYLOOST.js.map +0 -1
  71. package/dist/chunk-MLX2BOJC.js.map +0 -1
  72. package/dist/chunk-NKAJPJ4G.js.map +0 -1
  73. package/dist/chunk-O3GVVND4.js.map +0 -1
  74. package/dist/chunk-ODL3SSPT.js.map +0 -1
  75. package/dist/chunk-PN52A5AA.js.map +0 -1
  76. package/dist/chunk-QRVN6A6E.js.map +0 -1
  77. package/dist/chunk-RFAPOKHJ.js.map +0 -1
  78. package/dist/chunk-SSUPBUF5.js.map +0 -1
  79. package/dist/chunk-U32AG3G3.js.map +0 -1
  80. package/dist/chunk-U4MRIFKL.js.map +0 -1
  81. package/dist/chunk-VJJM2SSH.js +0 -275
  82. package/dist/chunk-VJJM2SSH.js.map +0 -1
  83. package/dist/chunk-VSYHZECT.js.map +0 -1
  84. package/dist/chunk-WC7WULGV.js.map +0 -1
  85. package/dist/chunk-XKUMNGBW.js.map +0 -1
  86. package/dist/chunk-YRT7GQ5Y.js +0 -28
  87. package/dist/chunk-YRT7GQ5Y.js.map +0 -1
  88. package/dist/container/index.js.map +0 -1
  89. package/dist/csv/index.js.map +0 -1
  90. package/dist/docx/index.js.map +0 -1
  91. package/dist/epub/index.js.map +0 -1
  92. package/dist/extract-H6RXJMHP.js +0 -15
  93. package/dist/extract-H6RXJMHP.js.map +0 -1
  94. package/dist/html/index.js.map +0 -1
  95. package/dist/images-7FBWPKE3.js +0 -7
  96. package/dist/images-7FBWPKE3.js.map +0 -1
  97. package/dist/index.js.map +0 -1
  98. package/dist/infer/index.js.map +0 -1
  99. package/dist/layouts-QVPK3ZCU.js +0 -12
  100. package/dist/layouts-QVPK3ZCU.js.map +0 -1
  101. package/dist/mapTheme-IR27S6IV.js.map +0 -1
  102. package/dist/ooxml/index.js.map +0 -1
  103. package/dist/pdf/index.js.map +0 -1
  104. package/dist/pptx/index.js.map +0 -1
  105. package/dist/registry/index.js.map +0 -1
  106. package/dist/xlsx/index.js.map +0 -1
  107. package/src/__tests__/container.test.ts +0 -230
  108. package/src/__tests__/convert.test.ts +0 -495
  109. package/src/__tests__/csvImport.test.ts +0 -84
  110. package/src/__tests__/docxExport.test.ts +0 -457
  111. package/src/__tests__/docxImport.test.ts +0 -410
  112. package/src/__tests__/epub.test.ts +0 -649
  113. package/src/__tests__/exportThemeReconciliation.test.ts +0 -87
  114. package/src/__tests__/formatRegistry.test.ts +0 -174
  115. package/src/__tests__/html.test.ts +0 -435
  116. package/src/__tests__/htmlImport.test.ts +0 -57
  117. package/src/__tests__/inferTheme.test.ts +0 -135
  118. package/src/__tests__/lossyWarnings.test.ts +0 -146
  119. package/src/__tests__/ooxml.test.ts +0 -271
  120. package/src/__tests__/ooxmlCancellation.test.ts +0 -113
  121. package/src/__tests__/ooxmlThemeReader.test.ts +0 -92
  122. package/src/__tests__/pdfExport.test.ts +0 -322
  123. package/src/__tests__/pdfImport.test.ts +0 -384
  124. package/src/__tests__/plainHtml.test.ts +0 -417
  125. package/src/__tests__/plainHtmlBundle.test.ts +0 -253
  126. package/src/__tests__/pptxExport.test.ts +0 -138
  127. package/src/__tests__/pptxImport.test.ts +0 -145
  128. package/src/__tests__/pptxInferFixtures.ts +0 -314
  129. package/src/__tests__/pptxLayoutInfer.test.ts +0 -395
  130. package/src/__tests__/roundTrip.test.ts +0 -201
  131. package/src/__tests__/roundTripAssets.test.ts +0 -50
  132. package/src/__tests__/roundTripMatrix.fixtures.ts +0 -86
  133. package/src/__tests__/roundTripMatrix.helpers.ts +0 -154
  134. package/src/__tests__/roundTripMatrix.test.ts +0 -142
  135. package/src/__tests__/sharedContainer.test.ts +0 -41
  136. package/src/__tests__/sharedImages.test.ts +0 -61
  137. package/src/__tests__/xlsxExport.test.ts +0 -164
  138. package/src/__tests__/xlsxImport.test.ts +0 -80
  139. package/src/__tests__/zipSafety.test.ts +0 -317
  140. package/src/container/index.ts +0 -94
  141. package/src/csv/index.ts +0 -188
  142. package/src/docx/export.ts +0 -1267
  143. package/src/docx/import.ts +0 -995
  144. package/src/docx/index.ts +0 -26
  145. package/src/docx/styles.ts +0 -145
  146. package/src/epub/export.ts +0 -968
  147. package/src/epub/index.ts +0 -20
  148. package/src/html/docsHtmlBundle.ts +0 -373
  149. package/src/html/htmlTemplate.ts +0 -385
  150. package/src/html/imageUtils.ts +0 -61
  151. package/src/html/import.ts +0 -297
  152. package/src/html/index.ts +0 -212
  153. package/src/html/plainHtml.ts +0 -790
  154. package/src/html/plainHtmlBundle.ts +0 -421
  155. package/src/index.ts +0 -109
  156. package/src/infer/extract.ts +0 -127
  157. package/src/infer/index.ts +0 -199
  158. package/src/infer/mapTheme.ts +0 -176
  159. package/src/infer/types.ts +0 -27
  160. package/src/ooxml/index.ts +0 -111
  161. package/src/ooxml/namespaces.ts +0 -196
  162. package/src/ooxml/readUtils.ts +0 -44
  163. package/src/ooxml/reader.ts +0 -318
  164. package/src/ooxml/themeReader.ts +0 -197
  165. package/src/ooxml/types.ts +0 -103
  166. package/src/ooxml/writer.ts +0 -339
  167. package/src/ooxml/xmlUtils.ts +0 -123
  168. package/src/pdf/export.ts +0 -1084
  169. package/src/pdf/import.ts +0 -1164
  170. package/src/pdf/index.ts +0 -29
  171. package/src/pdf/styles.ts +0 -180
  172. package/src/pptx/export.ts +0 -1184
  173. package/src/pptx/import.ts +0 -455
  174. package/src/pptx/index.ts +0 -52
  175. package/src/pptx/layouts.ts +0 -1222
  176. package/src/pptx/styles.ts +0 -96
  177. package/src/pptx/templates.ts +0 -187
  178. package/src/registry/convert.ts +0 -433
  179. package/src/registry/defaultFormats.ts +0 -413
  180. package/src/registry/errors.ts +0 -46
  181. package/src/registry/index.ts +0 -43
  182. package/src/registry/registry.ts +0 -48
  183. package/src/registry/types.ts +0 -170
  184. package/src/shared/boundedZipArchive.ts +0 -383
  185. package/src/shared/container.ts +0 -28
  186. package/src/shared/fidelity.ts +0 -130
  187. package/src/shared/images.ts +0 -44
  188. package/src/shared/inlineRuns.ts +0 -99
  189. package/src/shared/text.ts +0 -41
  190. package/src/shared/zipEntryCount.ts +0 -151
  191. package/src/shared/zipLimits.ts +0 -296
  192. package/src/shared/zipSafety.ts +0 -19
  193. package/src/xlsx/export.ts +0 -253
  194. package/src/xlsx/import.ts +0 -160
  195. package/src/xlsx/index.ts +0 -35
@@ -1,21 +1,26 @@
1
1
  import {
2
+ RelIdAllocator,
2
3
  inlineNodeToRuns,
3
- inlineNodesToRuns
4
- } from "./chunk-6M7Z25LA.js";
4
+ inlineNodesToRuns,
5
+ normalizeOoxmlHex,
6
+ sanitizeOfficeHyperlink
7
+ } from "./chunk-GX7RAUME.js";
5
8
  import {
6
9
  stripHtmlTags
7
- } from "./chunk-PN52A5AA.js";
10
+ } from "./chunk-SBUW7NHR.js";
8
11
  import {
9
12
  createPackage
10
- } from "./chunk-U32AG3G3.js";
13
+ } from "./chunk-4V3KCHAP.js";
11
14
  import {
12
15
  escapeXml,
13
16
  xmlDeclaration
14
- } from "./chunk-U4MRIFKL.js";
17
+ } from "./chunk-JU2RHXUB.js";
15
18
  import {
16
19
  CONTENT_TYPE_DOCX_DOCUMENT,
17
20
  CONTENT_TYPE_DOCX_FONT_TABLE,
21
+ CONTENT_TYPE_DOCX_FOOTER,
18
22
  CONTENT_TYPE_DOCX_FOOTNOTES,
23
+ CONTENT_TYPE_DOCX_HEADER,
19
24
  CONTENT_TYPE_DOCX_NUMBERING,
20
25
  CONTENT_TYPE_DOCX_SETTINGS,
21
26
  CONTENT_TYPE_DOCX_STYLES,
@@ -23,24 +28,29 @@ import {
23
28
  NS_R,
24
29
  NS_WML,
25
30
  REL_FONT_TABLE,
31
+ REL_FOOTER,
26
32
  REL_FOOTNOTES,
33
+ REL_HEADER,
27
34
  REL_HYPERLINK,
28
35
  REL_IMAGE,
29
36
  REL_NUMBERING,
30
37
  REL_OFFICE_DOCUMENT,
31
38
  REL_SETTINGS,
32
39
  REL_STYLES,
40
+ baseDirOf,
33
41
  getPartBinary,
34
42
  getPartRelationships,
35
43
  getPartXml,
36
- openPackage
37
- } from "./chunk-WC7WULGV.js";
44
+ openPackage,
45
+ requireMainPartPath,
46
+ resolveTarget
47
+ } from "./chunk-X2DEAXNK.js";
38
48
  import {
39
49
  buildContainer
40
- } from "./chunk-SSUPBUF5.js";
50
+ } from "./chunk-IIQYS2YH.js";
41
51
  import {
42
52
  extToMime
43
- } from "./chunk-O3GVVND4.js";
53
+ } from "./chunk-AONELFLA.js";
44
54
 
45
55
  // src/docx/export.ts
46
56
  import { resolveFontFamily } from "@bendyline/squisq/schemas";
@@ -103,10 +113,31 @@ function pointsToTwips(points) {
103
113
 
104
114
  // src/docx/export.ts
105
115
  async function markdownDocToDocx(doc, options = {}) {
116
+ options.signal?.throwIfAborted();
106
117
  const resolvedOptions = options.themeId !== void 0 ? options : { ...options, themeId: readFrontmatterThemeId(doc.frontmatter) };
107
118
  const ctx = new ExportContext(resolvedOptions, doc);
108
- const bodyXml = convertBlocks(doc.children, ctx);
109
- return buildDocxPackage(bodyXml, ctx, resolvedOptions);
119
+ const stories = partitionDocumentStories(doc.children);
120
+ ctx.setRelationshipSource("document");
121
+ const bodyXml = convertBlocks(stories.body, ctx);
122
+ ctx.setRelationshipSource("header");
123
+ const headerXml = convertBlocks(stories.header, ctx);
124
+ ctx.setRelationshipSource("footer");
125
+ const footerXml = convertBlocks(stories.footer, ctx);
126
+ ctx.setRelationshipSource("document");
127
+ return buildDocxPackage(bodyXml, headerXml, footerXml, ctx, resolvedOptions);
128
+ }
129
+ function partitionDocumentStories(nodes) {
130
+ const stories = { body: [], header: [], footer: [] };
131
+ for (const node of nodes) {
132
+ if (node.type === "containerDirective" && node.name === "docx-header") {
133
+ stories.header.push(...node.children);
134
+ } else if (node.type === "containerDirective" && node.name === "docx-footer") {
135
+ stories.footer.push(...node.children);
136
+ } else {
137
+ stories.body.push(node);
138
+ }
139
+ }
140
+ return stories;
110
141
  }
111
142
  async function docToDocx(doc, options = {}) {
112
143
  const markdownDoc = docToMarkdown(doc);
@@ -114,12 +145,20 @@ async function docToDocx(doc, options = {}) {
114
145
  }
115
146
  var ExportContext = class {
116
147
  constructor(options, doc) {
117
- this.nextRelId = 1;
148
+ /**
149
+ * One id source per rels part — see `RelIdAllocator`. Hyperlink, image AND
150
+ * fixed (styles/numbering/settings/…) rels all draw from this, keyed by the
151
+ * story part they belong to, so `word/_rels/document.xml.rels` cannot
152
+ * contain a duplicate `Id` no matter how many hyperlinks/images a document
153
+ * has.
154
+ */
155
+ this.relIds = new RelIdAllocator();
118
156
  this.nextNumId = 1;
119
157
  this.nextFootnoteId = 1;
120
158
  // 0 is separator, start user footnotes at 1
121
159
  /** Relationships for word/_rels/document.xml.rels */
122
160
  this.relationships = [];
161
+ this.relationshipSource = "document";
123
162
  /** Numbering definitions (abstract + num) */
124
163
  this.numberingDefs = [];
125
164
  /** Footnote XML bodies (keyed by footnote id) */
@@ -143,11 +182,10 @@ var ExportContext = class {
143
182
  const theme = resolveThemeForDoc(doc, options.themeId, options.themeRegistry);
144
183
  themeFont = firstFontFromStack(resolveFontFamily(theme.typography?.bodyFont, ""));
145
184
  themeTitleFont = firstFontFromStack(resolveFontFamily(theme.typography?.titleFont, ""));
146
- const stripHash = (c) => c.startsWith("#") ? c.slice(1) : c;
147
- if (theme.colors?.primary) themeHeadingColor = stripHash(theme.colors.primary);
148
- if (theme.colors?.text) themeBodyColor = stripHash(theme.colors.text);
149
- if (theme.colors?.textMuted) themeMutedColor = stripHash(theme.colors.textMuted);
150
- if (theme.colors?.background) themeBackgroundColor = stripHash(theme.colors.background);
185
+ themeHeadingColor = normalizeOoxmlHex(theme.colors?.primary);
186
+ themeBodyColor = normalizeOoxmlHex(theme.colors?.text);
187
+ themeMutedColor = normalizeOoxmlHex(theme.colors?.textMuted);
188
+ themeBackgroundColor = normalizeOoxmlHex(theme.colors?.background);
151
189
  }
152
190
  this.font = options.defaultFont ?? themeFont ?? DEFAULT_FONT;
153
191
  this.headingFont = themeTitleFont ?? this.font;
@@ -157,19 +195,33 @@ var ExportContext = class {
157
195
  this.mutedColor = themeMutedColor;
158
196
  this.backgroundColor = themeBackgroundColor;
159
197
  this.resolvedImages = options.images ?? /* @__PURE__ */ new Map();
198
+ this.allowRelativeHyperlinks = options.allowRelativeHyperlinks ?? false;
199
+ this.signal = options.signal;
160
200
  }
161
- /** Allocate a new relationship ID */
201
+ /** Allocate a relationship ID on the story part currently being converted. */
162
202
  allocRelId() {
163
- return `rId${this.nextRelId++}`;
203
+ return this.allocRelIdFor(this.relationshipSource);
204
+ }
205
+ /** Allocate a relationship ID on a specific story part. */
206
+ allocRelIdFor(source) {
207
+ return this.relIds.alloc(docxStoryPartPath(source));
208
+ }
209
+ setRelationshipSource(source) {
210
+ this.relationshipSource = source;
164
211
  }
165
212
  /** Add a hyperlink relationship and return the rId */
166
213
  addHyperlink(url) {
214
+ const target = sanitizeOfficeHyperlink(url, {
215
+ allowRelative: this.allowRelativeHyperlinks
216
+ });
217
+ if (!target) return null;
167
218
  const id = this.allocRelId();
168
219
  this.relationships.push({
169
220
  id,
170
221
  type: REL_HYPERLINK,
171
- target: url,
172
- targetMode: "External"
222
+ target,
223
+ targetMode: "External",
224
+ source: this.relationshipSource
173
225
  });
174
226
  return id;
175
227
  }
@@ -182,7 +234,8 @@ var ExportContext = class {
182
234
  this.relationships.push({
183
235
  id: relId,
184
236
  type: REL_IMAGE,
185
- target: `media/${filename}`
237
+ target: `media/${filename}`,
238
+ source: this.relationshipSource
186
239
  });
187
240
  return { relId, docPrId };
188
241
  }
@@ -204,6 +257,11 @@ var ExportContext = class {
204
257
  return id;
205
258
  }
206
259
  };
260
+ function docxStoryPartPath(source) {
261
+ if (source === "header") return "word/header1.xml";
262
+ if (source === "footer") return "word/footer1.xml";
263
+ return "word/document.xml";
264
+ }
207
265
  function firstFontFromStack(stack) {
208
266
  if (!stack) return "";
209
267
  const generics = /* @__PURE__ */ new Set([
@@ -230,7 +288,9 @@ function firstFontFromStack(stack) {
230
288
  }
231
289
  function convertBlocks(nodes, ctx) {
232
290
  const parts = [];
233
- for (const node of nodes) {
291
+ for (let index = 0; index < nodes.length; index++) {
292
+ if ((index & 255) === 0) ctx.signal?.throwIfAborted();
293
+ const node = nodes[index];
234
294
  parts.push(convertBlock(node, ctx, 0));
235
295
  }
236
296
  return parts.join("");
@@ -257,6 +317,8 @@ function convertBlock(node, ctx, listDepth) {
257
317
  return convertMathBlock(node);
258
318
  case "footnoteDefinition":
259
319
  return convertFootnoteDefinition(node, ctx);
320
+ case "containerDirective":
321
+ return convertBlocks(node.children, ctx);
260
322
  default:
261
323
  return "";
262
324
  }
@@ -390,21 +452,20 @@ function convertInline(node, ctx, format) {
390
452
  function makeRun(text, format) {
391
453
  if (!text) return "";
392
454
  const rPrParts = [];
455
+ if (format.code) {
456
+ rPrParts.push(`<w:rFonts w:ascii="${DEFAULT_CODE_FONT}" w:hAnsi="${DEFAULT_CODE_FONT}"/>`);
457
+ }
393
458
  if (format.bold) rPrParts.push("<w:b/>");
394
459
  if (format.italic) rPrParts.push("<w:i/>");
395
460
  if (format.strike) rPrParts.push("<w:strike/>");
396
461
  if (format.color) rPrParts.push(`<w:color w:val="${format.color}"/>`);
397
- if (format.code) {
398
- rPrParts.push(
399
- `<w:rFonts w:ascii="${DEFAULT_CODE_FONT}" w:hAnsi="${DEFAULT_CODE_FONT}"/>`,
400
- `<w:sz w:val="${DEFAULT_CODE_FONT_SIZE}"/>`
401
- );
402
- }
462
+ if (format.code) rPrParts.push(`<w:sz w:val="${DEFAULT_CODE_FONT_SIZE}"/>`);
403
463
  const rPr = rPrParts.length > 0 ? `<w:rPr>${rPrParts.join("")}</w:rPr>` : "";
404
464
  return `<w:r>${rPr}<w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r>`;
405
465
  }
406
466
  function convertLink(node, ctx, format) {
407
467
  const rId = ctx.addHyperlink(node.url);
468
+ if (!rId) return convertInlines(node.children, ctx, format);
408
469
  const styledRuns = convertInlinesWithHyperlinkStyle(node.children, ctx, format);
409
470
  return `<w:hyperlink r:id="${rId}">${styledRuns}</w:hyperlink>`;
410
471
  }
@@ -421,13 +482,10 @@ function convertInlinesWithHyperlinkStyle(nodes, ctx, format) {
421
482
  }
422
483
  function makeHyperlinkRun(text, format) {
423
484
  if (!text) return "";
424
- const rPrParts = [
425
- '<w:rStyle w:val="Hyperlink"/>',
426
- `<w:color w:val="${HYPERLINK_COLOR}"/>`,
427
- '<w:u w:val="single"/>'
428
- ];
485
+ const rPrParts = ['<w:rStyle w:val="Hyperlink"/>'];
429
486
  if (format.bold) rPrParts.push("<w:b/>");
430
487
  if (format.italic) rPrParts.push("<w:i/>");
488
+ rPrParts.push(`<w:color w:val="${HYPERLINK_COLOR}"/>`, '<w:u w:val="single"/>');
431
489
  return `<w:r><w:rPr>${rPrParts.join("")}</w:rPr><w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r>`;
432
490
  }
433
491
  function convertImage(node, ctx) {
@@ -498,16 +556,25 @@ function convertFootnoteRef(node, ctx) {
498
556
  const fnId = ctx.getFootnoteId(node.identifier);
499
557
  return `<w:r><w:rPr><w:rStyle w:val="FootnoteReference"/><w:vertAlign w:val="superscript"/></w:rPr><w:footnoteReference w:id="${fnId}"/></w:r>`;
500
558
  }
501
- async function buildDocxPackage(bodyXml, ctx, options) {
559
+ async function buildDocxPackage(bodyXml, headerXml, footerXml, ctx, options) {
502
560
  const pkg = createPackage();
503
- let relCounter = 100;
504
- const stylesRelId = `rId${relCounter++}`;
505
- const numberingRelId = `rId${relCounter++}`;
506
- const settingsRelId = `rId${relCounter++}`;
507
- const fontTableRelId = `rId${relCounter++}`;
508
- const footnotesRelId = `rId${relCounter++}`;
509
- const documentXml = buildDocumentXml(bodyXml, ctx.backgroundColor);
561
+ const hasHeader = headerXml.trim().length > 0;
562
+ const hasFooter = footerXml.trim().length > 0;
563
+ const stylesRelId = ctx.allocRelIdFor("document");
564
+ const settingsRelId = ctx.allocRelIdFor("document");
565
+ const fontTableRelId = ctx.allocRelIdFor("document");
566
+ const numberingRelId = ctx.hasLists ? ctx.allocRelIdFor("document") : void 0;
567
+ const footnotesRelId = ctx.hasFootnotes ? ctx.allocRelIdFor("document") : void 0;
568
+ const headerRelId = hasHeader ? ctx.allocRelIdFor("document") : void 0;
569
+ const footerRelId = hasFooter ? ctx.allocRelIdFor("document") : void 0;
570
+ const documentXml = buildDocumentXml(bodyXml, ctx.backgroundColor, headerRelId, footerRelId);
510
571
  pkg.addPart("word/document.xml", documentXml, CONTENT_TYPE_DOCX_DOCUMENT);
572
+ if (hasHeader) {
573
+ pkg.addPart("word/header1.xml", buildStoryXml("header", headerXml), CONTENT_TYPE_DOCX_HEADER);
574
+ }
575
+ if (hasFooter) {
576
+ pkg.addPart("word/footer1.xml", buildStoryXml("footer", footerXml), CONTENT_TYPE_DOCX_FOOTER);
577
+ }
511
578
  const stylesXml = buildStylesXml(options, ctx);
512
579
  pkg.addPart("word/styles.xml", stylesXml, CONTENT_TYPE_DOCX_STYLES);
513
580
  const settingsXml = buildSettingsXml(ctx.backgroundColor !== void 0);
@@ -542,22 +609,36 @@ async function buildDocxPackage(bodyXml, ctx, options) {
542
609
  type: REL_FONT_TABLE,
543
610
  target: "fontTable.xml"
544
611
  });
545
- if (ctx.hasLists) {
612
+ if (numberingRelId) {
546
613
  pkg.addRelationship("word/document.xml", {
547
614
  id: numberingRelId,
548
615
  type: REL_NUMBERING,
549
616
  target: "numbering.xml"
550
617
  });
551
618
  }
552
- if (ctx.hasFootnotes) {
619
+ if (footnotesRelId) {
553
620
  pkg.addRelationship("word/document.xml", {
554
621
  id: footnotesRelId,
555
622
  type: REL_FOOTNOTES,
556
623
  target: "footnotes.xml"
557
624
  });
558
625
  }
559
- for (const rel of ctx.relationships) {
626
+ if (headerRelId) {
627
+ pkg.addRelationship("word/document.xml", {
628
+ id: headerRelId,
629
+ type: REL_HEADER,
630
+ target: "header1.xml"
631
+ });
632
+ }
633
+ if (footerRelId) {
560
634
  pkg.addRelationship("word/document.xml", {
635
+ id: footerRelId,
636
+ type: REL_FOOTER,
637
+ target: "footer1.xml"
638
+ });
639
+ }
640
+ for (const rel of ctx.relationships) {
641
+ pkg.addRelationship(docxStoryPartPath(rel.source), {
561
642
  id: rel.id,
562
643
  type: rel.type,
563
644
  target: rel.target,
@@ -578,9 +659,14 @@ async function buildDocxPackage(bodyXml, ctx, options) {
578
659
  }
579
660
  return pkg.toArrayBuffer();
580
661
  }
581
- function buildDocumentXml(bodyXml, backgroundColor) {
662
+ function buildDocumentXml(bodyXml, backgroundColor, headerRelId, footerRelId) {
582
663
  const backgroundEl = backgroundColor ? `<w:background w:color="${backgroundColor}"/>` : "";
583
- return xmlDeclaration() + `<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="${NS_MC}" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="${NS_R}" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="${NS_WML}" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">` + backgroundEl + `<w:body>` + bodyXml + `<w:sectPr><w:pgSz w:w="12240" w:h="15840"/><w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/><w:cols w:space="720"/></w:sectPr></w:body></w:document>`;
664
+ const storyReferences = (headerRelId ? `<w:headerReference w:type="default" r:id="${headerRelId}"/>` : "") + (footerRelId ? `<w:footerReference w:type="default" r:id="${footerRelId}"/>` : "");
665
+ return xmlDeclaration() + `<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="${NS_MC}" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="${NS_R}" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="${NS_WML}" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">` + backgroundEl + `<w:body>` + bodyXml + `<w:sectPr>` + storyReferences + `<w:pgSz w:w="12240" w:h="15840"/><w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/><w:cols w:space="720"/></w:sectPr></w:body></w:document>`;
666
+ }
667
+ function buildStoryXml(kind, bodyXml) {
668
+ const element = kind === "header" ? "hdr" : "ftr";
669
+ return xmlDeclaration() + `<w:${element} xmlns:w="${NS_WML}" xmlns:r="${NS_R}" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing">` + bodyXml + `</w:${element}>`;
584
670
  }
585
671
  function buildStylesXml(options, ctx) {
586
672
  const font = ctx.font;
@@ -655,18 +741,20 @@ function buildFootnotesXml(ctx) {
655
741
  // src/docx/import.ts
656
742
  import { markdownToDoc } from "@bendyline/squisq/doc";
657
743
  import { stringifyMarkdown } from "@bendyline/squisq/markdown";
744
+ var DOCX_MAIN_PART = "word/document.xml";
658
745
  async function docxToMarkdownDoc(data, options = {}) {
659
746
  const pkg = await openPackage(data, options);
660
- const ctx = await buildImportContext(pkg, options);
661
- const documentXml = await getPartXml(pkg, "word/document.xml");
747
+ const mainPart = requireMainPartPath(pkg, DOCX_MAIN_PART, "DOCX");
748
+ const ctx = await buildImportContext(pkg, options, mainPart);
749
+ const documentXml = await getPartXml(pkg, mainPart);
662
750
  if (!documentXml) {
663
- return { type: "document", children: [] };
751
+ throw new Error(`Invalid DOCX package: main document part "${mainPart}" could not be parsed.`);
664
752
  }
665
753
  const body = getFirstElement(documentXml, "body");
666
754
  if (!body) {
667
755
  return { type: "document", children: [] };
668
756
  }
669
- const blocks = await convertBody(body, ctx);
757
+ const blocks = await convertDocumentStories(body, ctx);
670
758
  return { type: "document", children: blocks };
671
759
  }
672
760
  async function docxToDoc(data, options = {}) {
@@ -675,16 +763,19 @@ async function docxToDoc(data, options = {}) {
675
763
  }
676
764
  async function docxToContainer(data, options = {}) {
677
765
  const pkg = await openPackage(data, options);
678
- const ctx = await buildImportContext(pkg, { ...options, extractImages: true });
679
- const documentXml = await getPartXml(pkg, "word/document.xml");
680
- if (!documentXml) return buildContainer("", []);
766
+ const mainPart = requireMainPartPath(pkg, DOCX_MAIN_PART, "DOCX");
767
+ const ctx = await buildImportContext(pkg, { ...options, extractImages: true }, mainPart);
768
+ const documentXml = await getPartXml(pkg, mainPart);
769
+ if (!documentXml) {
770
+ throw new Error(`Invalid DOCX package: main document part "${mainPart}" could not be parsed.`);
771
+ }
681
772
  const body = getFirstElement(documentXml, "body");
682
773
  if (!body) return buildContainer("", []);
683
- const blocks = await convertBody(body, ctx);
774
+ const blocks = await convertDocumentStories(body, ctx);
684
775
  const markdownDoc = { type: "document", children: blocks };
685
776
  return buildContainer(stringifyMarkdown(markdownDoc), ctx.extractedImages);
686
777
  }
687
- async function buildImportContext(pkg, options) {
778
+ async function buildImportContext(pkg, options, mainPartPath = DOCX_MAIN_PART) {
688
779
  const ctx = {
689
780
  headingStyles: /* @__PURE__ */ new Map(),
690
781
  quoteStyles: /* @__PURE__ */ new Set(),
@@ -693,6 +784,9 @@ async function buildImportContext(pkg, options) {
693
784
  documentRels: /* @__PURE__ */ new Map(),
694
785
  numbering: /* @__PURE__ */ new Map(),
695
786
  footnotes: /* @__PURE__ */ new Map(),
787
+ endnotes: /* @__PURE__ */ new Map(),
788
+ currentPartPath: mainPartPath,
789
+ mainPartPath,
696
790
  pkg,
697
791
  options,
698
792
  extractedImages: /* @__PURE__ */ new Map(),
@@ -711,12 +805,13 @@ async function buildImportContext(pkg, options) {
711
805
  ctx.inlineCodeStyles.add(id);
712
806
  }
713
807
  await parseStyles(pkg, ctx);
714
- const rels = await getPartRelationships(pkg, "word/document.xml");
808
+ const rels = await getPartRelationships(pkg, mainPartPath);
715
809
  for (const rel of rels) {
716
810
  ctx.documentRels.set(rel.id, rel);
717
811
  }
718
812
  await parseNumbering(pkg, ctx);
719
813
  await parseFootnotes(pkg, ctx);
814
+ await parseEndnotes(pkg, ctx);
720
815
  return ctx;
721
816
  }
722
817
  async function parseStyles(pkg, ctx) {
@@ -797,9 +892,67 @@ async function parseFootnotes(pkg, ctx) {
797
892
  ctx.footnotes.set(id, fn);
798
893
  }
799
894
  }
895
+ async function parseEndnotes(pkg, ctx) {
896
+ const doc = await getPartXml(pkg, "word/endnotes.xml");
897
+ if (!doc) return;
898
+ const endnoteEls = getAllElements(doc, "endnote");
899
+ for (const note of endnoteEls) {
900
+ const id = getAttr(note, "id");
901
+ const type = getAttr(note, "type");
902
+ if (!id || type === "separator" || type === "continuationSeparator") continue;
903
+ ctx.endnotes.set(id, note);
904
+ }
905
+ }
906
+ async function convertDocumentStories(body, ctx) {
907
+ const bodyBlocks = await convertBody(body, ctx);
908
+ const headers = await convertRelatedStories(body, ctx, "header");
909
+ const footers = await convertRelatedStories(body, ctx, "footer");
910
+ const noteDefinitions = await convertNoteDefinitions(ctx);
911
+ return [...bodyBlocks, ...headers, ...footers, ...noteDefinitions];
912
+ }
913
+ async function convertRelatedStories(body, ctx, kind) {
914
+ const relationshipTypeSuffix = `/${kind}`;
915
+ const referenceName = `${kind}Reference`;
916
+ const referenceTypes = /* @__PURE__ */ new Map();
917
+ for (const reference of getAllElements(body, referenceName)) {
918
+ const id = reference.getAttributeNS(NS_R, "id") ?? reference.getAttribute("r:id");
919
+ if (id) referenceTypes.set(id, getAttr(reference, "type") ?? "default");
920
+ }
921
+ const documentRelationships = ctx.documentRels;
922
+ const previousPartPath = ctx.currentPartPath;
923
+ const results = [];
924
+ for (const relationship of documentRelationships.values()) {
925
+ if (!relationship.type.endsWith(relationshipTypeSuffix)) continue;
926
+ const partPath = resolveTarget(baseDirOf(ctx.mainPartPath), relationship.target);
927
+ const part = await getPartXml(ctx.pkg, partPath);
928
+ if (!part?.documentElement) continue;
929
+ const partRelationships = await getPartRelationships(ctx.pkg, partPath);
930
+ ctx.documentRels = new Map(partRelationships.map((rel) => [rel.id, rel]));
931
+ ctx.currentPartPath = partPath;
932
+ try {
933
+ const children = await convertBody(part.documentElement, ctx);
934
+ if (children.length === 0) continue;
935
+ results.push({
936
+ type: "containerDirective",
937
+ name: `docx-${kind}`,
938
+ attributes: {
939
+ type: referenceTypes.get(relationship.id) ?? "default",
940
+ source: partPath
941
+ },
942
+ children
943
+ });
944
+ } finally {
945
+ ctx.documentRels = documentRelationships;
946
+ ctx.currentPartPath = previousPartPath;
947
+ }
948
+ }
949
+ return results;
950
+ }
800
951
  async function convertBody(body, ctx) {
952
+ return convertBlockElements(Array.from(body.children), ctx);
953
+ }
954
+ async function convertBlockElements(children, ctx) {
801
955
  const result = [];
802
- const children = Array.from(body.children);
803
956
  let i = 0;
804
957
  while (i < children.length) {
805
958
  const el = children[i];
@@ -821,12 +974,22 @@ async function convertBody(body, ctx) {
821
974
  const table = await convertTable2(el, ctx);
822
975
  if (table) result.push(table);
823
976
  i++;
977
+ } else if (localName === "sdt") {
978
+ const content = getFirstChildElement(el, "sdtContent");
979
+ if (content) result.push(...await convertBlockElements(Array.from(content.children), ctx));
980
+ i++;
981
+ } else if (localName === "ins" || localName === "moveTo" || localName === "customXml" || localName === "smartTag" || localName === "fldSimple") {
982
+ result.push(...await convertBlockElements(Array.from(el.children), ctx));
983
+ i++;
984
+ } else if (localName === "AlternateContent") {
985
+ const selected = getFirstChildElement(el, "Choice") ?? getFirstChildElement(el, "Fallback");
986
+ if (selected)
987
+ result.push(...await convertBlockElements(Array.from(selected.children), ctx));
988
+ i++;
824
989
  } else {
825
990
  i++;
826
991
  }
827
992
  }
828
- const footnoteNodes = await convertFootnoteDefinitions(ctx);
829
- result.push(...footnoteNodes);
830
993
  return result;
831
994
  }
832
995
  async function convertParagraph2(el, ctx) {
@@ -857,8 +1020,10 @@ async function convertParagraph2(el, ctx) {
857
1020
  return { type: "paragraph", children: inlines };
858
1021
  }
859
1022
  async function convertRuns(paragraphEl, ctx) {
1023
+ return mergeAdjacentText(await convertInlineElements(Array.from(paragraphEl.children), ctx));
1024
+ }
1025
+ async function convertInlineElements(children, ctx) {
860
1026
  const result = [];
861
- const children = Array.from(paragraphEl.children);
862
1027
  for (const child of children) {
863
1028
  const localName = child.localName;
864
1029
  if (localName === "r") {
@@ -867,9 +1032,23 @@ async function convertRuns(paragraphEl, ctx) {
867
1032
  } else if (localName === "hyperlink") {
868
1033
  const link = await convertHyperlink(child, ctx);
869
1034
  if (link) result.push(link);
1035
+ } else if (localName === "sdt") {
1036
+ const content = getFirstChildElement(child, "sdtContent");
1037
+ if (content) {
1038
+ result.push(...await convertInlineElements(Array.from(content.children), ctx));
1039
+ }
1040
+ } else if (localName === "ins" || localName === "moveTo" || localName === "customXml" || localName === "smartTag" || localName === "fldSimple") {
1041
+ result.push(...await convertInlineElements(Array.from(child.children), ctx));
1042
+ } else if (localName === "AlternateContent") {
1043
+ const selected = getFirstChildElement(child, "Choice") ?? getFirstChildElement(child, "Fallback");
1044
+ if (selected) {
1045
+ result.push(...await convertInlineElements(Array.from(selected.children), ctx));
1046
+ }
1047
+ } else if (localName === "drawing" || localName === "pict") {
1048
+ result.push(...await convertDrawingContent(child, ctx));
870
1049
  }
871
1050
  }
872
- return mergeAdjacentText(result);
1051
+ return result;
873
1052
  }
874
1053
  async function convertRun(runEl, ctx) {
875
1054
  const result = [];
@@ -895,8 +1074,10 @@ async function convertRun(runEl, ctx) {
895
1074
  }
896
1075
  result.push(node);
897
1076
  }
898
- } else if (localName === "br") {
1077
+ } else if (localName === "br" || localName === "cr") {
899
1078
  result.push({ type: "break" });
1079
+ } else if (localName === "tab") {
1080
+ result.push({ type: "text", value: " " });
900
1081
  } else if (localName === "footnoteReference") {
901
1082
  const fnId = getAttr(child, "id");
902
1083
  if (fnId && fnId !== "0" && fnId !== "-1") {
@@ -905,13 +1086,79 @@ async function convertRun(runEl, ctx) {
905
1086
  identifier: `fn${fnId}`
906
1087
  });
907
1088
  }
908
- } else if (localName === "drawing" || localName === "pict") {
909
- const img = await extractImage(child, ctx);
910
- if (img) result.push(img);
1089
+ } else if (localName === "endnoteReference") {
1090
+ const noteId = getAttr(child, "id");
1091
+ if (noteId && noteId !== "0" && noteId !== "-1") {
1092
+ result.push({
1093
+ type: "footnoteReference",
1094
+ identifier: `endnote${noteId}`
1095
+ });
1096
+ }
1097
+ } else if (localName === "drawing" || localName === "pict" || localName === "object") {
1098
+ result.push(...await convertDrawingContent(child, ctx));
1099
+ } else if (localName === "AlternateContent") {
1100
+ const selected = getFirstChildElement(child, "Choice") ?? getFirstChildElement(child, "Fallback");
1101
+ if (selected) {
1102
+ result.push(...await convertInlineElements(Array.from(selected.children), ctx));
1103
+ }
1104
+ }
1105
+ }
1106
+ return result;
1107
+ }
1108
+ async function convertDrawingContent(el, ctx) {
1109
+ const result = [];
1110
+ const textBoxes = findDescendants(el, "txbxContent");
1111
+ for (const textBox of textBoxes) {
1112
+ const inlines = await flattenContainerToInlines(textBox, ctx);
1113
+ appendInlineGroup(result, inlines);
1114
+ }
1115
+ const image = await extractImage(el, ctx);
1116
+ if (image) result.push(image);
1117
+ if (textBoxes.length > 0 && result.length > 0) {
1118
+ if (result[0]?.type !== "break") result.unshift({ type: "break" });
1119
+ if (result[result.length - 1]?.type !== "break") {
1120
+ result.push({ type: "break" });
911
1121
  }
912
1122
  }
913
1123
  return result;
914
1124
  }
1125
+ async function flattenContainerToInlines(container, ctx) {
1126
+ const result = [];
1127
+ for (const child of Array.from(container.children)) {
1128
+ if (child.localName === "p") {
1129
+ appendInlineGroup(result, await convertRuns(child, ctx));
1130
+ } else if (child.localName === "tbl") {
1131
+ appendInlineGroup(result, await flattenTableToInlines(child, ctx));
1132
+ } else if (child.localName === "sdt") {
1133
+ const content = getFirstChildElement(child, "sdtContent");
1134
+ if (content) appendInlineGroup(result, await flattenContainerToInlines(content, ctx));
1135
+ } else if (child.localName === "ins" || child.localName === "moveTo" || child.localName === "customXml" || child.localName === "smartTag" || child.localName === "fldSimple") {
1136
+ appendInlineGroup(result, await flattenContainerToInlines(child, ctx));
1137
+ } else if (child.localName === "AlternateContent") {
1138
+ const selected = getFirstChildElement(child, "Choice") ?? getFirstChildElement(child, "Fallback");
1139
+ if (selected) appendInlineGroup(result, await flattenContainerToInlines(selected, ctx));
1140
+ } else if (child.localName === "r" || child.localName === "hyperlink") {
1141
+ appendInlineGroup(result, await convertInlineElements([child], ctx));
1142
+ }
1143
+ }
1144
+ return mergeAdjacentText(result);
1145
+ }
1146
+ async function flattenTableToInlines(table, ctx) {
1147
+ const result = [];
1148
+ for (const row of getAllChildElements(table, "tr")) {
1149
+ for (const cell of getAllChildElements(row, "tc")) {
1150
+ appendInlineGroup(result, await flattenContainerToInlines(cell, ctx));
1151
+ }
1152
+ }
1153
+ return mergeAdjacentText(result);
1154
+ }
1155
+ function appendInlineGroup(target, group) {
1156
+ if (group.length === 0) return;
1157
+ if (target.length > 0 && target[target.length - 1]?.type !== "break") {
1158
+ target.push({ type: "break" });
1159
+ }
1160
+ target.push(...group);
1161
+ }
915
1162
  function parseRunFormat(rPr, ctx) {
916
1163
  if (!rPr) return { bold: false, italic: false, strike: false, code: false };
917
1164
  const bold = hasChildElement(rPr, "b") && !isFalseToggle(getFirstChildElement(rPr, "b"));
@@ -940,12 +1187,7 @@ async function convertHyperlink(el, ctx) {
940
1187
  const anchor = el.getAttributeNS(NS_WML, "anchor") ?? el.getAttribute("w:anchor");
941
1188
  if (anchor) url = `#${anchor}`;
942
1189
  }
943
- const inlines = [];
944
- for (const child of Array.from(el.children)) {
945
- if (child.localName === "r") {
946
- inlines.push(...await convertRun(child, ctx));
947
- }
948
- }
1190
+ const inlines = await convertInlineElements(Array.from(el.children), ctx);
949
1191
  if (inlines.length === 0) return null;
950
1192
  return {
951
1193
  type: "link",
@@ -955,22 +1197,14 @@ async function convertHyperlink(el, ctx) {
955
1197
  }
956
1198
  async function extractImage(el, ctx) {
957
1199
  const blip = findDescendant(el, "blip");
958
- if (!blip) {
959
- return { type: "image", url: "", alt: "Image" };
960
- }
961
- const rId = blip.getAttributeNS(NS_R, "embed") ?? blip.getAttribute("r:embed");
962
- if (!rId) {
963
- return { type: "image", url: "", alt: "Image" };
964
- }
1200
+ const imageData = findDescendant(el, "imagedata");
1201
+ const rId = blip ? blip.getAttributeNS(NS_R, "embed") ?? blip.getAttribute("r:embed") : imageData?.getAttributeNS(NS_R, "id") ?? imageData?.getAttribute("r:id") ?? imageData?.getAttribute("o:relid");
1202
+ if (!rId) return null;
965
1203
  const rel = ctx.documentRels.get(rId);
966
- if (!rel) {
967
- return { type: "image", url: "", alt: "Image" };
968
- }
969
- const target = rel.target.startsWith("/") ? rel.target.slice(1) : `word/${rel.target}`;
1204
+ if (!rel || rel.targetMode === "External") return null;
1205
+ const target = resolveTarget(baseDirOf(ctx.currentPartPath), rel.target);
970
1206
  const data = await getPartBinary(ctx.pkg, target);
971
- if (!data) {
972
- return { type: "image", url: "", alt: "Image" };
973
- }
1207
+ if (!data) return null;
974
1208
  const dot = target.lastIndexOf(".");
975
1209
  const ext = dot !== -1 ? target.slice(dot).toLowerCase() : ".png";
976
1210
  const mimeType = extToMime(ext);
@@ -978,7 +1212,8 @@ async function extractImage(el, ctx) {
978
1212
  const imagePath = `images/image${ctx.imageCounter}${ext}`;
979
1213
  ctx.extractedImages.set(imagePath, { data, mimeType });
980
1214
  const docPr = findDescendant(el, "docPr");
981
- const alt = docPr?.getAttribute("descr") || docPr?.getAttribute("title") || "Image";
1215
+ const shape = findDescendant(el, "shape");
1216
+ const alt = docPr?.getAttribute("descr") || docPr?.getAttribute("title") || shape?.getAttribute("alt") || shape?.getAttribute("title") || "Image";
982
1217
  return {
983
1218
  type: "image",
984
1219
  url: imagePath,
@@ -993,6 +1228,14 @@ function findDescendant(el, localName) {
993
1228
  }
994
1229
  return null;
995
1230
  }
1231
+ function findDescendants(el, localName) {
1232
+ const results = [];
1233
+ for (const child of Array.from(el.children)) {
1234
+ if (child.localName === localName) results.push(child);
1235
+ results.push(...findDescendants(child, localName));
1236
+ }
1237
+ return results;
1238
+ }
996
1239
  async function collectList(elements, startIdx, ctx) {
997
1240
  const firstNumPr = getNumPr(elements[startIdx]);
998
1241
  const { numId } = firstNumPr;
@@ -1020,6 +1263,8 @@ async function collectList(elements, startIdx, ctx) {
1020
1263
  children: nested.items
1021
1264
  };
1022
1265
  lastItem.children.push(nestedList);
1266
+ } else {
1267
+ items.push(...nested.items);
1023
1268
  }
1024
1269
  i += nested.consumed;
1025
1270
  consumed += nested.consumed;
@@ -1100,42 +1345,40 @@ async function convertTableRow2(trEl, ctx, isHeader) {
1100
1345
  return { type: "tableRow", children: cells };
1101
1346
  }
1102
1347
  async function convertTableCell2(tcEl, ctx, isHeader) {
1103
- const inlines = [];
1104
- const paragraphs = getAllChildElements(tcEl, "p");
1105
- for (let pi = 0; pi < paragraphs.length; pi++) {
1106
- if (pi > 0) {
1107
- inlines.push({ type: "break" });
1108
- }
1109
- const runs = await convertRuns(paragraphs[pi], ctx);
1110
- inlines.push(...runs);
1111
- }
1348
+ const inlines = await flattenContainerToInlines(tcEl, ctx);
1112
1349
  return {
1113
1350
  type: "tableCell",
1114
1351
  isHeader,
1115
1352
  children: mergeAdjacentText(inlines)
1116
1353
  };
1117
1354
  }
1118
- async function convertFootnoteDefinitions(ctx) {
1355
+ async function convertNoteDefinitions(ctx) {
1356
+ return [
1357
+ ...await convertNotePartDefinitions(ctx, ctx.footnotes, "fn", "word/footnotes.xml"),
1358
+ ...await convertNotePartDefinitions(ctx, ctx.endnotes, "endnote", "word/endnotes.xml")
1359
+ ];
1360
+ }
1361
+ async function convertNotePartDefinitions(ctx, notes, identifierPrefix, partPath) {
1119
1362
  const results = [];
1120
- for (const [id, el] of ctx.footnotes) {
1121
- const children = [];
1122
- const paragraphs = getAllChildElements(el, "p");
1123
- for (const p of paragraphs) {
1124
- const inlines = await convertRuns(p, ctx);
1125
- if (inlines.length > 0) {
1126
- children.push({
1127
- type: "paragraph",
1128
- children: inlines
1363
+ const previousRelationships = ctx.documentRels;
1364
+ const previousPartPath = ctx.currentPartPath;
1365
+ const partRelationships = await getPartRelationships(ctx.pkg, partPath);
1366
+ ctx.documentRels = new Map(partRelationships.map((rel) => [rel.id, rel]));
1367
+ ctx.currentPartPath = partPath;
1368
+ try {
1369
+ for (const [id, el] of notes) {
1370
+ const children = await convertBody(el, ctx);
1371
+ if (children.length > 0) {
1372
+ results.push({
1373
+ type: "footnoteDefinition",
1374
+ identifier: `${identifierPrefix}${id}`,
1375
+ children
1129
1376
  });
1130
1377
  }
1131
1378
  }
1132
- if (children.length > 0) {
1133
- results.push({
1134
- type: "footnoteDefinition",
1135
- identifier: `fn${id}`,
1136
- children
1137
- });
1138
- }
1379
+ } finally {
1380
+ ctx.documentRels = previousRelationships;
1381
+ ctx.currentPartPath = previousPartPath;
1139
1382
  }
1140
1383
  return results;
1141
1384
  }
@@ -1209,4 +1452,3 @@ export {
1209
1452
  docxToDoc,
1210
1453
  docxToContainer
1211
1454
  };
1212
- //# sourceMappingURL=chunk-CRAVSMPZ.js.map