@bendyline/squisq-formats 1.3.0 → 1.4.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 (224) hide show
  1. package/dist/chunk-6M7Z25LA.js +46 -0
  2. package/dist/chunk-6M7Z25LA.js.map +1 -0
  3. package/dist/{chunk-S3Y7H2BK.js → chunk-APSIAEXL.js} +9 -8
  4. package/dist/chunk-APSIAEXL.js.map +1 -0
  5. package/dist/{chunk-UDS45KUJ.js → chunk-BJQIJ4S4.js} +36 -52
  6. package/dist/chunk-BJQIJ4S4.js.map +1 -0
  7. package/dist/{chunk-33YRFXZZ.js → chunk-CRTC6DEX.js} +259 -13
  8. package/dist/chunk-CRTC6DEX.js.map +1 -0
  9. package/dist/chunk-FMOIGA5T.js +201 -0
  10. package/dist/chunk-FMOIGA5T.js.map +1 -0
  11. package/dist/{chunk-VN2KEOYB.js → chunk-I2GFTEOV.js} +321 -91
  12. package/dist/chunk-I2GFTEOV.js.map +1 -0
  13. package/dist/chunk-JBNVE2GV.js +116 -0
  14. package/dist/chunk-JBNVE2GV.js.map +1 -0
  15. package/dist/chunk-L53YIGYS.js +141 -0
  16. package/dist/chunk-L53YIGYS.js.map +1 -0
  17. package/dist/{chunk-ERZ627GR.js → chunk-ON232HJR.js} +11 -26
  18. package/dist/chunk-ON232HJR.js.map +1 -0
  19. package/dist/chunk-PN52A5AA.js +32 -0
  20. package/dist/chunk-PN52A5AA.js.map +1 -0
  21. package/dist/{chunk-7DEUGIOO.js → chunk-QSE7EWE7.js} +12 -87
  22. package/dist/chunk-QSE7EWE7.js.map +1 -0
  23. package/dist/container/index.d.ts +7 -5
  24. package/dist/container/index.js +39 -70
  25. package/dist/container/index.js.map +1 -1
  26. package/dist/csv/index.d.ts +37 -0
  27. package/dist/csv/index.js +13 -0
  28. package/dist/csv/index.js.map +1 -0
  29. package/dist/docx/index.d.ts +128 -15
  30. package/dist/docx/index.js +19 -22
  31. package/dist/docx/index.js.map +1 -1
  32. package/dist/epub/index.d.ts +66 -11
  33. package/dist/epub/index.js +11 -18
  34. package/dist/epub/index.js.map +1 -1
  35. package/dist/html/index.d.ts +315 -15
  36. package/dist/html/index.js +32 -165
  37. package/dist/html/index.js.map +1 -1
  38. package/dist/index.d.ts +10 -38
  39. package/dist/index.js +80 -37
  40. package/dist/index.js.map +1 -1
  41. package/dist/ooxml/index.d.ts +353 -14
  42. package/dist/ooxml/index.js +116 -22
  43. package/dist/ooxml/index.js.map +1 -1
  44. package/dist/pdf/index.d.ts +143 -17
  45. package/dist/pdf/index.js +16 -25
  46. package/dist/pdf/index.js.map +1 -1
  47. package/dist/pptx/index.d.ts +98 -21
  48. package/dist/pptx/index.js +18 -34
  49. package/dist/pptx/index.js.map +1 -1
  50. package/dist/xlsx/index.d.ts +29 -25
  51. package/dist/xlsx/index.js +13 -44
  52. package/dist/xlsx/index.js.map +1 -1
  53. package/package.json +7 -2
  54. package/src/__tests__/csvImport.test.ts +51 -0
  55. package/src/__tests__/exportThemeReconciliation.test.ts +87 -0
  56. package/src/__tests__/htmlImport.test.ts +57 -0
  57. package/src/__tests__/plainHtml.test.ts +31 -0
  58. package/src/__tests__/pptxExport.test.ts +138 -0
  59. package/src/__tests__/pptxImport.test.ts +70 -0
  60. package/src/__tests__/roundTripMatrix.fixtures.ts +86 -0
  61. package/src/__tests__/roundTripMatrix.helpers.ts +154 -0
  62. package/src/__tests__/roundTripMatrix.test.ts +136 -0
  63. package/src/__tests__/xlsxImport.test.ts +80 -0
  64. package/src/csv/index.ts +165 -0
  65. package/src/docx/export.ts +27 -44
  66. package/src/docx/import.ts +1 -2
  67. package/src/epub/export.ts +10 -27
  68. package/src/html/import.ts +297 -0
  69. package/src/html/index.ts +4 -0
  70. package/src/html/plainHtml.ts +66 -16
  71. package/src/index.ts +10 -3
  72. package/src/ooxml/namespaces.ts +3 -0
  73. package/src/pdf/export.ts +10 -5
  74. package/src/pdf/import.ts +1 -1
  75. package/src/pptx/export.ts +225 -85
  76. package/src/pptx/import.ts +156 -0
  77. package/src/pptx/index.ts +10 -28
  78. package/src/shared/inlineRuns.ts +99 -0
  79. package/src/shared/text.ts +41 -0
  80. package/src/xlsx/import.ts +159 -0
  81. package/src/xlsx/index.ts +11 -28
  82. package/dist/__tests__/container.test.d.ts +0 -8
  83. package/dist/__tests__/container.test.d.ts.map +0 -1
  84. package/dist/__tests__/container.test.js +0 -147
  85. package/dist/__tests__/container.test.js.map +0 -1
  86. package/dist/__tests__/docxExport.test.d.ts +0 -8
  87. package/dist/__tests__/docxExport.test.d.ts.map +0 -1
  88. package/dist/__tests__/docxExport.test.js +0 -383
  89. package/dist/__tests__/docxExport.test.js.map +0 -1
  90. package/dist/__tests__/docxImport.test.d.ts +0 -8
  91. package/dist/__tests__/docxImport.test.d.ts.map +0 -1
  92. package/dist/__tests__/docxImport.test.js +0 -309
  93. package/dist/__tests__/docxImport.test.js.map +0 -1
  94. package/dist/__tests__/epub.test.d.ts +0 -8
  95. package/dist/__tests__/epub.test.d.ts.map +0 -1
  96. package/dist/__tests__/epub.test.js +0 -472
  97. package/dist/__tests__/epub.test.js.map +0 -1
  98. package/dist/__tests__/html.test.d.ts +0 -8
  99. package/dist/__tests__/html.test.d.ts.map +0 -1
  100. package/dist/__tests__/html.test.js +0 -317
  101. package/dist/__tests__/html.test.js.map +0 -1
  102. package/dist/__tests__/ooxml.test.d.ts +0 -5
  103. package/dist/__tests__/ooxml.test.d.ts.map +0 -1
  104. package/dist/__tests__/ooxml.test.js +0 -152
  105. package/dist/__tests__/ooxml.test.js.map +0 -1
  106. package/dist/__tests__/pdfExport.test.d.ts +0 -8
  107. package/dist/__tests__/pdfExport.test.d.ts.map +0 -1
  108. package/dist/__tests__/pdfExport.test.js +0 -275
  109. package/dist/__tests__/pdfExport.test.js.map +0 -1
  110. package/dist/__tests__/pdfImport.test.d.ts +0 -11
  111. package/dist/__tests__/pdfImport.test.d.ts.map +0 -1
  112. package/dist/__tests__/pdfImport.test.js +0 -254
  113. package/dist/__tests__/pdfImport.test.js.map +0 -1
  114. package/dist/__tests__/plainHtml.test.d.ts +0 -5
  115. package/dist/__tests__/plainHtml.test.d.ts.map +0 -1
  116. package/dist/__tests__/plainHtml.test.js +0 -327
  117. package/dist/__tests__/plainHtml.test.js.map +0 -1
  118. package/dist/__tests__/plainHtmlBundle.test.d.ts +0 -11
  119. package/dist/__tests__/plainHtmlBundle.test.d.ts.map +0 -1
  120. package/dist/__tests__/plainHtmlBundle.test.js +0 -210
  121. package/dist/__tests__/plainHtmlBundle.test.js.map +0 -1
  122. package/dist/__tests__/roundTrip.test.d.ts +0 -7
  123. package/dist/__tests__/roundTrip.test.d.ts.map +0 -1
  124. package/dist/__tests__/roundTrip.test.js +0 -173
  125. package/dist/__tests__/roundTrip.test.js.map +0 -1
  126. package/dist/chunk-33YRFXZZ.js.map +0 -1
  127. package/dist/chunk-67KIJHV2.js +0 -21
  128. package/dist/chunk-67KIJHV2.js.map +0 -1
  129. package/dist/chunk-7DEUGIOO.js.map +0 -1
  130. package/dist/chunk-ERZ627GR.js.map +0 -1
  131. package/dist/chunk-S3Y7H2BK.js.map +0 -1
  132. package/dist/chunk-UDS45KUJ.js.map +0 -1
  133. package/dist/chunk-VN2KEOYB.js.map +0 -1
  134. package/dist/chunk-YN5HFCEW.js +0 -120
  135. package/dist/chunk-YN5HFCEW.js.map +0 -1
  136. package/dist/container/index.d.ts.map +0 -1
  137. package/dist/docx/export.d.ts +0 -69
  138. package/dist/docx/export.d.ts.map +0 -1
  139. package/dist/docx/export.js +0 -985
  140. package/dist/docx/export.js.map +0 -1
  141. package/dist/docx/import.d.ts +0 -65
  142. package/dist/docx/import.d.ts.map +0 -1
  143. package/dist/docx/import.js +0 -779
  144. package/dist/docx/import.js.map +0 -1
  145. package/dist/docx/index.d.ts.map +0 -1
  146. package/dist/docx/styles.d.ts +0 -68
  147. package/dist/docx/styles.d.ts.map +0 -1
  148. package/dist/docx/styles.js +0 -125
  149. package/dist/docx/styles.js.map +0 -1
  150. package/dist/epub/export.d.ts +0 -72
  151. package/dist/epub/export.d.ts.map +0 -1
  152. package/dist/epub/export.js +0 -698
  153. package/dist/epub/export.js.map +0 -1
  154. package/dist/epub/index.d.ts.map +0 -1
  155. package/dist/html/docsHtmlBundle.d.ts +0 -53
  156. package/dist/html/docsHtmlBundle.d.ts.map +0 -1
  157. package/dist/html/docsHtmlBundle.js +0 -299
  158. package/dist/html/docsHtmlBundle.js.map +0 -1
  159. package/dist/html/htmlTemplate.d.ts +0 -70
  160. package/dist/html/htmlTemplate.d.ts.map +0 -1
  161. package/dist/html/htmlTemplate.js +0 -282
  162. package/dist/html/htmlTemplate.js.map +0 -1
  163. package/dist/html/imageUtils.d.ts +0 -29
  164. package/dist/html/imageUtils.d.ts.map +0 -1
  165. package/dist/html/imageUtils.js +0 -63
  166. package/dist/html/imageUtils.js.map +0 -1
  167. package/dist/html/index.d.ts.map +0 -1
  168. package/dist/html/plainHtml.d.ts +0 -79
  169. package/dist/html/plainHtml.d.ts.map +0 -1
  170. package/dist/html/plainHtml.js +0 -614
  171. package/dist/html/plainHtml.js.map +0 -1
  172. package/dist/html/plainHtmlBundle.d.ts +0 -93
  173. package/dist/html/plainHtmlBundle.d.ts.map +0 -1
  174. package/dist/html/plainHtmlBundle.js +0 -357
  175. package/dist/html/plainHtmlBundle.js.map +0 -1
  176. package/dist/index.d.ts.map +0 -1
  177. package/dist/ooxml/index.d.ts.map +0 -1
  178. package/dist/ooxml/namespaces.d.ts +0 -77
  179. package/dist/ooxml/namespaces.d.ts.map +0 -1
  180. package/dist/ooxml/namespaces.js +0 -104
  181. package/dist/ooxml/namespaces.js.map +0 -1
  182. package/dist/ooxml/reader.d.ts +0 -52
  183. package/dist/ooxml/reader.d.ts.map +0 -1
  184. package/dist/ooxml/reader.js +0 -190
  185. package/dist/ooxml/reader.js.map +0 -1
  186. package/dist/ooxml/types.d.ts +0 -82
  187. package/dist/ooxml/types.d.ts.map +0 -1
  188. package/dist/ooxml/types.js +0 -9
  189. package/dist/ooxml/types.js.map +0 -1
  190. package/dist/ooxml/writer.d.ts +0 -71
  191. package/dist/ooxml/writer.d.ts.map +0 -1
  192. package/dist/ooxml/writer.js +0 -200
  193. package/dist/ooxml/writer.js.map +0 -1
  194. package/dist/ooxml/xmlUtils.d.ts +0 -76
  195. package/dist/ooxml/xmlUtils.d.ts.map +0 -1
  196. package/dist/ooxml/xmlUtils.js +0 -108
  197. package/dist/ooxml/xmlUtils.js.map +0 -1
  198. package/dist/pdf/export.d.ts +0 -56
  199. package/dist/pdf/export.d.ts.map +0 -1
  200. package/dist/pdf/export.js +0 -768
  201. package/dist/pdf/export.js.map +0 -1
  202. package/dist/pdf/import.d.ts +0 -94
  203. package/dist/pdf/import.d.ts.map +0 -1
  204. package/dist/pdf/import.js +0 -796
  205. package/dist/pdf/import.js.map +0 -1
  206. package/dist/pdf/index.d.ts.map +0 -1
  207. package/dist/pdf/styles.d.ts +0 -142
  208. package/dist/pdf/styles.d.ts.map +0 -1
  209. package/dist/pdf/styles.js +0 -145
  210. package/dist/pdf/styles.js.map +0 -1
  211. package/dist/pptx/export.d.ts +0 -75
  212. package/dist/pptx/export.d.ts.map +0 -1
  213. package/dist/pptx/export.js +0 -724
  214. package/dist/pptx/export.js.map +0 -1
  215. package/dist/pptx/index.d.ts.map +0 -1
  216. package/dist/pptx/styles.d.ts +0 -54
  217. package/dist/pptx/styles.d.ts.map +0 -1
  218. package/dist/pptx/styles.js +0 -73
  219. package/dist/pptx/styles.js.map +0 -1
  220. package/dist/pptx/templates.d.ts +0 -24
  221. package/dist/pptx/templates.d.ts.map +0 -1
  222. package/dist/pptx/templates.js +0 -158
  223. package/dist/pptx/templates.js.map +0 -1
  224. package/dist/xlsx/index.d.ts.map +0 -1
