@bendyline/squisq-formats 2.1.0 → 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-NNHKUXKA.js → chunk-26ISNJ7Y.js} +85 -65
  5. package/dist/{chunk-NKAJPJ4G.js → chunk-2JJ5RFDZ.js} +0 -1
  6. package/dist/{chunk-WQSHGBLN.js → chunk-3NKXBZSR.js} +193 -42
  7. package/dist/{chunk-KURGXM4I.js → chunk-4V3KCHAP.js} +3 -4
  8. package/dist/{chunk-MLX2BOJC.js → chunk-6RQOV3B3.js} +1 -2
  9. package/dist/{chunk-EW54IRRS.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-SC67HYQJ.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-RLU7UFYU.js → chunk-IPN56VLW.js} +83 -58
  17. package/dist/{chunk-DTDF6QDP.js → chunk-JE6LSIHE.js} +81 -20
  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-74GO3FVS.js → chunk-PU7REGWV.js} +5 -8
  24. package/dist/{chunk-PN52A5AA.js → chunk-SBUW7NHR.js} +0 -1
  25. package/dist/{chunk-QFLDYKCR.js → chunk-TAAENIRB.js} +5 -8
  26. package/dist/{chunk-7ARKUCQT.js → chunk-X2DEAXNK.js} +54 -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 -11
  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-4VUWTSGM.js.map +0 -1
  62. package/dist/chunk-6M7Z25LA.js +0 -46
  63. package/dist/chunk-6M7Z25LA.js.map +0 -1
  64. package/dist/chunk-74GO3FVS.js.map +0 -1
  65. package/dist/chunk-7ARKUCQT.js.map +0 -1
  66. package/dist/chunk-DTDF6QDP.js.map +0 -1
  67. package/dist/chunk-EW54IRRS.js.map +0 -1
  68. package/dist/chunk-FE6OJV6O.js.map +0 -1
  69. package/dist/chunk-GVS2XXV6.js.map +0 -1
  70. package/dist/chunk-KURGXM4I.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-NNHKUXKA.js.map +0 -1
  74. package/dist/chunk-O3GVVND4.js.map +0 -1
  75. package/dist/chunk-ODL3SSPT.js.map +0 -1
  76. package/dist/chunk-PN52A5AA.js.map +0 -1
  77. package/dist/chunk-QFLDYKCR.js.map +0 -1
  78. package/dist/chunk-RFAPOKHJ.js.map +0 -1
  79. package/dist/chunk-RLU7UFYU.js.map +0 -1
  80. package/dist/chunk-SC67HYQJ.js.map +0 -1
  81. package/dist/chunk-SSUPBUF5.js.map +0 -1
  82. package/dist/chunk-U4MRIFKL.js.map +0 -1
  83. package/dist/chunk-UGYF5AZE.js +0 -275
  84. package/dist/chunk-UGYF5AZE.js.map +0 -1
  85. package/dist/chunk-WQSHGBLN.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-OJ7ZQV6P.js +0 -15
  93. package/dist/extract-OJ7ZQV6P.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-5VDIRPIJ.js +0 -12
  100. package/dist/layouts-5VDIRPIJ.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 -491
  111. package/src/__tests__/docxImport.test.ts +0 -531
  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 -439
  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 -1375
  143. package/src/docx/import.ts +0 -1250
  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 -217
  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 +0,0 @@
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, ThemeRegistry } 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 /** Explicit caller-owned registry for non-document custom themes. */\n themeRegistry?: ThemeRegistry;\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, options.themeRegistry);\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 type { ContentContainer } from '@bendyline/squisq/storage';\nimport { buildContainer } from '../shared/container.js';\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 loaded = await loadPdfDocument(bytes);\n try {\n const textLines = await extractTextLines(loaded.pdf);\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 } finally {\n await loaded.pdf.destroy?.();\n }\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 loaded = await loadPdfDocument(bytes);\n let textLines: TextLine[];\n let images: ExtractedImage[];\n try {\n [textLines, images] = await Promise.all([extractTextLines(loaded.pdf), extractImages(loaded)]);\n } finally {\n await loaded.pdf.destroy?.();\n }\n\n // Under Node there is no DOM/canvas, so extractImages() returns [] via its\n // environment guard and embedded images can't be decoded. The registry's\n // `importContainer` wrapper surfaces this (typeof document === 'undefined')\n // as a ConversionResult warning; direct callers of pdfToContainer get no\n // signal, which is an accepted trade-off for the degraded Node path.\n\n const bodySize = options.bodyFontSize ?? detectBodyFontSize(textLines);\n\n // Classify text into blocks, capturing the originating page of each block\n // (parallel array) so images can be placed on their own page rather than\n // dumped at the document end.\n const blockPages: number[] = [];\n let blocks = classifyLines(textLines, bodySize, options, blockPages);\n if (images.length > 0) {\n blocks = insertImageBlocks(blocks, blockPages, images);\n }\n\n const markdownDoc: MarkdownDocument = { type: 'document', children: blocks };\n\n // pdf image extraction only ever produces PNG (canvas re-encode), so every\n // entry gets image/png.\n return buildContainer(\n stringifyMarkdown(markdownDoc),\n images.map((img) => [img.path, { data: img.data, mimeType: 'image/png' }] as const),\n );\n}\n\n/**\n * Extracted image with position info for placement.\n *\n * `page` is captured reliably from the operator-list walk. `y` is currently\n * always 0 — recovering a real y within the page would require tracking the\n * current transformation matrix (CTM) during the paint operator, which is out\n * of scope here (see the placement note in `insertImageBlocks`).\n *\n * Exported for direct unit testing of `insertImageBlocks`.\n */\nexport interface 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(loaded: LoadedPdf): Promise<ExtractedImage[]> {\n // Canvas is required for PNG encoding — skip in non-browser environments\n if (typeof document === 'undefined') return [];\n\n const { pdfjsLib, pdf } = loaded;\n\n const OPS_paintImageXObject = pdfjsLib.OPS?.paintImageXObject ?? 85;\n\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/** Wrap an extracted image as a paragraph containing an image node. */\nfunction imageParagraph(img: ExtractedImage): MarkdownParagraph {\n const imgNode: MarkdownImage = {\n type: 'image',\n url: img.path,\n alt: `Image ${img.path.replace('images/image', '').replace('.png', '')}`,\n };\n return { type: 'paragraph', children: [imgNode] };\n}\n\n/**\n * Insert image reference blocks among the text blocks, page by page.\n *\n * Each image is placed immediately after the LAST content block that\n * originated from the same page (per the parallel `blockPages` array).\n * Images on a page that produced no text blocks (e.g. an image-only page)\n * fall back to the last block of the nearest preceding page that did; if no\n * such page exists, they are appended at the document end.\n *\n * Placement is intentionally page-level only. Ordering *within* a page follows\n * the extraction order of the images (roughly the paint-operator order) — real\n * vertical (y) ordering within a page is future work, since `ExtractedImage.y`\n * is not yet populated (it needs CTM tracking during the paint operator).\n *\n * @param blocks - The classified content blocks, in document order.\n * @param blockPages - Parallel array: `blockPages[i]` is the 0-based page that\n * `blocks[i]` came from. Must be the same length as `blocks`.\n * @param images - Extracted images with a reliable `page` field.\n */\nexport function insertImageBlocks(\n blocks: MarkdownBlockNode[],\n blockPages: number[],\n images: ExtractedImage[],\n): MarkdownBlockNode[] {\n if (images.length === 0) return blocks;\n\n // No text blocks at all → every image simply appends in order.\n if (blocks.length === 0) {\n return images.map(imageParagraph);\n }\n\n // Map each page → index of its LAST block, and remember which pages have\n // blocks (sorted ascending) for the image-only-page fallback lookup.\n const lastBlockIndexByPage = new Map<number, number>();\n for (let i = 0; i < blocks.length; i++) {\n lastBlockIndexByPage.set(blockPages[i], i);\n }\n const pagesWithBlocks = [...lastBlockIndexByPage.keys()].sort((a, b) => a - b);\n\n const lastIndex = blocks.length - 1;\n\n /** Resolve the block index after which an image on `page` should be inserted. */\n const anchorFor = (page: number): number => {\n const direct = lastBlockIndexByPage.get(page);\n if (direct !== undefined) return direct;\n // Image-only page: fall back to the nearest preceding page with blocks.\n let anchor = -1;\n for (const p of pagesWithBlocks) {\n if (p < page) anchor = lastBlockIndexByPage.get(p)!;\n else break;\n }\n // No preceding page with blocks → append at the document end.\n return anchor === -1 ? lastIndex : anchor;\n };\n\n // Group image paragraphs by the block index they should follow, preserving\n // image order within each group.\n const insertAfter = new Map<number, MarkdownParagraph[]>();\n for (const img of images) {\n const anchor = anchorFor(img.page);\n const group = insertAfter.get(anchor);\n if (group) group.push(imageParagraph(img));\n else insertAfter.set(anchor, [imageParagraph(img)]);\n }\n\n const result: MarkdownBlockNode[] = [];\n for (let i = 0; i < blocks.length; i++) {\n result.push(blocks[i]);\n const imgs = insertAfter.get(i);\n if (imgs) result.push(...imgs);\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 destroy?(): Promise<void>;\n}\n\ninterface LoadedPdf {\n pdfjsLib: PdfjsLib & { OPS?: Record<string, number> };\n pdf: PdfjsDocument;\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 loadPdfDocument(data: Uint8Array): Promise<LoadedPdf> {\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 & { 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 loadingTask = pdfjsLib.getDocument({\n data,\n isEvalSupported: false,\n useSystemFonts: true,\n });\n\n return { pdfjsLib, pdf: await loadingTask.promise };\n}\n\nasync function extractTextLines(pdf: PdfjsDocument): Promise<TextLine[]> {\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 /**\n * Optional out-parameter: when provided, receives one entry per produced\n * block giving the 0-based page that block originated from. Kept parallel\n * to the returned block array so image placement can be page-aware without\n * mutating the (strictly-typed) block nodes themselves.\n */\n blockPages?: number[],\n): MarkdownBlockNode[] {\n const blocks: MarkdownBlockNode[] = [];\n /** Push a block and record its originating page in the parallel array. */\n const pushBlock = (block: MarkdownBlockNode, page: number): void => {\n blocks.push(block);\n if (blockPages) blockPages.push(page);\n };\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 pushBlock(\n {\n type: 'heading',\n depth,\n children: buildInlineNodes(line, options),\n } as MarkdownHeading,\n line.page,\n );\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 pushBlock(\n {\n type: 'code',\n value: codeLines.join('\\n'),\n } as MarkdownCodeBlock,\n line.page,\n );\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 pushBlock(table, line.page);\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 pushBlock(listResult.list, line.page);\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 pushBlock(\n {\n type: 'blockquote',\n children: quoteBlocks,\n } as MarkdownBlockquote,\n line.page,\n );\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 pushBlock(\n {\n type: 'paragraph',\n children: mergeAdjacentText(allInlines),\n } as MarkdownParagraph,\n line.page,\n );\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 +0,0 @@
1
- {"version":3,"sources":["../src/ooxml/writer.ts"],"sourcesContent":["/**\n * OOXML Package Writer\n *\n * Builds OOXML archives (.docx, .pptx, .xlsx) from parts.\n * Handles automatic generation of [Content_Types].xml and\n * _rels/*.rels files from the registered parts and relationships.\n */\n\nimport JSZip from 'jszip';\nimport type { CoreProperties, PackagePart, PendingRelationship, Relationship } from './types.js';\nimport {\n NS_CONTENT_TYPES,\n NS_RELATIONSHIPS,\n NS_CORE_PROPERTIES,\n NS_DC,\n NS_DCTERMS,\n NS_XSI,\n CONTENT_TYPE_RELATIONSHIPS,\n CONTENT_TYPE_CORE_PROPERTIES,\n REL_CORE_PROPERTIES,\n} from './namespaces.js';\nimport { xmlDeclaration, escapeXml } from './xmlUtils.js';\nimport { assertSafeZipPath } from '../shared/zipSafety.js';\n\n// ============================================\n// Package Builder\n// ============================================\n\n/**\n * Mutable builder for constructing an OOXML package.\n *\n * Add parts, relationships, and core properties, then call `toBlob()`\n * or `toArrayBuffer()` to produce the final ZIP archive.\n *\n * @example\n * ```ts\n * const builder = createPackage();\n * builder.addPart('word/document.xml', documentXml, CONTENT_TYPE_DOCX_DOCUMENT);\n * builder.addRelationship('', {\n * id: 'rId1',\n * type: REL_OFFICE_DOCUMENT,\n * target: 'word/document.xml',\n * });\n * const blob = await builder.toBlob();\n * ```\n */\nexport interface OoxmlPackageBuilder {\n /**\n * Add an XML or text part to the package.\n *\n * @param path - Path within the archive (e.g., \"word/document.xml\")\n * @param content - XML string content\n * @param contentType - MIME content type for [Content_Types].xml\n */\n addPart(path: string, content: string, contentType: string): void;\n\n /**\n * Add a binary part to the package (e.g., an image).\n *\n * @param path - Path within the archive (e.g., \"word/media/image1.png\")\n * @param data - Binary content\n * @param contentType - MIME content type (e.g., \"image/png\")\n */\n addBinaryPart(path: string, data: ArrayBuffer | Uint8Array, contentType: string): void;\n\n /**\n * Register a relationship.\n *\n * @param sourcePart - The part this relationship belongs to (e.g., \"word/document.xml\").\n * Use \"\" (empty string) for root-level relationships (_rels/.rels).\n * @param rel - The relationship entry\n */\n addRelationship(sourcePart: string, rel: Relationship): void;\n\n /**\n * Set core document properties (docProps/core.xml).\n * Calling this multiple times overwrites previous values.\n */\n setCoreProperties(props: CoreProperties): void;\n\n /**\n * Assemble the final OOXML package as a Blob.\n * Generates [Content_Types].xml and all _rels/*.rels files automatically.\n */\n toBlob(): Promise<Blob>;\n\n /**\n * Assemble the final OOXML package as an ArrayBuffer.\n */\n toArrayBuffer(): Promise<ArrayBuffer>;\n}\n\n/**\n * Create a new OOXML package builder.\n */\nexport function createPackage(): OoxmlPackageBuilder {\n const parts: PackagePart[] = [];\n const relationships: PendingRelationship[] = [];\n let coreProps: CoreProperties | undefined;\n\n return {\n addPart(path, content, contentType) {\n assertSafeZipPath(path);\n parts.push({ path, content, contentType });\n },\n\n addBinaryPart(path, data, contentType) {\n assertSafeZipPath(path);\n parts.push({ path, binaryContent: data, contentType });\n },\n\n addRelationship(sourcePart, rel) {\n if (sourcePart) assertSafeZipPath(sourcePart);\n relationships.push({ sourcePart, relationship: rel });\n },\n\n setCoreProperties(props) {\n coreProps = props;\n },\n\n async toBlob() {\n const zip = assemble(parts, relationships, coreProps);\n return zip.generateAsync({ type: 'blob' });\n },\n\n async toArrayBuffer() {\n const zip = assemble(parts, relationships, coreProps);\n return zip.generateAsync({ type: 'arraybuffer' });\n },\n };\n}\n\n// ============================================\n// Assembly\n// ============================================\n\n/**\n * Assemble a JSZip archive from parts, relationships, and core properties.\n */\nfunction assemble(\n parts: PackagePart[],\n relationships: PendingRelationship[],\n coreProps?: CoreProperties,\n): JSZip {\n const zip = new JSZip();\n // Assembly must be idempotent: callers may request both Blob and\n // ArrayBuffer representations from one builder. Never append generated\n // relationships to the builder-owned array.\n const assembledRelationships = relationships.slice();\n\n // Write content parts\n for (const part of parts) {\n if (part.content !== undefined) {\n zip.file(part.path, part.content);\n } else if (part.binaryContent !== undefined) {\n zip.file(part.path, part.binaryContent);\n }\n }\n\n // Add core properties if set\n if (coreProps) {\n const coreXml = buildCorePropertiesXml(coreProps);\n zip.file('docProps/core.xml', coreXml);\n\n // Add the generated relationship unless the caller already supplied it.\n if (\n !assembledRelationships.some(\n ({ sourcePart, relationship }) =>\n sourcePart === '' && relationship.type === REL_CORE_PROPERTIES,\n )\n ) {\n const ids = new Set(assembledRelationships.map(({ relationship }) => relationship.id));\n let nextId = 100;\n while (ids.has(`rId${nextId}`)) nextId++;\n assembledRelationships.push({\n sourcePart: '',\n relationship: {\n id: `rId${nextId}`,\n type: REL_CORE_PROPERTIES,\n target: 'docProps/core.xml',\n },\n });\n }\n }\n\n // Build and write [Content_Types].xml\n zip.file('[Content_Types].xml', buildContentTypesXml(parts, coreProps));\n\n // Build and write _rels/*.rels files\n const relsBySource = groupRelationshipsBySource(assembledRelationships);\n for (const [sourcePart, rels] of relsBySource) {\n const relsPath = sourcePart === '' ? '_rels/.rels' : buildRelsPath(sourcePart);\n zip.file(relsPath, buildRelationshipsXml(rels));\n }\n\n return zip;\n}\n\n// ============================================\n// [Content_Types].xml\n// ============================================\n\nfunction buildContentTypesXml(parts: PackagePart[], coreProps?: CoreProperties): string {\n const lines: string[] = [xmlDeclaration()];\n lines.push(`<Types xmlns=\"${NS_CONTENT_TYPES}\">`);\n\n // Default extension types\n const extensionTypes = new Map<string, string>();\n extensionTypes.set('rels', CONTENT_TYPE_RELATIONSHIPS);\n extensionTypes.set('xml', 'application/xml');\n\n // Collect extensions from binary parts (e.g., png, jpeg)\n for (const part of parts) {\n if (part.binaryContent !== undefined) {\n const ext = getExtension(part.path);\n if (ext && !extensionTypes.has(ext)) {\n extensionTypes.set(ext, part.contentType);\n }\n }\n }\n\n for (const [ext, ct] of extensionTypes) {\n lines.push(` <Default Extension=\"${escapeXml(ext)}\" ContentType=\"${escapeXml(ct)}\"/>`);\n }\n\n // Override entries for each XML part\n for (const part of parts) {\n if (part.content !== undefined) {\n lines.push(\n ` <Override PartName=\"/${escapeXml(part.path)}\" ContentType=\"${escapeXml(part.contentType)}\"/>`,\n );\n }\n }\n\n // Core properties override\n if (coreProps) {\n lines.push(\n ` <Override PartName=\"/docProps/core.xml\" ContentType=\"${CONTENT_TYPE_CORE_PROPERTIES}\"/>`,\n );\n }\n\n lines.push('</Types>');\n return lines.join('\\n');\n}\n\n// ============================================\n// Relationships XML\n// ============================================\n\nfunction buildRelationshipsXml(rels: Relationship[]): string {\n const lines: string[] = [xmlDeclaration()];\n lines.push(`<Relationships xmlns=\"${NS_RELATIONSHIPS}\">`);\n\n for (const rel of rels) {\n const attrs: string[] = [\n `Id=\"${escapeXml(rel.id)}\"`,\n `Type=\"${escapeXml(rel.type)}\"`,\n `Target=\"${escapeXml(rel.target)}\"`,\n ];\n if (rel.targetMode) {\n attrs.push(`TargetMode=\"${escapeXml(rel.targetMode)}\"`);\n }\n lines.push(` <Relationship ${attrs.join(' ')}/>`);\n }\n\n lines.push('</Relationships>');\n return lines.join('\\n');\n}\n\n// ============================================\n// Core Properties XML\n// ============================================\n\nfunction buildCorePropertiesXml(props: CoreProperties): string {\n const lines: string[] = [xmlDeclaration()];\n lines.push(\n `<cp:coreProperties` +\n ` xmlns:cp=\"${NS_CORE_PROPERTIES}\"` +\n ` xmlns:dc=\"${NS_DC}\"` +\n ` xmlns:dcterms=\"${NS_DCTERMS}\"` +\n ` xmlns:xsi=\"${NS_XSI}\">`,\n );\n\n if (props.title) lines.push(` <dc:title>${escapeXml(props.title)}</dc:title>`);\n if (props.subject) lines.push(` <dc:subject>${escapeXml(props.subject)}</dc:subject>`);\n if (props.creator) lines.push(` <dc:creator>${escapeXml(props.creator)}</dc:creator>`);\n if (props.description)\n lines.push(` <dc:description>${escapeXml(props.description)}</dc:description>`);\n if (props.keywords) lines.push(` <cp:keywords>${escapeXml(props.keywords)}</cp:keywords>`);\n if (props.lastModifiedBy)\n lines.push(` <cp:lastModifiedBy>${escapeXml(props.lastModifiedBy)}</cp:lastModifiedBy>`);\n if (props.revision) lines.push(` <cp:revision>${escapeXml(props.revision)}</cp:revision>`);\n if (props.created) {\n lines.push(\n ` <dcterms:created xsi:type=\"dcterms:W3CDTF\">${escapeXml(props.created)}</dcterms:created>`,\n );\n }\n if (props.modified) {\n lines.push(\n ` <dcterms:modified xsi:type=\"dcterms:W3CDTF\">${escapeXml(props.modified)}</dcterms:modified>`,\n );\n }\n\n lines.push('</cp:coreProperties>');\n return lines.join('\\n');\n}\n\n// ============================================\n// Helpers\n// ============================================\n\nfunction getExtension(path: string): string | undefined {\n const dot = path.lastIndexOf('.');\n if (dot === -1) return undefined;\n return path.substring(dot + 1).toLowerCase();\n}\n\nfunction buildRelsPath(partPath: string): string {\n const lastSlash = partPath.lastIndexOf('/');\n if (lastSlash === -1) {\n return `_rels/${partPath}.rels`;\n }\n const dir = partPath.substring(0, lastSlash);\n const file = partPath.substring(lastSlash + 1);\n return `${dir}/_rels/${file}.rels`;\n}\n\nfunction groupRelationshipsBySource(pending: PendingRelationship[]): Map<string, Relationship[]> {\n const grouped = new Map<string, Relationship[]>();\n for (const { sourcePart, relationship } of pending) {\n let list = grouped.get(sourcePart);\n if (!list) {\n list = [];\n grouped.set(sourcePart, list);\n }\n list.push(relationship);\n }\n return grouped;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/html/imageUtils.ts"],"sourcesContent":["/**\n * Image Utilities for HTML Export\n *\n * Browser-compatible helpers for converting image data to base64 data URIs\n * and inferring MIME types from filenames.\n */\n\nimport { extToMime } from '../shared/images.js';\n\n/**\n * Infer a MIME type from a filename's extension.\n * Returns 'application/octet-stream' for unknown types.\n *\n * Thin wrapper over the shared {@link extToMime} map so html export, docx\n * import, and pptx import all agree on extension → MIME.\n */\nexport function inferMimeType(filename: string): string {\n return extToMime(filename.split('.').pop() ?? '');\n}\n\n/**\n * Convert an ArrayBuffer to a base64-encoded data URI string.\n *\n * @param buffer - The binary image data\n * @param mimeType - MIME type (e.g., 'image/jpeg'). If not provided, defaults to\n * 'application/octet-stream'.\n * @returns A `data:` URI string\n */\nexport function arrayBufferToBase64DataUrl(buffer: ArrayBuffer, mimeType: string): string {\n const bytes = new Uint8Array(buffer);\n // Keep chunks divisible by three so concatenating their independently\n // encoded results cannot introduce padding in the middle of the stream.\n // This avoids both quadratic string concatenation and one image-sized\n // intermediate binary string for large embedded assets.\n const chunkSize = 3 * 8192;\n const chunks: string[] = [];\n for (let offset = 0; offset < bytes.length; offset += chunkSize) {\n const end = Math.min(offset + chunkSize, bytes.length);\n let binary = '';\n for (let i = offset; i < end; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n chunks.push(btoa(binary));\n }\n return `data:${mimeType};base64,${chunks.join('')}`;\n}\n\n/**\n * Extract the filename from a path or URL (strips directory and query).\n *\n * @example\n * extractFilename('images/hero.jpg') // 'hero.jpg'\n * extractFilename('https://example.com/photo.png?v=2') // 'photo.png'\n */\nexport function extractFilename(path: string): string {\n // Strip query/hash\n const clean = path.split('?')[0].split('#')[0];\n // Get last segment\n const parts = clean.split('/');\n return parts[parts.length - 1] || path;\n}\n"],"mappings":";;;;;AAgBO,SAAS,cAAc,UAA0B;AACtD,SAAO,UAAU,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE;AAClD;AAUO,SAAS,2BAA2B,QAAqB,UAA0B;AACxF,QAAM,QAAQ,IAAI,WAAW,MAAM;AAKnC,QAAM,YAAY,IAAI;AACtB,QAAM,SAAmB,CAAC;AAC1B,WAAS,SAAS,GAAG,SAAS,MAAM,QAAQ,UAAU,WAAW;AAC/D,UAAM,MAAM,KAAK,IAAI,SAAS,WAAW,MAAM,MAAM;AACrD,QAAI,SAAS;AACb,aAAS,IAAI,QAAQ,IAAI,KAAK,KAAK;AACjC,gBAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,IACxC;AACA,WAAO,KAAK,KAAK,MAAM,CAAC;AAAA,EAC1B;AACA,SAAO,QAAQ,QAAQ,WAAW,OAAO,KAAK,EAAE,CAAC;AACnD;AASO,SAAS,gBAAgB,MAAsB;AAEpD,QAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAE7C,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,SAAO,MAAM,MAAM,SAAS,CAAC,KAAK;AACpC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/infer/mapTheme.ts"],"sourcesContent":["/**\n * Map a raw OOXML theme extraction onto a compiled Squisq `Theme`.\n *\n * Only a handful of seeds are taken from the file (lead accents, background,\n * text, major/minor fonts); `compileTheme` derives the rest of the palette\n * via OKLCh so the result is always a complete, validated theme that stays\n * re-editable in the customizer (seeds are recorded on `Theme.seedColors`).\n */\n\nimport type {\n DeepPartial,\n Theme,\n ThemeColorScheme,\n ThemeSeedColors,\n} from '@bendyline/squisq/schemas';\nimport {\n accentToColorScheme,\n compileTheme,\n contrastRatio,\n isHex,\n matchFontFamily,\n relativeLuminance,\n} from '@bendyline/squisq/schemas';\nimport type { PptxColorHints } from '../pptx/layouts.js';\nimport type { ExtractedFileTheme } from './types.js';\n\nexport interface CompileExtractedOptions {\n /** Preferred display name for the compiled theme (e.g. the file's basename). */\n nameHint?: string;\n}\n\nexport interface MappedThemePartial {\n partial: DeepPartial<Theme>;\n warnings: string[];\n}\n\n/** Neutral primary used when a theme resolves no usable accent slots. */\nconst FALLBACK_PRIMARY = '#3182ce';\n\n/** Accents closer to the background than this contrast are dropped (near-invisible). */\nconst MIN_ACCENT_CONTRAST = 1.3;\n\nconst ACCENT_SLOTS = ['accent1', 'accent2', 'accent3', 'accent4', 'accent5', 'accent6'] as const;\n\n/** Companion slot providing `backgroundLight` for each background slot. */\nconst COMPANION_SLOT = { lt1: 'lt2', dk1: 'dk2', lt2: 'lt1', dk2: 'dk1' } as const;\n\nfunction slugify(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug || 'imported';\n}\n\n/**\n * Build the `DeepPartial<Theme>` for an extraction. Exported for tests; most\n * callers want {@link compileExtractedTheme}.\n */\nexport function extractedThemeToPartial(\n extraction: ExtractedFileTheme,\n opts: CompileExtractedOptions = {},\n): MappedThemePartial {\n const warnings: string[] = [];\n const { colors, colorMap, fonts } = extraction;\n\n const name = opts.nameHint?.trim() || extraction.themeName || 'Imported Theme';\n const partial: DeepPartial<Theme> = {\n id: `custom-${slugify(name)}`,\n name,\n description: `Imported from a ${extraction.sourceFormat} file${\n extraction.themeName ? ` (theme \"${extraction.themeName}\")` : ''\n }.`,\n };\n\n if (colors) {\n const primary = colors.accent1 ?? colors.accent2;\n if (!primary) {\n warnings.push('theme: no usable accent colors; primary defaults to Squisq blue');\n }\n\n let background: string | undefined = colors[colorMap.bg1];\n let text: string | undefined = colors[colorMap.tx1];\n // Pathological color maps can land background and text on similar\n // surfaces; when they don't contrast, let the compiler derive both\n // from the primary instead of shipping unreadable seeds.\n if (\n background &&\n text &&\n Math.abs(relativeLuminance(background) - relativeLuminance(text)) < 0.2\n ) {\n warnings.push('theme: background and text colors are too close; deriving surfaces instead');\n background = undefined;\n text = undefined;\n }\n\n const seeds: ThemeSeedColors = {\n primary: primary ?? FALLBACK_PRIMARY,\n ...(colors.accent2 ? { secondary: colors.accent2 } : {}),\n ...(colors.accent3 ? { accent: colors.accent3 } : {}),\n ...(background ? { background } : {}),\n ...(text ? { text } : {}),\n };\n partial.seedColors = seeds;\n\n const explicitColors: DeepPartial<Theme>['colors'] = {};\n const companion = background ? colors[COMPANION_SLOT[colorMap.bg1]] : undefined;\n if (background && companion && companion !== background) {\n explicitColors.backgroundLight = companion;\n }\n if (colors.hlink && isHex(colors.hlink)) {\n explicitColors.highlight = colors.hlink;\n }\n if (Object.keys(explicitColors).length > 0) partial.colors = explicitColors;\n\n const schemes: Record<string, ThemeColorScheme> = {};\n const seenHexes = new Set<string>();\n const schemeBackground = background ?? colors[colorMap.bg1];\n for (const slot of ACCENT_SLOTS) {\n const hex = colors[slot];\n if (!hex || seenHexes.has(hex)) continue;\n if (schemeBackground && contrastRatio(hex, schemeBackground) < MIN_ACCENT_CONTRAST) {\n warnings.push(`theme: ${slot} is too close to the background; dropped from accents`);\n continue;\n }\n seenHexes.add(hex);\n schemes[slot] = accentToColorScheme(hex);\n }\n if (Object.keys(schemes).length > 0) partial.colorSchemes = schemes;\n }\n\n if (fonts?.majorLatin || fonts?.minorLatin) {\n partial.typography = {\n ...(fonts.majorLatin ? { titleFont: matchFontFamily(fonts.majorLatin) } : {}),\n ...(fonts.minorLatin ? { bodyFont: matchFontFamily(fonts.minorLatin) } : {}),\n };\n }\n\n return { partial, warnings };\n}\n\n/**\n * Compile an extraction into a full validated Squisq theme. Mapping warnings\n * (dropped accents, low-contrast surfaces) are returned alongside; extraction\n * warnings stay on `extraction.warnings`.\n */\nexport function compileExtractedTheme(\n extraction: ExtractedFileTheme,\n opts: CompileExtractedOptions = {},\n): { theme: Theme; warnings: string[] } {\n const { partial, warnings } = extractedThemeToPartial(extraction, opts);\n return { theme: compileTheme(partial), warnings };\n}\n\n/**\n * Build the color hints the PPTX layout generator uses to resolve scheme\n * color references (`a:schemeClr val=\"accent1\"`, `bg1`, `tx1`, …) on\n * decorative shapes.\n */\nexport function colorHintsFromExtraction(extraction: ExtractedFileTheme): PptxColorHints {\n const { colors, colorMap } = extraction;\n if (!colors) return {};\n const schemeColors: Record<string, string> = {};\n for (const [slot, hex] of Object.entries(colors)) {\n if (typeof hex === 'string') schemeColors[slot] = hex;\n }\n const background = colors[colorMap.bg1];\n const text = colors[colorMap.tx1];\n if (background) schemeColors['bg1'] = background;\n if (text) schemeColors['tx1'] = text;\n return {\n ...(background ? { background } : {}),\n ...(text ? { text } : {}),\n schemeColors,\n };\n}\n"],"mappings":"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","names":[]}