@@ -0,0 +1,46 @@
1
+ import {
2
+ stripHtmlTags
3
+ } from "./chunk-PN52A5AA.js";
4
+
5
+ // src/shared/inlineRuns.ts
6
+ function inlineNodesToRuns(nodes, handlers, format = {}) {
7
+ let out = "";
8
+ for (const node of nodes) {
9
+ out += inlineNodeToRuns(node, handlers, format);
10
+ }
11
+ return out;
12
+ }
13
+ function inlineNodeToRuns(node, handlers, format) {
14
+ switch (node.type) {
15
+ case "text":
16
+ return handlers.run(node.value, format);
17
+ case "strong":
18
+ return inlineNodesToRuns(node.children, handlers, { ...format, bold: true });
19
+ case "emphasis":
20
+ return inlineNodesToRuns(node.children, handlers, { ...format, italic: true });
21
+ case "delete":
22
+ return inlineNodesToRuns(node.children, handlers, { ...format, strike: true });
23
+ case "inlineCode":
24
+ return handlers.run(node.value, { ...format, code: true });
25
+ case "inlineMath":
26
+ return handlers.run(node.value, { ...format, code: true });
27
+ case "htmlInline":
28
+ return handlers.run(stripHtmlTags(node.rawHtml), format);
29
+ case "link":
30
+ return handlers.link(node, format);
31
+ case "image":
32
+ return handlers.image(node, format);
33
+ case "break":
34
+ return handlers.lineBreak();
35
+ case "footnoteReference":
36
+ return handlers.footnoteRef ? handlers.footnoteRef(node) : "";
37
+ default:
38
+ return "";
39
+ }
40
+ }
41
+
42
+ export {
43
+ inlineNodesToRuns,
44
+ inlineNodeToRuns
45
+ };
46
+ //# sourceMappingURL=chunk-6M7Z25LA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/inlineRuns.ts"],"sourcesContent":["/**\n * Shared inline walker for the \"run-based\" exporters.\n *\n * DOCX (`<w:r>`) and PPTX (`<a:r>`) both render inline markdown by threading an\n * active character-format object DOWN through nested nodes (strong → bold,\n * emphasis → italic, …) and emitting flat styled runs at the leaves — as\n * opposed to the HTML exporters, which WRAP children in tags, or the PDF\n * exporter, which accumulates positioned spans. That control flow is identical\n * between DOCX and PPTX; only the leaf serialization differs.\n *\n * This module owns the traversal once. Each exporter supplies the handful of\n * leaf handlers (`run`, `link`, `image`, `lineBreak`, optional `footnoteRef`)\n * via {@link InlineRunHandlers}. Because the handler interface is typed, adding\n * a new inline node type here forces both exporters to be considered — there's\n * no longer a per-exporter `default:` that can silently drift.\n */\n\nimport type {\n MarkdownInlineNode,\n MarkdownLink,\n MarkdownImage,\n MarkdownFootnoteReference,\n} from '@bendyline/squisq/markdown';\nimport { stripHtmlTags } from './text.js';\n\n/** Active character formatting threaded down through nested inline nodes. */\nexport interface InlineRunFormat {\n bold?: boolean;\n italic?: boolean;\n strike?: boolean;\n code?: boolean;\n /** Explicit run color (hex without `#`); only some exporters use this. */\n color?: string;\n}\n\n/**\n * Per-format leaf handlers. The walker handles the shared format-threading\n * control flow; these turn the leaves into the target dialect's markup.\n */\nexport interface InlineRunHandlers {\n /** Emit a styled text run for `text`, given the active format. */\n run(text: string, format: InlineRunFormat): string;\n /** Emit a hyperlink (the handler manages its own child rendering). */\n link(node: MarkdownLink, format: InlineRunFormat): string;\n /** Emit an inline image. */\n image(node: MarkdownImage, format: InlineRunFormat): string;\n /** Emit a hard line break. */\n lineBreak(): string;\n /** Emit a footnote reference. Omit to drop footnote refs (PPTX). */\n footnoteRef?(node: MarkdownFootnoteReference): string;\n}\n\n/** Walk a list of inline nodes, concatenating each rendered run. */\nexport function inlineNodesToRuns(\n nodes: MarkdownInlineNode[],\n handlers: InlineRunHandlers,\n format: InlineRunFormat = {},\n): string {\n let out = '';\n for (const node of nodes) {\n out += inlineNodeToRuns(node, handlers, format);\n }\n return out;\n}\n\n/** Render a single inline node under the active format. */\nexport function inlineNodeToRuns(\n node: MarkdownInlineNode,\n handlers: InlineRunHandlers,\n format: InlineRunFormat,\n): string {\n switch (node.type) {\n case 'text':\n return handlers.run(node.value, format);\n case 'strong':\n return inlineNodesToRuns(node.children, handlers, { ...format, bold: true });\n case 'emphasis':\n return inlineNodesToRuns(node.children, handlers, { ...format, italic: true });\n case 'delete':\n return inlineNodesToRuns(node.children, handlers, { ...format, strike: true });\n case 'inlineCode':\n return handlers.run(node.value, { ...format, code: true });\n case 'inlineMath':\n return handlers.run(node.value, { ...format, code: true });\n case 'htmlInline':\n return handlers.run(stripHtmlTags(node.rawHtml), format);\n case 'link':\n return handlers.link(node, format);\n case 'image':\n return handlers.image(node, format);\n case 'break':\n return handlers.lineBreak();\n case 'footnoteReference':\n return handlers.footnoteRef ? handlers.footnoteRef(node) : '';\n default:\n // linkReference, imageReference, textDirective, inlineIcon, … — skip.\n return '';\n }\n}\n"],"mappings":";;;;;AAqDO,SAAS,kBACd,OACA,UACA,SAA0B,CAAC,GACnB;AACR,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,WAAO,iBAAiB,MAAM,UAAU,MAAM;AAAA,EAChD;AACA,SAAO;AACT;AAGO,SAAS,iBACd,MACA,UACA,QACQ;AACR,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,SAAS,IAAI,KAAK,OAAO,MAAM;AAAA,IACxC,KAAK;AACH,aAAO,kBAAkB,KAAK,UAAU,UAAU,EAAE,GAAG,QAAQ,MAAM,KAAK,CAAC;AAAA,IAC7E,KAAK;AACH,aAAO,kBAAkB,KAAK,UAAU,UAAU,EAAE,GAAG,QAAQ,QAAQ,KAAK,CAAC;AAAA,IAC/E,KAAK;AACH,aAAO,kBAAkB,KAAK,UAAU,UAAU,EAAE,GAAG,QAAQ,QAAQ,KAAK,CAAC;AAAA,IAC/E,KAAK;AACH,aAAO,SAAS,IAAI,KAAK,OAAO,EAAE,GAAG,QAAQ,MAAM,KAAK,CAAC;AAAA,IAC3D,KAAK;AACH,aAAO,SAAS,IAAI,KAAK,OAAO,EAAE,GAAG,QAAQ,MAAM,KAAK,CAAC;AAAA,IAC3D,KAAK;AACH,aAAO,SAAS,IAAI,cAAc,KAAK,OAAO,GAAG,MAAM;AAAA,IACzD,KAAK;AACH,aAAO,SAAS,KAAK,MAAM,MAAM;AAAA,IACnC,KAAK;AACH,aAAO,SAAS,MAAM,MAAM,MAAM;AAAA,IACpC,KAAK;AACH,aAAO,SAAS,UAAU;AAAA,IAC5B,KAAK;AACH,aAAO,SAAS,cAAc,SAAS,YAAY,IAAI,IAAI;AAAA,IAC7D;AAEE,aAAO;AAAA,EACX;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  // src/pdf/export.ts
2
2
  import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
3
- import { resolveTheme } from "@bendyline/squisq/schemas";
4
- import { docToMarkdown } from "@bendyline/squisq/doc";
3
+ import { docToMarkdown, resolveThemeForDoc } from "@bendyline/squisq/doc";
4
+ import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
5
5
 
6
6
  // src/pdf/styles.ts
7
7
  var PAGE_WIDTH_LETTER = 612;
@@ -73,7 +73,7 @@ async function markdownDocToPdf(doc, options = {}) {
73
73
  if (options.author) pdfDoc.setAuthor(options.author);
74
74
  pdfDoc.setCreationDate(/* @__PURE__ */ new Date());
75
75
  pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
76
- const ctx = await createExportContext(pdfDoc, options);
76
+ const ctx = await createExportContext(pdfDoc, options, doc);
77
77
  renderBlocks(doc.children, ctx, 0);
78
78
  const bytes = await pdfDoc.save();
79
79
  return bytes.buffer;
@@ -82,7 +82,7 @@ async function docToPdf(doc, options = {}) {
82
82
  const markdownDoc = docToMarkdown(doc);
83
83
  return markdownDocToPdf(markdownDoc, options);
84
84
  }
85
- async function createExportContext(pdfDoc, options) {
85
+ async function createExportContext(pdfDoc, options, doc) {
86
86
  const [regular, bold, italic, boldItalic, mono, monoBold] = await Promise.all([
87
87
  pdfDoc.embedFont(StandardFonts.Helvetica),
88
88
  pdfDoc.embedFont(StandardFonts.HelveticaBold),
@@ -100,8 +100,9 @@ async function createExportContext(pdfDoc, options) {
100
100
  let colorText = COLOR_TEXT;
101
101
  let colorHeading = COLOR_HEADING;
102
102
  let colorLink = COLOR_LINK;
103
- if (options.themeId) {
104
- const theme = resolveTheme(options.themeId);
103
+ const themeId = options.themeId ?? readFrontmatterThemeId(doc.frontmatter);
104
+ if (themeId) {
105
+ const theme = resolveThemeForDoc(doc, themeId);
105
106
  if (theme.colors) {
106
107
  colorText = hexToRgb(theme.colors.text) ?? COLOR_TEXT;
107
108
  colorHeading = hexToRgb(theme.colors.primary) ?? COLOR_HEADING;
@@ -711,6 +712,7 @@ function hexToRgb(hex) {
711
712
 
712
713
  // src/pdf/import.ts
713
714
  import { markdownToDoc } from "@bendyline/squisq/doc";
715
+ import { stringifyMarkdown } from "@bendyline/squisq/markdown";
714
716
  import { MemoryContentContainer } from "@bendyline/squisq/storage";
715
717
  async function pdfToMarkdownDoc(data, options = {}) {
716
718
  const bytes = data instanceof Blob ? new Uint8Array(await data.arrayBuffer()) : data instanceof ArrayBuffer ? new Uint8Array(data) : data;
@@ -736,7 +738,6 @@ async function pdfToContainer(data, options = {}) {
736
738
  blocks = insertImageBlocks(blocks, images);
737
739
  }
738
740
  const markdownDoc = { type: "document", children: blocks };
739
- const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
740
741
  const markdown = stringifyMarkdown(markdownDoc);
741
742
  const container = new MemoryContentContainer();
742
743
  await container.writeDocument(markdown);
@@ -1283,4 +1284,4 @@ export {
1283
1284
  pdfToContainer,
1284
1285
  configurePdfWorker
1285
1286
  };
1286
- //# sourceMappingURL=chunk-S3Y7H2BK.js.map
1287
+ //# sourceMappingURL=chunk-APSIAEXL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pdf/export.ts","../src/pdf/styles.ts","../src/pdf/import.ts"],"sourcesContent":["/**\n * PDF Export\n *\n * Converts a squisq MarkdownDocument (or Doc) into a PDF file\n * using pdf-lib. Generates paginated, styled output with support for\n * headings, paragraphs, inline formatting, lists, code blocks,\n * blockquotes, tables, thematic breaks, and hyperlinks.\n *\n * Uses only the 14 standard PDF fonts (no font embedding required),\n * keeping output size small and rendering fast.\n *\n * @example\n * ```ts\n * import { parseMarkdown } from '@bendyline/squisq/markdown';\n * import { markdownDocToPdf } from '@bendyline/squisq-formats/pdf';\n *\n * const md = parseMarkdown('# Hello\\n\\nWorld **bold** text');\n * const buffer = await markdownDocToPdf(md);\n * ```\n */\n\nimport { PDFDocument, StandardFonts, rgb, PDFFont, PDFPage } from 'pdf-lib';\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { docToMarkdown, resolveThemeForDoc } from '@bendyline/squisq/doc';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownHeading,\n MarkdownParagraph,\n MarkdownBlockquote,\n MarkdownList,\n MarkdownListItem,\n MarkdownCodeBlock,\n MarkdownTable,\n MarkdownTableRow,\n MarkdownTableCell,\n MarkdownHtmlBlock,\n MarkdownMathBlock,\n MarkdownFootnoteDefinition,\n MarkdownText,\n MarkdownEmphasis,\n MarkdownStrong,\n MarkdownStrikethrough,\n MarkdownInlineCode,\n MarkdownLink,\n MarkdownImage,\n MarkdownInlineHtml,\n MarkdownInlineMath,\n MarkdownFootnoteReference,\n} from '@bendyline/squisq/markdown';\nimport { readFrontmatterThemeId } from '@bendyline/squisq/markdown';\n\nimport {\n PAGE_WIDTH_LETTER,\n PAGE_HEIGHT_LETTER,\n PAGE_WIDTH_A4,\n PAGE_HEIGHT_A4,\n DEFAULT_MARGIN,\n DEFAULT_FONT_SIZE,\n HEADING_SIZES,\n CODE_FONT_SIZE,\n LINE_HEIGHT_FACTOR,\n HEADING_SPACE_BEFORE,\n HEADING_SPACE_AFTER,\n PARAGRAPH_SPACING,\n LIST_INDENT,\n BULLET_CHAR,\n BLOCKQUOTE_INDENT,\n BLOCKQUOTE_BAR_WIDTH,\n COLOR_TEXT,\n COLOR_HEADING,\n COLOR_LINK,\n COLOR_CODE_TEXT,\n COLOR_BLOCKQUOTE_BAR,\n COLOR_BLOCKQUOTE_TEXT,\n COLOR_THEMATIC_BREAK,\n COLOR_TABLE_BORDER,\n COLOR_TABLE_HEADER_BG,\n TABLE_CELL_PAD_X,\n TABLE_CELL_PAD_Y,\n TABLE_BORDER_WIDTH,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/** Page size presets. */\nexport type PdfPageSize = 'letter' | 'a4';\n\n/**\n * Options for PDF export.\n */\nexport interface PdfExportOptions {\n /** Document title (PDF metadata). */\n title?: string;\n /** Document author (PDF metadata). */\n author?: string;\n /** Page size preset. Default: \"letter\". */\n pageSize?: PdfPageSize;\n /** Page margins in points. Default: 72 (1 inch). */\n margin?: number;\n /** Default body font size in points. Default: 11. */\n defaultFontSize?: number;\n /**\n * Squisq theme ID to apply (e.g., 'documentary', 'cinematic').\n * When set, overrides heading, text, and link colors with the theme palette.\n * Font changes are not supported (pdf-lib uses standard 14 PDF fonts only).\n */\n themeId?: string;\n}\n\n/**\n * Convert a MarkdownDocument to a PDF ArrayBuffer.\n */\nexport async function markdownDocToPdf(\n doc: MarkdownDocument,\n options: PdfExportOptions = {},\n): Promise<ArrayBuffer> {\n const pdfDoc = await PDFDocument.create();\n\n // Metadata\n if (options.title) pdfDoc.setTitle(options.title);\n if (options.author) pdfDoc.setAuthor(options.author);\n pdfDoc.setCreationDate(new Date());\n pdfDoc.setModificationDate(new Date());\n\n const ctx = await createExportContext(pdfDoc, options, doc);\n\n renderBlocks(doc.children, ctx, 0);\n\n const bytes = await pdfDoc.save();\n return bytes.buffer as ArrayBuffer;\n}\n\n/**\n * Convert a squisq Doc to a PDF ArrayBuffer.\n *\n * Convenience wrapper: Doc → MarkdownDocument → PDF.\n */\nexport async function docToPdf(doc: Doc, options: PdfExportOptions = {}): Promise<ArrayBuffer> {\n const markdownDoc = docToMarkdown(doc);\n return markdownDocToPdf(markdownDoc, options);\n}\n\n// ============================================\n// Export Context — tracks cursor, fonts, pages\n// ============================================\n\ninterface FontSet {\n regular: PDFFont;\n bold: PDFFont;\n italic: PDFFont;\n boldItalic: PDFFont;\n mono: PDFFont;\n monoBold: PDFFont;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\ninterface ExportContext {\n pdfDoc: PDFDocument;\n fonts: FontSet;\n pageWidth: number;\n pageHeight: number;\n margin: number;\n fontSize: number;\n /** Current page being drawn on. */\n page: PDFPage;\n /** Current y position (from top of page, decreasing). */\n y: number;\n /** Content area width = pageWidth - 2*margin. */\n contentWidth: number;\n /** Bottom margin y position. */\n bottomY: number;\n /** Resolved colors (may be overridden by theme). */\n colors: {\n text: RgbColor;\n heading: RgbColor;\n link: RgbColor;\n };\n}\n\nasync function createExportContext(\n pdfDoc: PDFDocument,\n options: PdfExportOptions,\n doc: MarkdownDocument,\n): Promise<ExportContext> {\n const [regular, bold, italic, boldItalic, mono, monoBold] = await Promise.all([\n pdfDoc.embedFont(StandardFonts.Helvetica),\n pdfDoc.embedFont(StandardFonts.HelveticaBold),\n pdfDoc.embedFont(StandardFonts.HelveticaOblique),\n pdfDoc.embedFont(StandardFonts.HelveticaBoldOblique),\n pdfDoc.embedFont(StandardFonts.Courier),\n pdfDoc.embedFont(StandardFonts.CourierBold),\n ]);\n\n const isA4 = options.pageSize === 'a4';\n const pageWidth = isA4 ? PAGE_WIDTH_A4 : PAGE_WIDTH_LETTER;\n const pageHeight = isA4 ? PAGE_HEIGHT_A4 : PAGE_HEIGHT_LETTER;\n const margin = options.margin ?? DEFAULT_MARGIN;\n const fontSize = options.defaultFontSize ?? DEFAULT_FONT_SIZE;\n\n const page = pdfDoc.addPage([pageWidth, pageHeight]);\n\n // Resolve theme colors if themeId is set\n let colorText = COLOR_TEXT;\n let colorHeading = COLOR_HEADING;\n let colorLink = COLOR_LINK;\n\n // Honor an explicit themeId, else the doc's frontmatter theme (`squisq-theme`\n // / legacy) — mirroring PPTX/DOCX/HTML so every format themes consistently,\n // including inline custom themes (resolved doc-scoped).\n const themeId = options.themeId ?? readFrontmatterThemeId(doc.frontmatter);\n if (themeId) {\n const theme = resolveThemeForDoc(doc, themeId);\n if (theme.colors) {\n colorText = hexToRgb(theme.colors.text) ?? COLOR_TEXT;\n colorHeading = hexToRgb(theme.colors.primary) ?? COLOR_HEADING;\n colorLink = hexToRgb(theme.colors.highlight || theme.colors.secondary) ?? COLOR_LINK;\n }\n }\n\n return {\n pdfDoc,\n fonts: { regular, bold, italic, boldItalic, mono, monoBold },\n pageWidth,\n pageHeight,\n margin,\n fontSize,\n page,\n y: pageHeight - margin,\n contentWidth: pageWidth - 2 * margin,\n bottomY: margin,\n colors: { text: colorText, heading: colorHeading, link: colorLink },\n };\n}\n\n// ============================================\n// Page Break Management\n// ============================================\n\nfunction ensureSpace(ctx: ExportContext, needed: number): void {\n if (ctx.y - needed < ctx.bottomY) {\n newPage(ctx);\n }\n}\n\nfunction newPage(ctx: ExportContext): void {\n const page = ctx.pdfDoc.addPage([ctx.pageWidth, ctx.pageHeight]);\n ctx.page = page;\n ctx.y = ctx.pageHeight - ctx.margin;\n}\n\n// ============================================\n// Inline \"Span\" Model\n// ============================================\n\ninterface TextSpan {\n text: string;\n font: PDFFont;\n fontSize: number;\n color: { r: number; g: number; b: number };\n link?: string;\n strikethrough?: boolean;\n}\n\n/**\n * Flatten an inline node tree into a flat list of TextSpans,\n * accumulating bold/italic/code state as we recurse.\n */\nfunction flattenInlines(\n nodes: MarkdownInlineNode[],\n ctx: ExportContext,\n state: {\n bold: boolean;\n italic: boolean;\n code: boolean;\n link?: string;\n color?: { r: number; g: number; b: number };\n strikethrough?: boolean;\n },\n): TextSpan[] {\n const spans: TextSpan[] = [];\n\n for (const node of nodes) {\n switch (node.type) {\n case 'text': {\n const font = state.code\n ? state.bold\n ? ctx.fonts.monoBold\n : ctx.fonts.mono\n : pickFont(ctx, state.bold, state.italic);\n spans.push({\n text: (node as MarkdownText).value,\n font,\n fontSize: state.code ? CODE_FONT_SIZE : ctx.fontSize,\n color: state.code ? COLOR_CODE_TEXT : (state.color ?? ctx.colors.text),\n link: state.link,\n strikethrough: state.strikethrough,\n });\n break;\n }\n\n case 'strong':\n spans.push(\n ...flattenInlines((node as MarkdownStrong).children, ctx, { ...state, bold: true }),\n );\n break;\n\n case 'emphasis':\n spans.push(\n ...flattenInlines((node as MarkdownEmphasis).children, ctx, { ...state, italic: true }),\n );\n break;\n\n case 'delete':\n spans.push(\n ...flattenInlines((node as MarkdownStrikethrough).children, ctx, {\n ...state,\n strikethrough: true,\n }),\n );\n break;\n\n case 'inlineCode': {\n spans.push({\n text: (node as MarkdownInlineCode).value,\n font: ctx.fonts.mono,\n fontSize: CODE_FONT_SIZE,\n color: COLOR_CODE_TEXT,\n link: state.link,\n });\n break;\n }\n\n case 'link': {\n const linkNode = node as MarkdownLink;\n spans.push(\n ...flattenInlines(linkNode.children, ctx, {\n ...state,\n link: linkNode.url,\n color: ctx.colors.link,\n }),\n );\n break;\n }\n\n case 'image': {\n const imgNode = node as MarkdownImage;\n spans.push({\n text: imgNode.alt ? `[Image: ${imgNode.alt}]` : '[Image]',\n font: ctx.fonts.italic,\n fontSize: ctx.fontSize,\n color: COLOR_BLOCKQUOTE_TEXT,\n });\n break;\n }\n\n case 'break':\n spans.push({\n text: '\\n',\n font: ctx.fonts.regular,\n fontSize: ctx.fontSize,\n color: ctx.colors.text,\n });\n break;\n\n case 'htmlInline': {\n const html = (node as MarkdownInlineHtml).rawHtml;\n if (html) {\n spans.push({\n text: html,\n font: ctx.fonts.mono,\n fontSize: CODE_FONT_SIZE,\n color: COLOR_CODE_TEXT,\n });\n }\n break;\n }\n\n case 'inlineMath':\n spans.push({\n text: (node as MarkdownInlineMath).value,\n font: ctx.fonts.mono,\n fontSize: CODE_FONT_SIZE,\n color: COLOR_CODE_TEXT,\n });\n break;\n\n case 'footnoteReference': {\n const ref = node as MarkdownFootnoteReference;\n spans.push({\n text: `[${ref.identifier}]`,\n font: ctx.fonts.regular,\n fontSize: ctx.fontSize * 0.75,\n color: ctx.colors.link,\n });\n break;\n }\n\n // linkReference, imageReference, textDirective — render children or identifier\n default: {\n const fallback = node as unknown as { children?: MarkdownInlineNode[]; value?: string };\n if (fallback.children) {\n spans.push(...flattenInlines(fallback.children, ctx, state));\n } else if (fallback.value) {\n spans.push({\n text: fallback.value,\n font: pickFont(ctx, state.bold, state.italic),\n fontSize: ctx.fontSize,\n color: state.color ?? ctx.colors.text,\n });\n }\n break;\n }\n }\n }\n\n return spans;\n}\n\nfunction pickFont(ctx: ExportContext, bold: boolean, italic: boolean): PDFFont {\n if (bold && italic) return ctx.fonts.boldItalic;\n if (bold) return ctx.fonts.bold;\n if (italic) return ctx.fonts.italic;\n return ctx.fonts.regular;\n}\n\n// ============================================\n// Word-Wrap & Draw\n// ============================================\n\n/**\n * Word-wraps and draws a flat list of TextSpans within the given\n * available width, starting at ctx.y. Updates ctx.y after drawing.\n * Returns the y position after the last line.\n */\nfunction drawSpans(\n spans: TextSpan[],\n ctx: ExportContext,\n availableWidth: number,\n x0: number,\n): void {\n if (spans.length === 0) return;\n\n // Split spans at \\n and word boundaries, then wrap into lines\n const lines = wrapSpans(spans, availableWidth);\n\n for (const line of lines) {\n const lineHeight = getLineHeight(line);\n ensureSpace(ctx, lineHeight);\n\n let x = x0;\n for (const span of line) {\n ctx.page.drawText(span.text, {\n x,\n y: ctx.y - span.fontSize, // pdf-lib y is baseline\n size: span.fontSize,\n font: span.font,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n\n const textWidth = span.font.widthOfTextAtSize(span.text, span.fontSize);\n\n // Underline for links\n if (span.link) {\n ctx.page.drawLine({\n start: { x, y: ctx.y - span.fontSize - 1 },\n end: { x: x + textWidth, y: ctx.y - span.fontSize - 1 },\n thickness: 0.5,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n }\n\n // Strikethrough\n if (span.strikethrough) {\n const midY = ctx.y - span.fontSize * 0.6;\n ctx.page.drawLine({\n start: { x, y: midY },\n end: { x: x + textWidth, y: midY },\n thickness: 0.5,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n }\n\n x += textWidth;\n }\n\n ctx.y -= lineHeight;\n }\n}\n\n/**\n * Break a flat list of spans into wrapped lines that fit within\n * `maxWidth`. Respects explicit \\n characters.\n */\nfunction wrapSpans(spans: TextSpan[], maxWidth: number): TextSpan[][] {\n const lines: TextSpan[][] = [];\n let currentLine: TextSpan[] = [];\n let lineWidth = 0;\n\n for (const span of spans) {\n // Handle explicit newlines\n if (span.text === '\\n') {\n lines.push(currentLine);\n currentLine = [];\n lineWidth = 0;\n continue;\n }\n\n // Split by whitespace for word-wrapping\n const words = splitIntoWords(span.text);\n\n for (const word of words) {\n const wordWidth = span.font.widthOfTextAtSize(word, span.fontSize);\n\n if (lineWidth + wordWidth > maxWidth && currentLine.length > 0 && word.trim().length > 0) {\n // Wrap to next line\n lines.push(currentLine);\n currentLine = [];\n lineWidth = 0;\n }\n\n // Trim leading space on new line\n const trimmedWord = currentLine.length === 0 ? word.replace(/^\\s+/, '') : word;\n if (trimmedWord.length > 0) {\n const tw = span.font.widthOfTextAtSize(trimmedWord, span.fontSize);\n currentLine.push({ ...span, text: trimmedWord });\n lineWidth += tw;\n }\n }\n }\n\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [[]];\n}\n\n/**\n * Split text into \"words\" preserving whitespace as separate tokens\n * so the wrapping logic can break at whitespace.\n */\nfunction splitIntoWords(text: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let inSpace = false;\n\n for (const ch of text) {\n const isSpace = ch === ' ' || ch === '\\t';\n if (isSpace !== inSpace && current.length > 0) {\n tokens.push(current);\n current = '';\n }\n current += ch;\n inSpace = isSpace;\n }\n if (current.length > 0) tokens.push(current);\n return tokens;\n}\n\nfunction getLineHeight(line: TextSpan[]): number {\n let maxSize = 0;\n for (const span of line) {\n if (span.fontSize > maxSize) maxSize = span.fontSize;\n }\n return (maxSize || DEFAULT_FONT_SIZE) * LINE_HEIGHT_FACTOR;\n}\n\n// ============================================\n// Block Renderers\n// ============================================\n\nfunction renderBlocks(nodes: MarkdownBlockNode[], ctx: ExportContext, extraIndent: number): void {\n for (const node of nodes) {\n renderBlock(node, ctx, extraIndent);\n }\n}\n\nfunction renderBlock(node: MarkdownBlockNode, ctx: ExportContext, extraIndent: number): void {\n switch (node.type) {\n case 'heading':\n renderHeading(node as MarkdownHeading, ctx, extraIndent);\n break;\n case 'paragraph':\n renderParagraph(node as MarkdownParagraph, ctx, extraIndent);\n break;\n case 'blockquote':\n renderBlockquote(node as MarkdownBlockquote, ctx, extraIndent);\n break;\n case 'list':\n renderList(node as MarkdownList, ctx, extraIndent, 0);\n break;\n case 'code':\n renderCodeBlock(node as MarkdownCodeBlock, ctx, extraIndent);\n break;\n case 'table':\n renderTable(node as MarkdownTable, ctx, extraIndent);\n break;\n case 'thematicBreak':\n renderThematicBreak(ctx, extraIndent);\n break;\n case 'htmlBlock':\n renderHtmlBlock(node as MarkdownHtmlBlock, ctx, extraIndent);\n break;\n case 'math':\n renderMathBlock(node as MarkdownMathBlock, ctx, extraIndent);\n break;\n case 'footnoteDefinition':\n renderFootnoteDefinition(node as MarkdownFootnoteDefinition, ctx, extraIndent);\n break;\n default:\n // containerDirective, leafDirective, definitionList, etc.\n // Render any children or value as best-effort\n renderFallbackBlock(node, ctx, extraIndent);\n break;\n }\n}\n\n// ---- Heading ----\n\nfunction renderHeading(node: MarkdownHeading, ctx: ExportContext, extraIndent: number): void {\n const origFontSize = ctx.fontSize;\n ctx.fontSize = HEADING_SIZES[node.depth] ?? DEFAULT_FONT_SIZE;\n\n ctx.y -= HEADING_SPACE_BEFORE;\n\n const x0 = ctx.margin + extraIndent;\n const w = ctx.contentWidth - extraIndent;\n\n const spans = flattenInlines(node.children, ctx, {\n bold: true,\n italic: false,\n code: false,\n color: ctx.colors.heading,\n });\n\n drawSpans(spans, ctx, w, x0);\n\n ctx.y -= HEADING_SPACE_AFTER;\n ctx.fontSize = origFontSize;\n}\n\n// ---- Paragraph ----\n\nfunction renderParagraph(\n node: MarkdownParagraph,\n ctx: ExportContext,\n extraIndent: number,\n colorOverride?: { r: number; g: number; b: number },\n): void {\n const x0 = ctx.margin + extraIndent;\n const w = ctx.contentWidth - extraIndent;\n\n const spans = flattenInlines(node.children, ctx, {\n bold: false,\n italic: false,\n code: false,\n color: colorOverride,\n });\n\n drawSpans(spans, ctx, w, x0);\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Blockquote ----\n\nfunction renderBlockquote(node: MarkdownBlockquote, ctx: ExportContext, extraIndent: number): void {\n const barX = ctx.margin + extraIndent + 4;\n const indent = extraIndent + BLOCKQUOTE_INDENT;\n const startY = ctx.y;\n\n for (const child of node.children) {\n if (child.type === 'paragraph') {\n renderParagraph(child as MarkdownParagraph, ctx, indent, COLOR_BLOCKQUOTE_TEXT);\n } else {\n renderBlock(child, ctx, indent);\n }\n }\n\n // Draw left bar from startY to ctx.y\n const endY = ctx.y + PARAGRAPH_SPACING; // undo last paragraph spacing\n if (startY > endY) {\n // Bar might span pages — draw on current page only\n ctx.page.drawRectangle({\n x: barX,\n y: endY,\n width: BLOCKQUOTE_BAR_WIDTH,\n height: startY - endY,\n color: rgb(COLOR_BLOCKQUOTE_BAR.r, COLOR_BLOCKQUOTE_BAR.g, COLOR_BLOCKQUOTE_BAR.b),\n });\n }\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- List ----\n\nfunction renderList(\n node: MarkdownList,\n ctx: ExportContext,\n extraIndent: number,\n depth: number,\n): void {\n const ordered = node.ordered ?? false;\n let counter = node.start ?? 1;\n\n for (const child of node.children) {\n if (child.type === 'listItem') {\n renderListItem(child as MarkdownListItem, ctx, extraIndent, depth, ordered, counter);\n if (ordered) counter++;\n }\n }\n}\n\nfunction renderListItem(\n item: MarkdownListItem,\n ctx: ExportContext,\n extraIndent: number,\n depth: number,\n ordered: boolean,\n counter: number,\n): void {\n const indent = extraIndent + depth * LIST_INDENT;\n const bullet = ordered ? `${counter}.` : BULLET_CHAR;\n const bulletFont = ctx.fonts.regular;\n const bulletWidth = bulletFont.widthOfTextAtSize(bullet + ' ', ctx.fontSize);\n\n // Draw bullet\n const lineHeight = ctx.fontSize * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineHeight);\n\n ctx.page.drawText(bullet, {\n x: ctx.margin + indent,\n y: ctx.y - ctx.fontSize,\n size: ctx.fontSize,\n font: bulletFont,\n color: rgb(ctx.colors.text.r, ctx.colors.text.g, ctx.colors.text.b),\n });\n\n const textIndent = indent + bulletWidth + 4;\n const textWidth = ctx.contentWidth - textIndent;\n\n // Render children at the text indent\n let isFirstChild = true;\n for (const child of item.children) {\n if (child.type === 'paragraph' && isFirstChild) {\n // First paragraph: render on same line as bullet\n const spans = flattenInlines((child as MarkdownParagraph).children, ctx, {\n bold: false,\n italic: false,\n code: false,\n });\n drawSpans(spans, ctx, textWidth, ctx.margin + textIndent);\n ctx.y -= PARAGRAPH_SPACING / 2;\n } else if (child.type === 'list') {\n renderList(child as MarkdownList, ctx, extraIndent, depth + 1);\n } else {\n renderBlock(child, ctx, textIndent);\n }\n isFirstChild = false;\n }\n}\n\n// ---- Code Block ----\n\nfunction renderCodeBlock(node: MarkdownCodeBlock, ctx: ExportContext, extraIndent: number): void {\n const x0 = ctx.margin + extraIndent;\n const _w = ctx.contentWidth - extraIndent;\n const lines = node.value.split('\\n');\n const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;\n const totalHeight = lines.length * lineH + 12; // 12 = vertical padding\n\n ensureSpace(ctx, Math.min(totalHeight, ctx.y - ctx.bottomY));\n\n const _bgTop = ctx.y;\n\n // Draw background first (we'll adjust after we know the final y)\n const _bgStartY = ctx.y;\n ctx.y -= 6; // top padding\n\n for (const line of lines) {\n ensureSpace(ctx, lineH);\n if (line.length > 0) {\n ctx.page.drawText(line, {\n x: x0 + 8,\n y: ctx.y - CODE_FONT_SIZE,\n size: CODE_FONT_SIZE,\n font: ctx.fonts.mono,\n color: rgb(COLOR_CODE_TEXT.r, COLOR_CODE_TEXT.g, COLOR_CODE_TEXT.b),\n });\n }\n ctx.y -= lineH;\n }\n\n ctx.y -= 6; // bottom padding\n\n // Draw background rectangle (behind text — pdf-lib draws on top,\n // so we could draw it first on a separate pass, but since we can't\n // easily re-order, we leave it as a visual approximation).\n // For a production implementation, you'd use a two-pass approach.\n // Here we draw the bg rect at the saved position. Text drawn after\n // will already be on the page. Since pdf-lib paints in order, we\n // accept that the bg goes on top — but only if bg is on same page.\n // A simpler approach: draw bg *before* text per page.\n\n // Actually, let's use a cleaner approach: draw line by line with bg\n // We already drew the text. For the background box, we'll just\n // not draw it behind (it would overlay). This is a known limitation\n // with pdf-lib's immediate mode. In practice the light grey bg makes\n // text hard to read if drawn on top. So we skip the bg for now and\n // rely on the monospace font to visually distinguish code.\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Table ----\n\nfunction renderTable(node: MarkdownTable, ctx: ExportContext, extraIndent: number): void {\n if (node.children.length === 0) return;\n\n const x0 = ctx.margin + extraIndent;\n const tableWidth = ctx.contentWidth - extraIndent;\n\n // Measure columns: get max width per column across all rows\n const numCols = Math.max(\n ...node.children.map((row) => (row as MarkdownTableRow).children.length),\n );\n if (numCols === 0) return;\n\n // Simple equal-width columns\n const colWidth = tableWidth / numCols;\n\n for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {\n const row = node.children[rowIdx] as MarkdownTableRow;\n const isHeader = rowIdx === 0 && row.children.some((c) => (c as MarkdownTableCell).isHeader);\n\n // Calculate row height\n const rowCellHeights = row.children.map((cell) => {\n const cellNode = cell as MarkdownTableCell;\n const spans = flattenInlines(cellNode.children, ctx, {\n bold: isHeader,\n italic: false,\n code: false,\n });\n const lines = wrapSpans(spans, colWidth - 2 * TABLE_CELL_PAD_X);\n return lines.length * ctx.fontSize * LINE_HEIGHT_FACTOR + 2 * TABLE_CELL_PAD_Y;\n });\n const rowHeight = Math.max(\n ...rowCellHeights,\n ctx.fontSize * LINE_HEIGHT_FACTOR + 2 * TABLE_CELL_PAD_Y,\n );\n\n ensureSpace(ctx, rowHeight);\n\n // Draw header background\n if (isHeader) {\n ctx.page.drawRectangle({\n x: x0,\n y: ctx.y - rowHeight,\n width: tableWidth,\n height: rowHeight,\n color: rgb(COLOR_TABLE_HEADER_BG.r, COLOR_TABLE_HEADER_BG.g, COLOR_TABLE_HEADER_BG.b),\n });\n }\n\n // Draw cell borders and text\n for (let colIdx = 0; colIdx < numCols; colIdx++) {\n const cellX = x0 + colIdx * colWidth;\n\n // Draw cell border\n ctx.page.drawRectangle({\n x: cellX,\n y: ctx.y - rowHeight,\n width: colWidth,\n height: rowHeight,\n borderColor: rgb(COLOR_TABLE_BORDER.r, COLOR_TABLE_BORDER.g, COLOR_TABLE_BORDER.b),\n borderWidth: TABLE_BORDER_WIDTH,\n });\n\n if (colIdx < row.children.length) {\n const cellNode = row.children[colIdx] as MarkdownTableCell;\n const spans = flattenInlines(cellNode.children, ctx, {\n bold: isHeader,\n italic: false,\n code: false,\n });\n\n // Draw text inside cell\n const savedY = ctx.y;\n ctx.y = ctx.y - TABLE_CELL_PAD_Y;\n\n const cellAvailableWidth = colWidth - 2 * TABLE_CELL_PAD_X;\n const wrappedLines = wrapSpans(spans, cellAvailableWidth);\n\n for (const line of wrappedLines) {\n let lx = cellX + TABLE_CELL_PAD_X;\n for (const span of line) {\n ctx.page.drawText(span.text, {\n x: lx,\n y: ctx.y - span.fontSize,\n size: span.fontSize,\n font: span.font,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n lx += span.font.widthOfTextAtSize(span.text, span.fontSize);\n }\n ctx.y -= getLineHeight(line);\n }\n\n ctx.y = savedY; // restore for next cell\n }\n }\n\n ctx.y -= rowHeight;\n }\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Thematic Break ----\n\nfunction renderThematicBreak(ctx: ExportContext, extraIndent: number): void {\n ctx.y -= PARAGRAPH_SPACING;\n ensureSpace(ctx, 10);\n\n const x0 = ctx.margin + extraIndent;\n const x1 = ctx.margin + ctx.contentWidth;\n\n ctx.page.drawLine({\n start: { x: x0, y: ctx.y },\n end: { x: x1, y: ctx.y },\n thickness: 1,\n color: rgb(COLOR_THEMATIC_BREAK.r, COLOR_THEMATIC_BREAK.g, COLOR_THEMATIC_BREAK.b),\n });\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- HTML Block ----\n\nfunction renderHtmlBlock(node: MarkdownHtmlBlock, ctx: ExportContext, extraIndent: number): void {\n if (!node.rawHtml) return;\n // Render raw HTML as monospace text (best effort)\n const lines = node.rawHtml.split('\\n');\n const x0 = ctx.margin + extraIndent;\n for (const line of lines) {\n if (line.trim().length === 0) continue;\n const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n ctx.page.drawText(line, {\n x: x0,\n y: ctx.y - CODE_FONT_SIZE,\n size: CODE_FONT_SIZE,\n font: ctx.fonts.mono,\n color: rgb(COLOR_CODE_TEXT.r, COLOR_CODE_TEXT.g, COLOR_CODE_TEXT.b),\n });\n ctx.y -= lineH;\n }\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Math Block ----\n\nfunction renderMathBlock(node: MarkdownMathBlock, ctx: ExportContext, extraIndent: number): void {\n // Render LaTeX source in monospace as fallback\n const lines = node.value.split('\\n');\n const x0 = ctx.margin + extraIndent;\n for (const line of lines) {\n const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n if (line.length > 0) {\n ctx.page.drawText(line, {\n x: x0,\n y: ctx.y - CODE_FONT_SIZE,\n size: CODE_FONT_SIZE,\n font: ctx.fonts.mono,\n color: rgb(COLOR_CODE_TEXT.r, COLOR_CODE_TEXT.g, COLOR_CODE_TEXT.b),\n });\n }\n ctx.y -= lineH;\n }\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Footnote Definition ----\n\nfunction renderFootnoteDefinition(\n node: MarkdownFootnoteDefinition,\n ctx: ExportContext,\n extraIndent: number,\n): void {\n // Draw footnote identifier\n const label = `[${node.identifier}]`;\n const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n\n ctx.page.drawText(label, {\n x: ctx.margin + extraIndent,\n y: ctx.y - ctx.fontSize * 0.75,\n size: ctx.fontSize * 0.75,\n font: ctx.fonts.bold,\n color: rgb(ctx.colors.link.r, ctx.colors.link.g, ctx.colors.link.b),\n });\n\n // Render children indented\n renderBlocks(node.children, ctx, extraIndent + LIST_INDENT);\n}\n\n// ---- Fallback ----\n\nfunction renderFallbackBlock(\n node: MarkdownBlockNode,\n ctx: ExportContext,\n extraIndent: number,\n): void {\n const fallback = node as unknown as {\n children?: (MarkdownBlockNode | MarkdownInlineNode)[];\n value?: string;\n };\n if (fallback.children && Array.isArray(fallback.children)) {\n // Could be block children or inline children\n if (fallback.children.length > 0 && typeof fallback.children[0]?.type === 'string') {\n const firstType = fallback.children[0].type;\n // Heuristic: if first child looks like an inline node, wrap as paragraph\n const inlineTypes = new Set([\n 'text',\n 'strong',\n 'emphasis',\n 'delete',\n 'inlineCode',\n 'link',\n 'image',\n 'break',\n ]);\n if (inlineTypes.has(firstType)) {\n const spans = flattenInlines(fallback.children as MarkdownInlineNode[], ctx, {\n bold: false,\n italic: false,\n code: false,\n });\n drawSpans(spans, ctx, ctx.contentWidth - extraIndent, ctx.margin + extraIndent);\n ctx.y -= PARAGRAPH_SPACING;\n return;\n }\n }\n renderBlocks(fallback.children as MarkdownBlockNode[], ctx, extraIndent);\n } else if (fallback.value && typeof fallback.value === 'string') {\n const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n ctx.page.drawText(fallback.value, {\n x: ctx.margin + extraIndent,\n y: ctx.y - ctx.fontSize,\n size: ctx.fontSize,\n font: ctx.fonts.regular,\n color: rgb(ctx.colors.text.r, ctx.colors.text.g, ctx.colors.text.b),\n });\n ctx.y -= lineH + PARAGRAPH_SPACING;\n }\n}\n\n// ============================================\n// Color Helpers\n// ============================================\n\nfunction hexToRgb(hex: string): RgbColor | undefined {\n const h = hex.startsWith('#') ? hex.slice(1) : hex;\n if (h.length !== 6) return undefined;\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n if (isNaN(r) || isNaN(g) || isNaN(b)) return undefined;\n return { r: r / 255, g: g / 255, b: b / 255 };\n}\n","/**\n * PDF Style / Layout Constants\n *\n * Shared constants used by both PDF export and import. Defines page\n * dimensions, margins, font sizes, colours, and spacing for rendering\n * MarkdownDocument content as a paginated PDF.\n */\n\n// ============================================\n// Page Layout\n// ============================================\n\n/** Standard US Letter page width in points (8.5 in × 72 pt/in). */\nexport const PAGE_WIDTH_LETTER = 612;\n\n/** Standard US Letter page height in points (11 in × 72 pt/in). */\nexport const PAGE_HEIGHT_LETTER = 792;\n\n/** A4 page width in points (210 mm ≈ 595 pt). */\nexport const PAGE_WIDTH_A4 = 595.28;\n\n/** A4 page height in points (297 mm ≈ 842 pt). */\nexport const PAGE_HEIGHT_A4 = 841.89;\n\n/** Default page margins in points (1 inch = 72 pt). */\nexport const DEFAULT_MARGIN = 72;\n\n// ============================================\n// Fonts — pdf-lib standard font names\n// ============================================\n\nexport const FONT_REGULAR = 'Helvetica';\nexport const FONT_BOLD = 'Helvetica-Bold';\nexport const FONT_ITALIC = 'Helvetica-Oblique';\nexport const FONT_BOLD_ITALIC = 'Helvetica-BoldOblique';\nexport const FONT_MONO = 'Courier';\nexport const FONT_MONO_BOLD = 'Courier-Bold';\n\n// ============================================\n// Font Sizes\n// ============================================\n\n/** Default body font size in points. */\nexport const DEFAULT_FONT_SIZE = 11;\n\n/** Heading font sizes indexed by depth (1-based). */\nexport const HEADING_SIZES: Record<number, number> = {\n 1: 24,\n 2: 20,\n 3: 16,\n 4: 14,\n 5: 13,\n 6: 12,\n};\n\n/** Font size used for code blocks and inline code. */\nexport const CODE_FONT_SIZE = 10;\n\n// ============================================\n// Spacing (in points)\n// ============================================\n\n/** Line height multiplier (leading). */\nexport const LINE_HEIGHT_FACTOR = 1.4;\n\n/** Space above a heading. */\nexport const HEADING_SPACE_BEFORE = 18;\n\n/** Space below a heading. */\nexport const HEADING_SPACE_AFTER = 6;\n\n/** Space between paragraphs. */\nexport const PARAGRAPH_SPACING = 8;\n\n/** Extra indent per list nesting level. */\nexport const LIST_INDENT = 24;\n\n/** Bullet character for unordered lists. */\nexport const BULLET_CHAR = '\\u2022'; // •\n\n/** Indent for blockquotes. */\nexport const BLOCKQUOTE_INDENT = 24;\n\n/** Width of the blockquote left bar. */\nexport const BLOCKQUOTE_BAR_WIDTH = 3;\n\n// ============================================\n// Colours (RGB 0-1)\n// ============================================\n\nexport const COLOR_TEXT = { r: 0.12, g: 0.12, b: 0.14 };\nexport const COLOR_HEADING = { r: 0.08, g: 0.08, b: 0.1 };\nexport const COLOR_LINK = { r: 0.05, g: 0.27, b: 0.73 };\nexport const COLOR_CODE_BG = { r: 0.95, g: 0.95, b: 0.97 };\nexport const COLOR_CODE_TEXT = { r: 0.2, g: 0.2, b: 0.22 };\nexport const COLOR_BLOCKQUOTE_BAR = { r: 0.75, g: 0.75, b: 0.78 };\nexport const COLOR_BLOCKQUOTE_TEXT = { r: 0.35, g: 0.35, b: 0.38 };\nexport const COLOR_THEMATIC_BREAK = { r: 0.78, g: 0.78, b: 0.8 };\nexport const COLOR_TABLE_BORDER = { r: 0.75, g: 0.75, b: 0.78 };\nexport const COLOR_TABLE_HEADER_BG = { r: 0.93, g: 0.93, b: 0.95 };\n\n// ============================================\n// Table Constants\n// ============================================\n\n/** Table cell horizontal padding in points. */\nexport const TABLE_CELL_PAD_X = 6;\n\n/** Table cell vertical padding in points. */\nexport const TABLE_CELL_PAD_Y = 4;\n\n/** Table border line width. */\nexport const TABLE_BORDER_WIDTH = 0.5;\n\n// ============================================\n// Import Heuristic Thresholds\n// ============================================\n\n/**\n * Minimum font size (in PDF points) to consider as a heading.\n * Anything ≥ this and larger than the detected body size is a heading.\n */\nexport const IMPORT_HEADING_MIN_SIZE = 13;\n\n/**\n * Font size ranges mapped to heading depth for import heuristics.\n * Applied when a text item's font size exceeds the body size.\n */\nexport const IMPORT_HEADING_SIZE_RANGES: Array<{ min: number; depth: number }> = [\n { min: 22, depth: 1 },\n { min: 18, depth: 2 },\n { min: 15, depth: 3 },\n { min: 13.5, depth: 4 },\n { min: 12.5, depth: 5 },\n { min: 12, depth: 6 },\n];\n\n/**\n * Y-distance (in points) between lines that implies a paragraph break\n * rather than a continuation of the same paragraph.\n */\nexport const IMPORT_PARAGRAPH_GAP = 4;\n\n/**\n * Characters that indicate an unordered list bullet at the start of a line.\n */\nexport const IMPORT_BULLET_CHARS = new Set([\n '\\u2022',\n '\\u2023',\n '\\u25E6',\n '\\u2043',\n '-',\n '*',\n '\\u2013',\n '\\u2014',\n '\\u25AA',\n '\\u25AB',\n]);\n\n/**\n * Regex to detect an ordered list prefix (e.g. \"1.\", \"2)\", \"iv.\").\n */\nexport const IMPORT_ORDERED_PREFIX = /^(\\d+|[a-z]+|[ivxlcdm]+)[.)]\\s/i;\n\n/**\n * Column alignment tolerance — text items whose x-positions differ by\n * less than this are considered to be in the same column (for table detection).\n */\nexport const IMPORT_COLUMN_TOLERANCE = 8;\n\n/**\n * Minimum number of consecutive rows with the same column count to\n * consider them a table.\n */\nexport const IMPORT_TABLE_MIN_ROWS = 2;\n\n/**\n * URL pattern for detecting links in extracted text.\n */\nexport const IMPORT_URL_PATTERN = /https?:\\/\\/[^\\s)>\\]]+/g;\n","/**\n * PDF Import\n *\n * Parses a PDF file and converts its content into a squisq\n * MarkdownDocument (or Doc) using heuristic detection of headings,\n * lists, code blocks, tables, blockquotes, and hyperlinks.\n *\n * Uses pdfjs-dist (Mozilla pdf.js) for text extraction — a battle-tested,\n * browser-compatible PDF parser. Since PDFs encode positioned glyphs\n * rather than semantic structure, all structure detection is inherently\n * heuristic and works best on simply-formatted documents.\n *\n * @example\n * ```ts\n * import { pdfToMarkdownDoc } from '@bendyline/squisq-formats/pdf';\n *\n * const response = await fetch('document.pdf');\n * const data = await response.arrayBuffer();\n * const doc = await pdfToMarkdownDoc(data);\n * ```\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { markdownToDoc } from '@bendyline/squisq/doc';\nimport { stringifyMarkdown } from '@bendyline/squisq/markdown';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownHeading,\n MarkdownParagraph,\n MarkdownBlockquote,\n MarkdownList,\n MarkdownListItem,\n MarkdownCodeBlock,\n MarkdownTable,\n MarkdownTableRow,\n MarkdownTableCell,\n MarkdownText,\n MarkdownEmphasis,\n MarkdownStrong,\n MarkdownInlineCode,\n MarkdownLink,\n MarkdownImage,\n} from '@bendyline/squisq/markdown';\n\nimport { MemoryContentContainer } from '@bendyline/squisq/storage';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\n\nimport {\n DEFAULT_FONT_SIZE,\n IMPORT_HEADING_MIN_SIZE,\n IMPORT_HEADING_SIZE_RANGES,\n IMPORT_PARAGRAPH_GAP,\n IMPORT_BULLET_CHARS,\n IMPORT_ORDERED_PREFIX,\n IMPORT_COLUMN_TOLERANCE,\n IMPORT_TABLE_MIN_ROWS,\n IMPORT_URL_PATTERN,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Options for PDF import.\n */\nexport interface PdfImportOptions {\n /**\n * Hint for the body font size used in the PDF (in points).\n * Text items larger than this are considered headings.\n * If not provided, the importer detects the most common font size.\n */\n bodyFontSize?: number;\n\n /** Whether to detect tables from column-aligned text. Default: true. */\n detectTables?: boolean;\n\n /** Whether to detect code blocks from monospace fonts. Default: true. */\n detectCodeBlocks?: boolean;\n\n /** Whether to detect blockquotes from indentation. Default: true. */\n detectBlockquotes?: boolean;\n\n /** Whether to detect URLs in text and convert to links. Default: true. */\n detectLinks?: boolean;\n}\n\n/**\n * Convert a PDF file to a MarkdownDocument.\n *\n * Structure detection is heuristic — results are best-effort.\n *\n * @param data - The raw PDF file as ArrayBuffer, Uint8Array, or Blob\n * @param options - Import options\n * @returns A MarkdownDocument representing the detected content\n */\nexport async function pdfToMarkdownDoc(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<MarkdownDocument> {\n const bytes =\n data instanceof Blob\n ? new Uint8Array(await data.arrayBuffer())\n : data instanceof ArrayBuffer\n ? new Uint8Array(data)\n : data;\n\n const textLines = await extractTextLines(bytes);\n\n if (textLines.length === 0) {\n return { type: 'document', children: [] };\n }\n\n const bodySize = options.bodyFontSize ?? detectBodyFontSize(textLines);\n const blocks = classifyLines(textLines, bodySize, options);\n\n return { type: 'document', children: blocks };\n}\n\n/**\n * Convert a PDF file to a squisq Doc.\n *\n * Convenience wrapper: PDF → MarkdownDocument → Doc.\n */\nexport async function pdfToDoc(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<Doc> {\n const markdownDoc = await pdfToMarkdownDoc(data, options);\n return markdownToDoc(markdownDoc);\n}\n\n/**\n * Convert a PDF file to a ContentContainer with markdown + extracted images.\n *\n * The container will contain:\n * - The primary markdown document (index.md)\n * - Any embedded images under images/ (e.g., images/image1.png)\n *\n * Image extraction uses pdfjs-dist's operator list API and requires a browser\n * environment (canvas is used to encode pixel data to PNG).\n *\n * @param data - The raw PDF file as ArrayBuffer, Uint8Array, or Blob\n * @param options - Import options\n * @returns A ContentContainer with the document and its media\n */\nexport async function pdfToContainer(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<ContentContainer> {\n const bytes =\n data instanceof Blob\n ? new Uint8Array(await data.arrayBuffer())\n : data instanceof ArrayBuffer\n ? new Uint8Array(data)\n : data;\n\n const textLines = await extractTextLines(bytes);\n const images = await extractImages(bytes);\n\n const bodySize = options.bodyFontSize ?? detectBodyFontSize(textLines);\n\n // If we have images, insert image references into the block list\n let blocks = classifyLines(textLines, bodySize, options);\n if (images.length > 0) {\n blocks = insertImageBlocks(blocks, images);\n }\n\n const markdownDoc: MarkdownDocument = { type: 'document', children: blocks };\n\n const markdown = stringifyMarkdown(markdownDoc);\n\n const container = new MemoryContentContainer();\n await container.writeDocument(markdown);\n\n for (const img of images) {\n await container.writeFile(img.path, new Uint8Array(img.data), 'image/png');\n }\n\n return container;\n}\n\n/** Extracted image with position info for placement. */\ninterface ExtractedImage {\n path: string;\n data: ArrayBuffer;\n page: number;\n y: number;\n}\n\n/**\n * Extract embedded images from a PDF using pdfjs-dist operator list API.\n * Requires browser canvas for PNG encoding.\n */\nasync function extractImages(data: Uint8Array): Promise<ExtractedImage[]> {\n // Canvas is required for PNG encoding — skip in non-browser environments\n if (typeof document === 'undefined') return [];\n\n let pdfjsLib: PdfjsLib & { OPS?: Record<string, number> };\n try {\n pdfjsLib = (await import('pdfjs-dist/legacy/build/pdf.mjs')) as unknown as PdfjsLib & {\n OPS?: Record<string, number>;\n };\n } catch {\n pdfjsLib = (await import('pdfjs-dist')) as unknown as PdfjsLib & {\n OPS?: Record<string, number>;\n };\n }\n\n await applyWorkerConfig(pdfjsLib);\n\n const OPS_paintImageXObject = pdfjsLib.OPS?.paintImageXObject ?? 85;\n\n const loadingTask = pdfjsLib.getDocument({\n data,\n isEvalSupported: false,\n useSystemFonts: true,\n });\n\n const pdf = await loadingTask.promise;\n const images: ExtractedImage[] = [];\n let counter = 0;\n\n for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {\n const page = (await pdf.getPage(pageNum)) as PdfjsPageFull;\n if (!page.getOperatorList) continue;\n\n const opList = await page.getOperatorList();\n const seen = new Set<string>();\n\n for (let i = 0; i < opList.fnArray.length; i++) {\n if (opList.fnArray[i] !== OPS_paintImageXObject) continue;\n\n const imgName = opList.argsArray[i]?.[0];\n if (!imgName || typeof imgName !== 'string' || seen.has(imgName)) continue;\n seen.add(imgName);\n\n try {\n const imgData = page.objs?.get(imgName) as PdfjsImageData | null;\n if (!imgData?.data || !imgData.width || !imgData.height) continue;\n\n const pngData = imageDataToPng(imgData);\n if (!pngData) continue;\n\n counter++;\n images.push({\n path: `images/image${counter}.png`,\n data: pngData,\n page: pageNum - 1,\n y: 0,\n });\n } catch {\n // Skip images that fail to extract\n }\n }\n }\n\n return images;\n}\n\n/** Minimal pdfjs image data shape. */\ninterface PdfjsImageData {\n width: number;\n height: number;\n data: Uint8ClampedArray;\n kind?: number;\n}\n\n/** Extended PdfjsPage with operator list and objs access. */\ninterface PdfjsPageFull extends PdfjsPage {\n getOperatorList(): Promise<{ fnArray: number[]; argsArray: unknown[][] }>;\n objs?: { get(name: string): unknown; has?(name: string): boolean };\n}\n\n/** Encode pdfjs image data to PNG using a canvas element. */\nfunction imageDataToPng(img: PdfjsImageData): ArrayBuffer | null {\n try {\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n\n // pdfjs kind=1 is GRAYSCALE, kind=2 is RGB, kind=3 is RGBA\n let imageData: ImageData;\n if (img.kind === 3 || img.data.length === img.width * img.height * 4) {\n // RGBA — use directly\n imageData = new ImageData(new Uint8ClampedArray(img.data), img.width, img.height);\n } else if (img.kind === 2 || img.data.length === img.width * img.height * 3) {\n // RGB — expand to RGBA\n const rgba = new Uint8ClampedArray(img.width * img.height * 4);\n for (let j = 0, k = 0; j < img.data.length; j += 3, k += 4) {\n rgba[k] = img.data[j];\n rgba[k + 1] = img.data[j + 1];\n rgba[k + 2] = img.data[j + 2];\n rgba[k + 3] = 255;\n }\n imageData = new ImageData(rgba, img.width, img.height);\n } else if (img.kind === 1 || img.data.length === img.width * img.height) {\n // Grayscale — expand to RGBA\n const rgba = new Uint8ClampedArray(img.width * img.height * 4);\n for (let j = 0, k = 0; j < img.data.length; j++, k += 4) {\n rgba[k] = img.data[j];\n rgba[k + 1] = img.data[j];\n rgba[k + 2] = img.data[j];\n rgba[k + 3] = 255;\n }\n imageData = new ImageData(rgba, img.width, img.height);\n } else {\n return null;\n }\n\n ctx.putImageData(imageData, 0, 0);\n\n // Convert canvas to PNG ArrayBuffer\n const dataUrl = canvas.toDataURL('image/png');\n const base64 = dataUrl.split(',')[1];\n const binaryStr = atob(base64);\n const bytes = new Uint8Array(binaryStr.length);\n for (let i = 0; i < binaryStr.length; i++) {\n bytes[i] = binaryStr.charCodeAt(i);\n }\n return bytes.buffer;\n } catch {\n return null;\n }\n}\n\n/**\n * Insert image reference blocks between text blocks.\n * Places each image after the last text block on the same page (or at the end).\n */\nfunction insertImageBlocks(\n blocks: MarkdownBlockNode[],\n images: ExtractedImage[],\n): MarkdownBlockNode[] {\n if (images.length === 0) return blocks;\n\n // Simple strategy: append all images at the end as paragraphs\n const result = [...blocks];\n for (const img of images) {\n const imgNode: MarkdownImage = {\n type: 'image',\n url: img.path,\n alt: `Image ${img.path.replace('images/image', '').replace('.png', '')}`,\n };\n const para: MarkdownParagraph = {\n type: 'paragraph',\n children: [imgNode],\n };\n result.push(para);\n }\n return result;\n}\n\n// ============================================\n// Internal Types\n// ============================================\n\n/** A single text item extracted from pdfjs. */\ninterface TextItem {\n str: string;\n x: number;\n y: number;\n width: number;\n height: number;\n /** Internal font ID from pdfjs (e.g. \"g_d0_f1\") */\n fontName: string;\n /** Resolved font family from pdfjs styles (e.g. \"sans-serif\", \"monospace\") */\n fontFamily: string;\n}\n\n/** A logical line: text items at roughly the same y-coordinate. */\ninterface TextLine {\n items: TextItem[];\n y: number;\n /** The page this line is on (0-based). */\n page: number;\n /** The predominant font size on this line. */\n fontSize: number;\n /** The predominant font family on this line. */\n fontFamily: string;\n /** The predominant font ID on this line (may contain bold/italic hints for embedded fonts). */\n fontName: string;\n /** The minimum x position (left edge). */\n minX: number;\n /** Full concatenated text. */\n text: string;\n}\n\n// ============================================\n// PDF Text Extraction (pdfjs-dist)\n// ============================================\n\n/**\n * Configure the pdfjs-dist PDF worker source URL.\n *\n * pdfjs-dist requires a worker for PDF parsing. In the **browser**, bundlers\n * (Vite, webpack) typically handle this automatically, or you can point to a\n * CDN-hosted worker script. In **Node.js / SSR / test** environments, call\n * this with a `file://` URL to the worker module **before** any import call.\n *\n * @example\n * ```ts\n * // Browser — CDN\n * configurePdfWorker('https://cdn.jsdelivr.net/npm/pdfjs-dist@4/legacy/build/pdf.worker.min.mjs');\n *\n * // Node / vitest — file URL\n * import { pathToFileURL } from 'url';\n * configurePdfWorker(pathToFileURL(require.resolve('pdfjs-dist/legacy/build/pdf.worker.mjs')).href);\n * ```\n */\nexport function configurePdfWorker(workerSrc: string): void {\n _workerSrc = workerSrc;\n}\n\n/** Module-level storage for the worker source URL. */\nlet _workerSrc: string | undefined;\n\n/** Minimal typed surface of the pdfjs-dist library used by the import path. */\ninterface PdfjsLib {\n GlobalWorkerOptions?: { workerSrc?: string };\n getDocument(params: { data: Uint8Array; isEvalSupported?: boolean; useSystemFonts?: boolean }): {\n promise: Promise<PdfjsDocument>;\n };\n}\n\ninterface PdfjsDocument {\n numPages: number;\n getPage(pageNum: number): Promise<PdfjsPage>;\n}\n\ninterface PdfjsPage {\n getTextContent(): Promise<{\n items: Array<{\n str: string;\n transform: number[];\n height: number;\n width?: number;\n fontName?: string;\n }>;\n styles?: Record<string, { fontFamily?: string }>;\n }>;\n}\n\nasync function applyWorkerConfig(pdfjsLib: PdfjsLib): Promise<void> {\n if (!pdfjsLib.GlobalWorkerOptions) return;\n if (pdfjsLib.GlobalWorkerOptions.workerSrc) return;\n\n if (_workerSrc) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = _workerSrc;\n }\n // If no workerSrc is set, pdfjs-dist's legacy build will attempt its\n // built-in fake-worker fallback. In browsers this usually works; in\n // Node.js the caller must have called configurePdfWorker() first.\n}\n\nasync function extractTextLines(data: Uint8Array): Promise<TextLine[]> {\n // Dynamic import — the legacy build bundles a fake-worker fallback\n // that avoids a real Web Worker in environments that don't support it.\n let pdfjsLib: PdfjsLib;\n try {\n pdfjsLib = (await import('pdfjs-dist/legacy/build/pdf.mjs')) as unknown as PdfjsLib;\n } catch {\n pdfjsLib = (await import('pdfjs-dist')) as unknown as PdfjsLib;\n }\n\n await applyWorkerConfig(pdfjsLib);\n\n const loadingTask = pdfjsLib.getDocument({\n data,\n isEvalSupported: false,\n useSystemFonts: true,\n });\n\n const pdf = await loadingTask.promise;\n const allLines: TextLine[] = [];\n\n for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {\n const page = await pdf.getPage(pageNum);\n const content = await page.getTextContent();\n\n // Build a fontName → fontFamily lookup from pdfjs styles\n const styleMap = content.styles || {};\n\n // Group text items into lines by y-coordinate\n const items: TextItem[] = [];\n for (const item of content.items) {\n if (!item.str || item.str.trim().length === 0) continue;\n const transform = item.transform || [1, 0, 0, 1, 0, 0];\n const x = transform[4];\n const y = transform[5];\n const height = Math.abs(transform[3]) || item.height || 12;\n const width = item.width || 0;\n const fontName = item.fontName || '';\n const fontFamily = styleMap[fontName]?.fontFamily || '';\n items.push({ str: item.str, x, y, width, height, fontName, fontFamily });\n }\n\n // Group into lines (items within 2pt of same y are same line)\n const lineMap = new Map<number, TextItem[]>();\n for (const item of items) {\n const roundedY = Math.round(item.y * 2) / 2;\n let foundKey: number | undefined;\n for (const key of lineMap.keys()) {\n if (Math.abs(key - roundedY) < 2) {\n foundKey = key;\n break;\n }\n }\n if (foundKey !== undefined) {\n lineMap.get(foundKey)!.push(item);\n } else {\n lineMap.set(roundedY, [item]);\n }\n }\n\n // Sort lines top-to-bottom (highest y first), items left-to-right\n const sortedKeys = [...lineMap.keys()].sort((a, b) => b - a);\n for (const key of sortedKeys) {\n const lineItems = lineMap.get(key)!.sort((a, b) => a.x - b.x);\n\n const fontSizes = lineItems.map((i) => i.height);\n const fontSize = mode(fontSizes) || 12;\n const fontFamilies = lineItems.map((i) => i.fontFamily);\n const fontFamily = modeStr(fontFamilies) || '';\n const fontNames = lineItems.map((i) => i.fontName);\n const fontName = modeStr(fontNames) || '';\n const minX = Math.min(...lineItems.map((i) => i.x));\n const text = lineItems.map((i) => i.str).join(' ');\n\n allLines.push({\n items: lineItems,\n y: key,\n page: pageNum - 1,\n fontSize,\n fontFamily,\n fontName,\n minX,\n text,\n });\n }\n }\n\n return allLines;\n}\n\n// ============================================\n// Font Size Detection\n// ============================================\n\nfunction detectBodyFontSize(lines: TextLine[]): number {\n const sizes = lines.map((l) => Math.round(l.fontSize * 2) / 2);\n return mode(sizes) || DEFAULT_FONT_SIZE;\n}\n\nfunction mode(arr: number[]): number {\n const freq = new Map<number, number>();\n for (const v of arr) freq.set(v, (freq.get(v) || 0) + 1);\n let maxCount = 0;\n let maxVal = 0;\n for (const [v, c] of freq) {\n if (c > maxCount) {\n maxCount = c;\n maxVal = v;\n }\n }\n return maxVal;\n}\n\nfunction modeStr(arr: string[]): string {\n const freq = new Map<string, number>();\n for (const v of arr) freq.set(v, (freq.get(v) || 0) + 1);\n let maxCount = 0;\n let maxVal = '';\n for (const [v, c] of freq) {\n if (c > maxCount) {\n maxCount = c;\n maxVal = v;\n }\n }\n return maxVal;\n}\n\n// ============================================\n// Line Classification → MarkdownBlockNode[]\n// ============================================\n\nfunction classifyLines(\n lines: TextLine[],\n bodySize: number,\n options: PdfImportOptions,\n): MarkdownBlockNode[] {\n const blocks: MarkdownBlockNode[] = [];\n const detectTables = options.detectTables !== false;\n const detectCodeBlocks = options.detectCodeBlocks !== false;\n const detectBlockquotes = options.detectBlockquotes !== false;\n const _detectLinks = options.detectLinks !== false;\n\n // Determine typical left margin (most common minX)\n const leftMargins = lines.map((l) => Math.round(l.minX));\n const typicalLeftMargin = mode(leftMargins) || 72;\n\n let i = 0;\n while (i < lines.length) {\n const line = lines[i];\n\n // --- Heading detection ---\n if (line.fontSize >= IMPORT_HEADING_MIN_SIZE && line.fontSize > bodySize + 1) {\n const depth = sizeToHeadingDepth(line.fontSize);\n blocks.push({\n type: 'heading',\n depth,\n children: buildInlineNodes(line, options),\n } as MarkdownHeading);\n i++;\n continue;\n }\n\n // --- Code block detection (monospace font runs) ---\n if (detectCodeBlocks && isMonospaceLine(line)) {\n const codeLines: string[] = [];\n while (i < lines.length && isMonospaceLine(lines[i])) {\n codeLines.push(lines[i].text);\n i++;\n }\n blocks.push({\n type: 'code',\n value: codeLines.join('\\n'),\n } as MarkdownCodeBlock);\n continue;\n }\n\n // --- Table detection (column-aligned consecutive lines) ---\n if (detectTables && i + 1 < lines.length) {\n const tableLines = tryDetectTable(lines, i, typicalLeftMargin);\n if (tableLines > 0) {\n const table = buildTable(lines.slice(i, i + tableLines), options);\n if (table) {\n blocks.push(table);\n i += tableLines;\n continue;\n }\n }\n }\n\n // --- List detection ---\n const bulletMatch = tryMatchBullet(line.text);\n const orderedMatch = line.text.match(IMPORT_ORDERED_PREFIX);\n if (bulletMatch || orderedMatch) {\n const listResult = consumeList(lines, i, typicalLeftMargin, bodySize, options);\n blocks.push(listResult.list);\n i = listResult.nextIndex;\n continue;\n }\n\n // --- Blockquote detection (indented text) ---\n if (detectBlockquotes && line.minX > typicalLeftMargin + 20) {\n const quoteLines: TextLine[] = [];\n while (\n i < lines.length &&\n lines[i].minX > typicalLeftMargin + 20 &&\n !isMonospaceLine(lines[i]) &&\n lines[i].fontSize <= bodySize + 1\n ) {\n quoteLines.push(lines[i]);\n i++;\n }\n const quoteBlocks: MarkdownBlockNode[] = quoteLines.map(\n (ql) =>\n ({\n type: 'paragraph',\n children: buildInlineNodes(ql, options),\n }) as MarkdownParagraph,\n );\n blocks.push({\n type: 'blockquote',\n children: quoteBlocks,\n } as MarkdownBlockquote);\n continue;\n }\n\n // --- Regular paragraph ---\n // Merge consecutive body-sized lines on the same page with small y-gaps\n const paraLines: TextLine[] = [line];\n i++;\n while (i < lines.length) {\n const next = lines[i];\n // Same page, same-ish font size, close y (within line-height gap), not bullet/heading\n if (\n next.page === line.page &&\n Math.abs(next.fontSize - bodySize) <= 1 &&\n !isMonospaceLine(next) &&\n next.minX <= typicalLeftMargin + 15 &&\n !tryMatchBullet(next.text) &&\n !next.text.match(IMPORT_ORDERED_PREFIX)\n ) {\n // Check y-gap: lines are sorted top-to-bottom so y decreases\n const yGap = paraLines[paraLines.length - 1].y - next.y;\n const lineHeight = bodySize * 1.6;\n if (yGap > 0 && yGap < lineHeight + IMPORT_PARAGRAPH_GAP) {\n paraLines.push(next);\n i++;\n } else {\n break;\n }\n } else {\n break;\n }\n }\n\n // Build paragraph from merged lines\n const allInlines: MarkdownInlineNode[] = [];\n for (let j = 0; j < paraLines.length; j++) {\n if (j > 0) {\n allInlines.push({ type: 'text', value: ' ' } as MarkdownText);\n }\n allInlines.push(...buildInlineNodes(paraLines[j], options));\n }\n\n if (allInlines.length > 0) {\n blocks.push({\n type: 'paragraph',\n children: mergeAdjacentText(allInlines),\n } as MarkdownParagraph);\n }\n }\n\n return blocks;\n}\n\n// ============================================\n// Heading Depth Mapping\n// ============================================\n\nfunction sizeToHeadingDepth(fontSize: number): 1 | 2 | 3 | 4 | 5 | 6 {\n for (const range of IMPORT_HEADING_SIZE_RANGES) {\n if (fontSize >= range.min) return range.depth as 1 | 2 | 3 | 4 | 5 | 6;\n }\n return 6;\n}\n\n// ============================================\n// Font Heuristics\n// ============================================\n\n/**\n * Check if a line is predominantly monospace.\n * Uses the resolved fontFamily from pdfjs styles first,\n * falls back to fontName pattern matching for embedded fonts.\n */\nfunction isMonospaceLine(line: TextLine): boolean {\n return isMonospaceFamily(line.fontFamily) || isMonospaceName(line.fontName);\n}\n\n/**\n * Check if a text item is monospace.\n */\nfunction isMonospaceItem(item: TextItem): boolean {\n return isMonospaceFamily(item.fontFamily) || isMonospaceName(item.fontName);\n}\n\nfunction isMonospaceFamily(fontFamily: string): boolean {\n const lower = fontFamily.toLowerCase();\n return lower === 'monospace' || lower.includes('monospace');\n}\n\nfunction isMonospaceName(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return (\n lower.includes('courier') ||\n lower.includes('mono') ||\n lower.includes('consolas') ||\n lower.includes('menlo') ||\n lower.includes('inconsolata') ||\n lower.includes('firacode') ||\n lower.includes('source code') ||\n lower.includes('dejavu sans mono')\n );\n}\n\nfunction isBoldFont(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return lower.includes('bold') || lower.includes('black') || lower.includes('heavy');\n}\n\nfunction isItalicFont(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return lower.includes('italic') || lower.includes('oblique') || lower.includes('slanted');\n}\n\n// ============================================\n// Inline Node Construction\n// ============================================\n\nfunction buildInlineNodes(line: TextLine, options: PdfImportOptions): MarkdownInlineNode[] {\n const nodes: MarkdownInlineNode[] = [];\n const detectLinksOpt = options.detectLinks !== false;\n\n for (const item of line.items) {\n const text = item.str;\n if (!text || text.trim().length === 0) continue;\n\n const bold = isBoldFont(item.fontName);\n const italic = isItalicFont(item.fontName);\n const mono = isMonospaceItem(item);\n\n let inlineNodes: MarkdownInlineNode[];\n\n if (mono) {\n inlineNodes = [{ type: 'inlineCode', value: text } as MarkdownInlineCode];\n } else if (detectLinksOpt) {\n inlineNodes = splitTextWithLinks(text);\n } else {\n inlineNodes = [{ type: 'text', value: text } as MarkdownText];\n }\n\n // Wrap in formatting\n for (const node of inlineNodes) {\n let wrapped: MarkdownInlineNode = node;\n if (italic) {\n wrapped = { type: 'emphasis', children: [wrapped] } as MarkdownEmphasis;\n }\n if (bold) {\n wrapped = { type: 'strong', children: [wrapped] } as MarkdownStrong;\n }\n nodes.push(wrapped);\n }\n }\n\n return nodes;\n}\n\n/**\n * Split a text string into text nodes and link nodes wherever\n * URL patterns are found.\n */\nfunction splitTextWithLinks(text: string): MarkdownInlineNode[] {\n const nodes: MarkdownInlineNode[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n IMPORT_URL_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = IMPORT_URL_PATTERN.exec(text)) !== null) {\n // Text before URL\n if (match.index > lastIndex) {\n nodes.push({ type: 'text', value: text.slice(lastIndex, match.index) } as MarkdownText);\n }\n // URL as link\n const url = match[0];\n nodes.push({\n type: 'link',\n url,\n children: [{ type: 'text', value: url } as MarkdownText],\n } as MarkdownLink);\n lastIndex = match.index + url.length;\n }\n\n // Remaining text\n if (lastIndex < text.length) {\n nodes.push({ type: 'text', value: text.slice(lastIndex) } as MarkdownText);\n }\n\n return nodes.length > 0 ? nodes : [{ type: 'text', value: text } as MarkdownText];\n}\n\n// ============================================\n// List Detection\n// ============================================\n\nfunction tryMatchBullet(text: string): boolean {\n if (text.length === 0) return false;\n return IMPORT_BULLET_CHARS.has(text[0]) || IMPORT_BULLET_CHARS.has(text.trimStart()[0]);\n}\n\nfunction stripBullet(text: string): string {\n const trimmed = text.trimStart();\n if (IMPORT_BULLET_CHARS.has(trimmed[0])) {\n return trimmed.slice(1).trimStart();\n }\n return text;\n}\n\nfunction stripOrderedPrefix(text: string): string {\n return text.replace(IMPORT_ORDERED_PREFIX, '');\n}\n\ninterface ListResult {\n list: MarkdownList;\n nextIndex: number;\n}\n\nfunction consumeList(\n lines: TextLine[],\n startIdx: number,\n _typicalLeftMargin: number,\n _bodySize: number,\n _options: PdfImportOptions,\n): ListResult {\n const firstLine = lines[startIdx];\n const isOrdered = !!firstLine.text.match(IMPORT_ORDERED_PREFIX);\n const items: MarkdownListItem[] = [];\n let i = startIdx;\n\n while (i < lines.length) {\n const line = lines[i];\n const isBullet = tryMatchBullet(line.text);\n const isOrd = !!line.text.match(IMPORT_ORDERED_PREFIX);\n\n if (!isBullet && !isOrd) break;\n // All items in one list should be same type\n if (isOrdered && !isOrd) break;\n if (!isOrdered && !isBullet) break;\n\n const cleanText = isOrdered ? stripOrderedPrefix(line.text) : stripBullet(line.text);\n const para: MarkdownParagraph = {\n type: 'paragraph',\n children: splitTextWithLinks(cleanText),\n };\n items.push({\n type: 'listItem',\n children: [para],\n } as MarkdownListItem);\n i++;\n }\n\n return {\n list: {\n type: 'list',\n ordered: isOrdered,\n children: items,\n } as MarkdownList,\n nextIndex: i,\n };\n}\n\n// ============================================\n// Table Detection\n// ============================================\n\n/**\n * Look ahead from index `start` and return the number of consecutive\n * lines that form an aligned table, or 0 if no table detected.\n */\nfunction tryDetectTable(lines: TextLine[], start: number, _typicalLeftMargin: number): number {\n // A table needs multiple items per line (columns) on consecutive lines\n // with roughly the same x-alignment pattern.\n\n const firstLine = lines[start];\n if (firstLine.items.length < 2) return 0;\n\n const cols = getColumnPositions(firstLine);\n if (cols.length < 2) return 0;\n\n let count = 1;\n for (let i = start + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.items.length < 2) break;\n\n // Check if this line's columns align with the first line's\n const lineCols = getColumnPositions(line);\n if (lineCols.length !== cols.length) break;\n\n let aligned = true;\n for (let c = 0; c < cols.length; c++) {\n if (Math.abs(lineCols[c] - cols[c]) > IMPORT_COLUMN_TOLERANCE) {\n aligned = false;\n break;\n }\n }\n if (!aligned) break;\n count++;\n }\n\n return count >= IMPORT_TABLE_MIN_ROWS ? count : 0;\n}\n\nfunction getColumnPositions(line: TextLine): number[] {\n // Cluster item x-positions\n const positions: number[] = [];\n for (const item of line.items) {\n const x = Math.round(item.x);\n // Check if this x is close to an existing column\n let found = false;\n for (const p of positions) {\n if (Math.abs(p - x) < IMPORT_COLUMN_TOLERANCE) {\n found = true;\n break;\n }\n }\n if (!found) positions.push(x);\n }\n return positions.sort((a, b) => a - b);\n}\n\nfunction buildTable(lines: TextLine[], _options: PdfImportOptions): MarkdownTable | null {\n if (lines.length === 0) return null;\n\n // Use the first line's column positions as anchors\n const cols = getColumnPositions(lines[0]);\n if (cols.length < 2) return null;\n\n const rows: MarkdownTableRow[] = [];\n\n for (let ri = 0; ri < lines.length; ri++) {\n const line = lines[ri];\n const cells: MarkdownTableCell[] = [];\n\n for (let ci = 0; ci < cols.length; ci++) {\n const colLeft = cols[ci] - IMPORT_COLUMN_TOLERANCE;\n const colRight = ci + 1 < cols.length ? cols[ci + 1] - IMPORT_COLUMN_TOLERANCE : Infinity;\n\n // Collect items in this column\n const cellItems = line.items.filter((item) => item.x >= colLeft && item.x < colRight);\n const text = cellItems\n .map((i) => i.str)\n .join(' ')\n .trim();\n\n cells.push({\n type: 'tableCell',\n isHeader: ri === 0,\n children: text.length > 0 ? [{ type: 'text', value: text } as MarkdownText] : [],\n } as MarkdownTableCell);\n }\n\n rows.push({\n type: 'tableRow',\n children: cells,\n } as MarkdownTableRow);\n }\n\n return {\n type: 'table',\n children: rows,\n } as MarkdownTable;\n}\n\n// ============================================\n// Text Merging\n// ============================================\n\n/**\n * Merge adjacent text nodes to reduce fragmentation.\n */\nfunction mergeAdjacentText(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {\n if (nodes.length <= 1) return nodes;\n\n const result: MarkdownInlineNode[] = [];\n for (const node of nodes) {\n const prev = result[result.length - 1];\n if (prev && prev.type === 'text' && node.type === 'text') {\n (prev as MarkdownText).value += (node as MarkdownText).value;\n } else {\n result.push(node);\n }\n }\n return result;\n}\n"],"mappings":"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","names":[]}
@@ -1,9 +1,17 @@
1
1
  import {
2
- getPartBinary,
3
- getPartRelationships,
4
- getPartXml,
5
- openPackage
6
- } from "./chunk-YN5HFCEW.js";
2
+ inlineNodeToRuns,
3
+ inlineNodesToRuns
4
+ } from "./chunk-6M7Z25LA.js";
5
+ import {
6
+ stripHtmlTags
7
+ } from "./chunk-PN52A5AA.js";
8
+ import {
9
+ createPackage
10
+ } from "./chunk-QSE7EWE7.js";
11
+ import {
12
+ escapeXml,
13
+ xmlDeclaration
14
+ } from "./chunk-U4MRIFKL.js";
7
15
  import {
8
16
  CONTENT_TYPE_DOCX_DOCUMENT,
9
17
  CONTENT_TYPE_DOCX_FONT_TABLE,
@@ -22,16 +30,15 @@ import {
22
30
  REL_OFFICE_DOCUMENT,
23
31
  REL_SETTINGS,
24
32
  REL_STYLES,
25
- createPackage
26
- } from "./chunk-7DEUGIOO.js";
27
- import {
28
- escapeXml,
29
- xmlDeclaration
30
- } from "./chunk-U4MRIFKL.js";
33
+ getPartBinary,
34
+ getPartRelationships,
35
+ getPartXml,
36
+ openPackage
37
+ } from "./chunk-FMOIGA5T.js";
31
38
 
32
39
  // src/docx/export.ts
33
- import { resolveTheme, resolveFontFamily } from "@bendyline/squisq/schemas";
34
- import { docToMarkdown } from "@bendyline/squisq/doc";
40
+ import { resolveFontFamily } from "@bendyline/squisq/schemas";
41
+ import { docToMarkdown, resolveThemeForDoc } from "@bendyline/squisq/doc";
35
42
  import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
36
43
 
37
44
  // src/docx/styles.ts
@@ -91,7 +98,7 @@ function pointsToTwips(points) {
91
98
  // src/docx/export.ts
92
99
  async function markdownDocToDocx(doc, options = {}) {
93
100
  const resolvedOptions = options.themeId !== void 0 ? options : { ...options, themeId: readFrontmatterThemeId(doc.frontmatter) };
94
- const ctx = new ExportContext(resolvedOptions);
101
+ const ctx = new ExportContext(resolvedOptions, doc);
95
102
  const bodyXml = convertBlocks(doc.children, ctx);
96
103
  return buildDocxPackage(bodyXml, ctx, resolvedOptions);
97
104
  }
@@ -100,7 +107,7 @@ async function docToDocx(doc, options = {}) {
100
107
  return markdownDocToDocx(markdownDoc, options);
101
108
  }
102
109
  var ExportContext = class {
103
- constructor(options) {
110
+ constructor(options, doc) {
104
111
  this.nextRelId = 1;
105
112
  this.nextNumId = 1;
106
113
  this.nextFootnoteId = 1;
@@ -127,7 +134,7 @@ var ExportContext = class {
127
134
  let themeMutedColor;
128
135
  let themeBackgroundColor;
129
136
  if (options.themeId) {
130
- const theme = resolveTheme(options.themeId);
137
+ const theme = resolveThemeForDoc(doc, options.themeId);
131
138
  themeFont = firstFontFromStack(resolveFontFamily(theme.typography?.bodyFont, ""));
132
139
  themeTitleFont = firstFontFromStack(resolveFontFamily(theme.typography?.titleFont, ""));
133
140
  const stripHash = (c) => c.startsWith("#") ? c.slice(1) : c;
@@ -359,40 +366,20 @@ function convertFootnoteDefinition(node, ctx) {
359
366
  ctx.footnotes.set(fnId, footnoteXml);
360
367
  return "";
361
368
  }
369
+ function docxRunHandlers(ctx) {
370
+ return {
371
+ run: (text, format) => makeRun(text, format),
372
+ link: (node, format) => convertLink(node, ctx, format),
373
+ image: (node) => convertImage(node, ctx),
374
+ lineBreak: () => `<w:r><w:br/></w:r>`,
375
+ footnoteRef: (node) => convertFootnoteRef(node, ctx)
376
+ };
377
+ }
362
378
  function convertInlines(nodes, ctx, format = {}) {
363
- const parts = [];
364
- for (const node of nodes) {
365
- parts.push(convertInline(node, ctx, format));
366
- }
367
- return parts.join("");
379
+ return inlineNodesToRuns(nodes, docxRunHandlers(ctx), format);
368
380
  }
369
381
  function convertInline(node, ctx, format) {
370
- switch (node.type) {
371
- case "text":
372
- return makeRun(node.value, format);
373
- case "strong":
374
- return convertInlines(node.children, ctx, { ...format, bold: true });
375
- case "emphasis":
376
- return convertInlines(node.children, ctx, { ...format, italic: true });
377
- case "delete":
378
- return convertInlines(node.children, ctx, { ...format, strike: true });
379
- case "inlineCode":
380
- return makeRun(node.value, { ...format, code: true });
381
- case "link":
382
- return convertLink(node, ctx, format);
383
- case "image":
384
- return convertImage(node, ctx);
385
- case "break":
386
- return `<w:r><w:br/></w:r>`;
387
- case "htmlInline":
388
- return makeRun(stripHtmlTags(node.rawHtml), format);
389
- case "inlineMath":
390
- return makeRun(node.value, { ...format, code: true });
391
- case "footnoteReference":
392
- return convertFootnoteRef(node, ctx);
393
- default:
394
- return "";
395
- }
382
+ return inlineNodeToRuns(node, docxRunHandlers(ctx), format);
396
383
  }
397
384
  function makeRun(text, format) {
398
385
  if (!text) return "";
@@ -658,12 +645,10 @@ function buildFootnotesXml(ctx) {
658
645
  }
659
646
  return xmlDeclaration() + `<w:footnotes xmlns:w="${NS_WML}" xmlns:r="${NS_R}">` + footnotes.join("") + `</w:footnotes>`;
660
647
  }
661
- function stripHtmlTags(html) {
662
- return html.replace(/<[^>]*>/g, "");
663
- }
664
648
 
665
649
  // src/docx/import.ts
666
650
  import { markdownToDoc } from "@bendyline/squisq/doc";
651
+ import { stringifyMarkdown } from "@bendyline/squisq/markdown";
667
652
  import { MemoryContentContainer } from "@bendyline/squisq/storage";
668
653
  async function docxToMarkdownDoc(data, options = {}) {
669
654
  const pkg = await openPackage(data);
@@ -700,7 +685,6 @@ async function docxToContainer(data, options = {}) {
700
685
  }
701
686
  const blocks = await convertBody(body, ctx);
702
687
  const markdownDoc = { type: "document", children: blocks };
703
- const { stringifyMarkdown } = await import("@bendyline/squisq/markdown");
704
688
  const markdown = stringifyMarkdown(markdownDoc);
705
689
  const container = new MemoryContentContainer();
706
690
  await container.writeDocument(markdown);
@@ -1247,4 +1231,4 @@ export {
1247
1231
  docxToDoc,
1248
1232
  docxToContainer
1249
1233
  };
1250
- //# sourceMappingURL=chunk-UDS45KUJ.js.map
1234
+ //# sourceMappingURL=chunk-BJQIJ4S4.js.map