@bendyline/squisq-formats 1.1.0 → 1.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.
- package/README.md +23 -10
- package/dist/{chunk-ULLIPBEJ.js → chunk-743COJWQ.js} +6 -2
- package/dist/chunk-743COJWQ.js.map +1 -0
- package/dist/{chunk-KAK4V57E.js → chunk-BHHEDPRB.js} +16 -112
- package/dist/chunk-BHHEDPRB.js.map +1 -0
- package/dist/{chunk-MQHCXI56.js → chunk-CLGUOVYR.js} +193 -23
- package/dist/chunk-CLGUOVYR.js.map +1 -0
- package/dist/chunk-FIFCQN3W.js +630 -0
- package/dist/chunk-FIFCQN3W.js.map +1 -0
- package/dist/chunk-KJ4NS4DX.js +120 -0
- package/dist/chunk-KJ4NS4DX.js.map +1 -0
- package/dist/{chunk-TBPD5PCU.js → chunk-S3Y7H2BK.js} +161 -11
- package/dist/chunk-S3Y7H2BK.js.map +1 -0
- package/dist/docx/export.d.ts +15 -0
- package/dist/docx/export.d.ts.map +1 -1
- package/dist/docx/export.js +144 -19
- package/dist/docx/export.js.map +1 -1
- package/dist/docx/import.d.ts +13 -0
- package/dist/docx/import.d.ts.map +1 -1
- package/dist/docx/import.js +102 -10
- package/dist/docx/import.js.map +1 -1
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/index.d.ts.map +1 -1
- package/dist/docx/index.js +1 -1
- package/dist/docx/index.js.map +1 -1
- package/dist/html/htmlTemplate.d.ts +6 -0
- package/dist/html/htmlTemplate.d.ts.map +1 -1
- package/dist/html/htmlTemplate.js +5 -1
- package/dist/html/htmlTemplate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/ooxml/index.d.ts +1 -1
- package/dist/ooxml/index.d.ts.map +1 -1
- package/dist/ooxml/index.js +1 -1
- package/dist/ooxml/index.js.map +1 -1
- package/dist/ooxml/namespaces.d.ts +10 -0
- package/dist/ooxml/namespaces.d.ts.map +1 -1
- package/dist/ooxml/namespaces.js +10 -0
- package/dist/ooxml/namespaces.js.map +1 -1
- package/dist/pdf/export.d.ts +6 -0
- package/dist/pdf/export.d.ts.map +1 -1
- package/dist/pdf/export.js +37 -9
- package/dist/pdf/export.js.map +1 -1
- package/dist/pdf/import.d.ts +16 -0
- package/dist/pdf/import.d.ts.map +1 -1
- package/dist/pdf/import.js +177 -0
- package/dist/pdf/import.js.map +1 -1
- package/dist/pdf/index.d.ts +1 -1
- package/dist/pdf/index.d.ts.map +1 -1
- package/dist/pdf/index.js +1 -1
- package/dist/pdf/index.js.map +1 -1
- package/dist/pptx/export.d.ts +75 -0
- package/dist/pptx/export.d.ts.map +1 -0
- package/dist/pptx/export.js +722 -0
- package/dist/pptx/export.js.map +1 -0
- package/dist/pptx/index.d.ts +12 -27
- package/dist/pptx/index.d.ts.map +1 -1
- package/dist/pptx/index.js +12 -22
- package/dist/pptx/index.js.map +1 -1
- package/dist/pptx/styles.d.ts +54 -0
- package/dist/pptx/styles.d.ts.map +1 -0
- package/dist/pptx/styles.js +73 -0
- package/dist/pptx/styles.js.map +1 -0
- package/dist/pptx/templates.d.ts +24 -0
- package/dist/pptx/templates.d.ts.map +1 -0
- package/dist/pptx/templates.js +158 -0
- package/dist/pptx/templates.js.map +1 -0
- package/package.json +2 -2
- package/src/docx/export.ts +183 -20
- package/src/docx/import.ts +127 -10
- package/src/docx/index.ts +1 -1
- package/src/html/htmlTemplate.ts +13 -0
- package/src/index.ts +2 -2
- package/src/ooxml/index.ts +7 -0
- package/src/ooxml/namespaces.ts +23 -0
- package/src/pdf/export.ts +57 -9
- package/src/pdf/import.ts +228 -0
- package/src/pdf/index.ts +1 -1
- package/src/pptx/export.ts +1041 -0
- package/src/pptx/index.ts +15 -37
- package/src/pptx/styles.ts +96 -0
- package/src/pptx/templates.ts +187 -0
- package/dist/chunk-532L4D5D.js +0 -21
- package/dist/chunk-532L4D5D.js.map +0 -1
- package/dist/chunk-KAK4V57E.js.map +0 -1
- package/dist/chunk-MQHCXI56.js.map +0 -1
- package/dist/chunk-TBPD5PCU.js.map +0 -1
- package/dist/chunk-ULLIPBEJ.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/docx/export.ts","../src/docx/styles.ts","../src/docx/import.ts"],"sourcesContent":["/**\n * DOCX Export\n *\n * Converts a squisq MarkdownDocument (or Doc) into a .docx file\n * by generating WordprocessingML XML and assembling the OOXML package.\n *\n * No third-party docx library — all XML is generated directly using\n * the shared ooxml/ infrastructure of this package.\n *\n * @example\n * ```ts\n * import { parseMarkdown } from '@bendyline/squisq/markdown';\n * import { markdownDocToDocx } from '@bendyline/squisq-formats/docx';\n *\n * const md = parseMarkdown('# Hello\\n\\nWorld **bold** text');\n * const blob = await markdownDocToDocx(md);\n * ```\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { docToMarkdown } 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 MarkdownLink,\n MarkdownImage,\n MarkdownFootnoteReference,\n} from '@bendyline/squisq/markdown';\n\nimport { createPackage } from '../ooxml/writer.js';\nimport { xmlDeclaration, escapeXml } from '../ooxml/xmlUtils.js';\nimport {\n NS_WML,\n NS_R,\n NS_MC,\n REL_OFFICE_DOCUMENT,\n REL_STYLES,\n REL_NUMBERING,\n REL_SETTINGS,\n REL_FONT_TABLE,\n REL_HYPERLINK,\n REL_FOOTNOTES,\n CONTENT_TYPE_DOCX_DOCUMENT,\n CONTENT_TYPE_DOCX_STYLES,\n CONTENT_TYPE_DOCX_NUMBERING,\n CONTENT_TYPE_DOCX_SETTINGS,\n CONTENT_TYPE_DOCX_FONT_TABLE,\n CONTENT_TYPE_DOCX_FOOTNOTES,\n} from '../ooxml/namespaces.js';\nimport {\n DEPTH_TO_STYLE_ID,\n HEADING_FONT_SIZES,\n DEFAULT_FONT,\n DEFAULT_HEADING_FONT,\n DEFAULT_FONT_SIZE_HALF_POINTS,\n DEFAULT_CODE_FONT,\n DEFAULT_CODE_FONT_SIZE,\n HYPERLINK_COLOR,\n pointsToTwips,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Options for DOCX export.\n */\nexport interface DocxExportOptions {\n /** Document title (appears in core properties) */\n title?: string;\n /** Document author */\n author?: string;\n /** Document description */\n description?: string;\n /** Default body font family. Default: \"Calibri\" */\n defaultFont?: string;\n /** Default body font size in points. Default: 11 */\n defaultFontSize?: number;\n}\n\n/**\n * Convert a MarkdownDocument to a .docx Blob.\n *\n * @param doc - The parsed markdown document\n * @param options - Export options\n * @returns An ArrayBuffer containing the .docx file\n */\nexport async function markdownDocToDocx(\n doc: MarkdownDocument,\n options: DocxExportOptions = {},\n): Promise<ArrayBuffer> {\n const ctx = new ExportContext(options);\n const bodyXml = convertBlocks(doc.children, ctx);\n return buildDocxPackage(bodyXml, ctx, options);\n}\n\n/**\n * Convert a squisq Doc to a .docx Blob.\n *\n * Convenience wrapper that converts Doc → MarkdownDocument → DOCX.\n *\n * @param doc - The squisq Doc\n * @param options - Export options\n * @returns An ArrayBuffer containing the .docx file\n */\nexport async function docToDocx(doc: Doc, options: DocxExportOptions = {}): Promise<ArrayBuffer> {\n const markdownDoc = docToMarkdown(doc);\n return markdownDocToDocx(markdownDoc, options);\n}\n\n// ============================================\n// Export Context\n// ============================================\n\n/**\n * Tracks state during export: relationship IDs, numbering definitions,\n * footnote bodies, and embedded images.\n */\nclass ExportContext {\n private nextRelId = 1;\n private nextNumId = 1;\n private nextFootnoteId = 1; // 0 is separator, start user footnotes at 1\n\n /** Relationships for word/_rels/document.xml.rels */\n readonly relationships: Array<{\n id: string;\n type: string;\n target: string;\n targetMode?: 'External';\n }> = [];\n\n /** Numbering definitions (abstract + num) */\n readonly numberingDefs: NumberingDef[] = [];\n\n /** Footnote XML bodies (keyed by footnote id) */\n readonly footnotes = new Map<number, string>();\n\n /** Footnote identifier → numeric id mapping */\n readonly footnoteIdMap = new Map<string, number>();\n\n /** Embedded images: rId → { path, data, contentType } */\n readonly images: Array<{\n relId: string;\n path: string;\n data: ArrayBuffer | Uint8Array;\n contentType: string;\n }> = [];\n\n /** Whether we have any lists (determines if numbering.xml is needed) */\n hasLists = false;\n\n /** Whether we have any footnotes */\n hasFootnotes = false;\n\n readonly font: string;\n readonly fontSize: number;\n\n constructor(options: DocxExportOptions) {\n this.font = options.defaultFont ?? DEFAULT_FONT;\n this.fontSize = options.defaultFontSize\n ? options.defaultFontSize * 2\n : DEFAULT_FONT_SIZE_HALF_POINTS;\n }\n\n /** Allocate a new relationship ID */\n allocRelId(): string {\n return `rId${this.nextRelId++}`;\n }\n\n /** Add a hyperlink relationship and return the rId */\n addHyperlink(url: string): string {\n const id = this.allocRelId();\n this.relationships.push({\n id,\n type: REL_HYPERLINK,\n target: url,\n targetMode: 'External',\n });\n return id;\n }\n\n /** Allocate a numbering definition for a list */\n allocNumbering(ordered: boolean): number {\n const numId = this.nextNumId++;\n this.numberingDefs.push({ numId, ordered });\n this.hasLists = true;\n return numId;\n }\n\n /** Register or look up a footnote by its string identifier */\n getFootnoteId(identifier: string): number {\n let id = this.footnoteIdMap.get(identifier);\n if (id === undefined) {\n id = this.nextFootnoteId++;\n this.footnoteIdMap.set(identifier, id);\n this.hasFootnotes = true;\n }\n return id;\n }\n}\n\ninterface NumberingDef {\n numId: number;\n ordered: boolean;\n}\n\n// ============================================\n// Block Conversion\n// ============================================\n\nfunction convertBlocks(nodes: MarkdownBlockNode[], ctx: ExportContext): string {\n const parts: string[] = [];\n for (const node of nodes) {\n parts.push(convertBlock(node, ctx, 0));\n }\n return parts.join('');\n}\n\nfunction convertBlock(node: MarkdownBlockNode, ctx: ExportContext, listDepth: number): string {\n switch (node.type) {\n case 'heading':\n return convertHeading(node, ctx);\n case 'paragraph':\n return convertParagraph(node, ctx);\n case 'blockquote':\n return convertBlockquote(node, ctx);\n case 'list':\n return convertList(node, ctx, listDepth);\n case 'code':\n return convertCodeBlock(node);\n case 'table':\n return convertTable(node, ctx);\n case 'thematicBreak':\n return convertThematicBreak();\n case 'htmlBlock':\n return convertHtmlBlock(node);\n case 'math':\n return convertMathBlock(node);\n case 'footnoteDefinition':\n return convertFootnoteDefinition(node, ctx);\n default:\n // Definition lists, directives, link definitions — skip or emit as plain text\n return '';\n }\n}\n\nfunction convertHeading(node: MarkdownHeading, ctx: ExportContext): string {\n const styleId = DEPTH_TO_STYLE_ID[node.depth] ?? 'Heading1';\n const runs = convertInlines(node.children, ctx);\n return `<w:p>` + `<w:pPr><w:pStyle w:val=\"${styleId}\"/></w:pPr>` + runs + `</w:p>`;\n}\n\nfunction convertParagraph(node: MarkdownParagraph, ctx: ExportContext): string {\n const runs = convertInlines(node.children, ctx);\n return `<w:p>${runs}</w:p>`;\n}\n\nfunction convertBlockquote(node: MarkdownBlockquote, ctx: ExportContext): string {\n // Render each child block as a paragraph with Quote style\n const parts: string[] = [];\n for (const child of node.children) {\n if (child.type === 'paragraph') {\n const runs = convertInlines(child.children, ctx);\n parts.push(\n `<w:p>` +\n `<w:pPr><w:pStyle w:val=\"Quote\"/>` +\n `<w:ind w:left=\"${pointsToTwips(36)}\"/>` +\n `<w:pBdr><w:left w:val=\"single\" w:sz=\"12\" w:space=\"4\" w:color=\"CCCCCC\"/></w:pBdr>` +\n `</w:pPr>` +\n runs +\n `</w:p>`,\n );\n } else {\n // Nested non-paragraph (e.g., nested blockquote, list) — recurse\n parts.push(convertBlock(child, ctx, 0));\n }\n }\n return parts.join('');\n}\n\nfunction convertList(node: MarkdownList, ctx: ExportContext, depth: number): string {\n const numId = ctx.allocNumbering(node.ordered ?? false);\n const parts: string[] = [];\n for (const item of node.children) {\n parts.push(convertListItem(item, ctx, numId, depth));\n }\n return parts.join('');\n}\n\nfunction convertListItem(\n item: MarkdownListItem,\n ctx: ExportContext,\n numId: number,\n depth: number,\n): string {\n const parts: string[] = [];\n for (const child of item.children) {\n if (child.type === 'paragraph') {\n const runs = convertInlines(child.children, ctx);\n parts.push(\n `<w:p>` +\n `<w:pPr>` +\n `<w:pStyle w:val=\"ListParagraph\"/>` +\n `<w:numPr><w:ilvl w:val=\"${depth}\"/><w:numId w:val=\"${numId}\"/></w:numPr>` +\n `</w:pPr>` +\n runs +\n `</w:p>`,\n );\n } else if (child.type === 'list') {\n // Nested list — increase depth\n parts.push(convertList(child, ctx, depth + 1));\n } else {\n parts.push(convertBlock(child, ctx, depth));\n }\n }\n return parts.join('');\n}\n\nfunction convertCodeBlock(node: MarkdownCodeBlock): string {\n // Emit each line as a separate paragraph with code styling\n const lines = node.value.split('\\n');\n const parts: string[] = [];\n for (const line of lines) {\n parts.push(\n `<w:p>` +\n `<w:pPr>` +\n `<w:pStyle w:val=\"Code\"/>` +\n `<w:pBdr>` +\n `<w:top w:val=\"single\" w:sz=\"4\" w:space=\"1\" w:color=\"CCCCCC\"/>` +\n `<w:left w:val=\"single\" w:sz=\"4\" w:space=\"4\" w:color=\"CCCCCC\"/>` +\n `<w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"1\" w:color=\"CCCCCC\"/>` +\n `<w:right w:val=\"single\" w:sz=\"4\" w:space=\"4\" w:color=\"CCCCCC\"/>` +\n `</w:pBdr>` +\n `<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"F5F5F5\"/>` +\n `</w:pPr>` +\n `<w:r>` +\n `<w:rPr><w:rFonts w:ascii=\"${DEFAULT_CODE_FONT}\" w:hAnsi=\"${DEFAULT_CODE_FONT}\"/>` +\n `<w:sz w:val=\"${DEFAULT_CODE_FONT_SIZE}\"/></w:rPr>` +\n `<w:t xml:space=\"preserve\">${escapeXml(line)}</w:t>` +\n `</w:r>` +\n `</w:p>`,\n );\n }\n return parts.join('');\n}\n\nfunction convertTable(node: MarkdownTable, ctx: ExportContext): string {\n const rows: string[] = [];\n for (let ri = 0; ri < node.children.length; ri++) {\n const row = node.children[ri];\n rows.push(convertTableRow(row, ctx, ri === 0, node.align));\n }\n\n return (\n `<w:tbl>` +\n `<w:tblPr>` +\n `<w:tblStyle w:val=\"TableGrid\"/>` +\n `<w:tblW w:w=\"0\" w:type=\"auto\"/>` +\n `<w:tblBorders>` +\n `<w:top w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:left w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:right w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:insideH w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:insideV w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `</w:tblBorders>` +\n `</w:tblPr>` +\n `<w:tblGrid/>` +\n rows.join('') +\n `</w:tbl>`\n );\n}\n\nfunction convertTableRow(\n row: MarkdownTableRow,\n ctx: ExportContext,\n isHeader: boolean,\n align?: (('left' | 'right' | 'center') | null)[],\n): string {\n const cells: string[] = [];\n for (let ci = 0; ci < row.children.length; ci++) {\n const cell = row.children[ci];\n const cellAlign = align?.[ci] ?? null;\n cells.push(convertTableCell(cell, ctx, isHeader, cellAlign));\n }\n const trPr = isHeader ? '<w:trPr><w:tblHeader/></w:trPr>' : '';\n return `<w:tr>${trPr}${cells.join('')}</w:tr>`;\n}\n\nfunction convertTableCell(\n cell: MarkdownTableCell,\n ctx: ExportContext,\n isHeader: boolean,\n align: 'left' | 'right' | 'center' | null,\n): string {\n const runs = convertInlines(cell.children, ctx);\n const rPr = isHeader ? '<w:rPr><w:b/></w:rPr>' : '';\n const jcMap = { left: 'left', center: 'center', right: 'right' };\n const jc = align ? `<w:jc w:val=\"${jcMap[align]}\"/>` : '';\n const pPr = rPr || jc ? `<w:pPr>${jc}</w:pPr>` : '';\n return `<w:tc><w:p>${pPr}${runs}</w:p></w:tc>`;\n}\n\nfunction convertThematicBreak(): string {\n return (\n `<w:p>` +\n `<w:pPr>` +\n `<w:pBdr><w:bottom w:val=\"single\" w:sz=\"6\" w:space=\"1\" w:color=\"auto\"/></w:pBdr>` +\n `</w:pPr>` +\n `</w:p>`\n );\n}\n\nfunction convertHtmlBlock(node: MarkdownHtmlBlock): string {\n // Best-effort: extract text content from the HTML\n const text = stripHtmlTags(node.rawHtml);\n if (!text.trim()) return '';\n return `<w:p><w:r><w:t xml:space=\"preserve\">${escapeXml(text)}</w:t></w:r></w:p>`;\n}\n\nfunction convertMathBlock(node: MarkdownMathBlock): string {\n // Emit as a styled paragraph with the raw LaTeX\n return (\n `<w:p>` +\n `<w:pPr><w:jc w:val=\"center\"/></w:pPr>` +\n `<w:r>` +\n `<w:rPr><w:rFonts w:ascii=\"Cambria Math\" w:hAnsi=\"Cambria Math\"/><w:i/></w:rPr>` +\n `<w:t xml:space=\"preserve\">${escapeXml(node.value)}</w:t>` +\n `</w:r>` +\n `</w:p>`\n );\n}\n\nfunction convertFootnoteDefinition(node: MarkdownFootnoteDefinition, ctx: ExportContext): string {\n const fnId = ctx.getFootnoteId(node.identifier);\n\n // Build the footnote body XML\n const bodyParts: string[] = [];\n for (const child of node.children) {\n if (child.type === 'paragraph') {\n const runs = convertInlines(child.children, ctx);\n bodyParts.push(`<w:p>${runs}</w:p>`);\n }\n }\n\n const footnoteXml =\n `<w:footnote w:id=\"${fnId}\">` +\n (bodyParts.length > 0 ? bodyParts.join('') : `<w:p/>`) +\n `</w:footnote>`;\n\n ctx.footnotes.set(fnId, footnoteXml);\n\n // Don't emit anything in the main body for the definition\n return '';\n}\n\n// ============================================\n// Inline Conversion\n// ============================================\n\n/**\n * Formatting state passed down through nested inline elements.\n */\ninterface InlineFormat {\n bold?: boolean;\n italic?: boolean;\n strike?: boolean;\n code?: boolean;\n}\n\nfunction convertInlines(\n nodes: MarkdownInlineNode[],\n ctx: ExportContext,\n format: InlineFormat = {},\n): string {\n const parts: string[] = [];\n for (const node of nodes) {\n parts.push(convertInline(node, ctx, format));\n }\n return parts.join('');\n}\n\nfunction convertInline(node: MarkdownInlineNode, ctx: ExportContext, format: InlineFormat): string {\n switch (node.type) {\n case 'text':\n return makeRun(node.value, format);\n case 'strong':\n return convertInlines(node.children, ctx, { ...format, bold: true });\n case 'emphasis':\n return convertInlines(node.children, ctx, { ...format, italic: true });\n case 'delete':\n return convertInlines(node.children, ctx, { ...format, strike: true });\n case 'inlineCode':\n return makeRun(node.value, { ...format, code: true });\n case 'link':\n return convertLink(node, ctx, format);\n case 'image':\n return convertImage(node);\n case 'break':\n return `<w:r><w:br/></w:r>`;\n case 'htmlInline':\n return makeRun(stripHtmlTags(node.rawHtml), format);\n case 'inlineMath':\n return makeRun(node.value, { ...format, code: true });\n case 'footnoteReference':\n return convertFootnoteRef(node, ctx);\n default:\n // linkReference, imageReference, textDirective — skip or emit plain\n return '';\n }\n}\n\nfunction makeRun(text: string, format: InlineFormat): string {\n if (!text) return '';\n\n const rPrParts: string[] = [];\n if (format.bold) rPrParts.push('<w:b/>');\n if (format.italic) rPrParts.push('<w:i/>');\n if (format.strike) rPrParts.push('<w:strike/>');\n if (format.code) {\n rPrParts.push(\n `<w:rFonts w:ascii=\"${DEFAULT_CODE_FONT}\" w:hAnsi=\"${DEFAULT_CODE_FONT}\"/>`,\n `<w:sz w:val=\"${DEFAULT_CODE_FONT_SIZE}\"/>`,\n );\n }\n\n const rPr = rPrParts.length > 0 ? `<w:rPr>${rPrParts.join('')}</w:rPr>` : '';\n\n // Use xml:space=\"preserve\" to keep leading/trailing whitespace\n return `<w:r>${rPr}<w:t xml:space=\"preserve\">${escapeXml(text)}</w:t></w:r>`;\n}\n\nfunction convertLink(node: MarkdownLink, ctx: ExportContext, format: InlineFormat): string {\n const rId = ctx.addHyperlink(node.url);\n const _runs = convertInlines(node.children, ctx, { ...format });\n\n // Wrap each run's rPr with hyperlink styling\n // For simplicity, emit inline runs with hyperlink color + underline\n const styledRuns = convertInlinesWithHyperlinkStyle(node.children, ctx, format);\n\n return `<w:hyperlink r:id=\"${rId}\">${styledRuns}</w:hyperlink>`;\n}\n\nfunction convertInlinesWithHyperlinkStyle(\n nodes: MarkdownInlineNode[],\n ctx: ExportContext,\n format: InlineFormat,\n): string {\n const parts: string[] = [];\n for (const node of nodes) {\n if (node.type === 'text') {\n parts.push(makeHyperlinkRun(node.value, format));\n } else {\n // For nested formatting inside links, add hyperlink style\n parts.push(convertInline(node, ctx, format));\n }\n }\n return parts.join('');\n}\n\nfunction makeHyperlinkRun(text: string, format: InlineFormat): string {\n if (!text) return '';\n\n const rPrParts: string[] = [\n '<w:rStyle w:val=\"Hyperlink\"/>',\n `<w:color w:val=\"${HYPERLINK_COLOR}\"/>`,\n '<w:u w:val=\"single\"/>',\n ];\n if (format.bold) rPrParts.push('<w:b/>');\n if (format.italic) rPrParts.push('<w:i/>');\n\n return (\n `<w:r><w:rPr>${rPrParts.join('')}</w:rPr>` +\n `<w:t xml:space=\"preserve\">${escapeXml(text)}</w:t></w:r>`\n );\n}\n\nfunction convertImage(_node: MarkdownImage): string {\n // Image embedding requires fetching the image data and packing it as\n // a binary part. For now, emit a placeholder text run.\n // Full image support can be added by fetching the URL and creating\n // a DrawingML inline + binary media part.\n const alt = _node.alt || _node.url;\n return makeRun(`[Image: ${alt}]`, { italic: true });\n}\n\nfunction convertFootnoteRef(node: MarkdownFootnoteReference, ctx: ExportContext): string {\n const fnId = ctx.getFootnoteId(node.identifier);\n return (\n `<w:r>` +\n `<w:rPr><w:rStyle w:val=\"FootnoteReference\"/><w:vertAlign w:val=\"superscript\"/></w:rPr>` +\n `<w:footnoteReference w:id=\"${fnId}\"/>` +\n `</w:r>`\n );\n}\n\n// ============================================\n// Package Assembly\n// ============================================\n\nasync function buildDocxPackage(\n bodyXml: string,\n ctx: ExportContext,\n options: DocxExportOptions,\n): Promise<ArrayBuffer> {\n const pkg = createPackage();\n\n // --- Register fixed relationships ---\n let relCounter = 100; // Start high to avoid collisions with dynamic rels\n const stylesRelId = `rId${relCounter++}`;\n const numberingRelId = `rId${relCounter++}`;\n const settingsRelId = `rId${relCounter++}`;\n const fontTableRelId = `rId${relCounter++}`;\n const footnotesRelId = `rId${relCounter++}`;\n\n // --- word/document.xml ---\n const documentXml = buildDocumentXml(bodyXml);\n pkg.addPart('word/document.xml', documentXml, CONTENT_TYPE_DOCX_DOCUMENT);\n\n // --- word/styles.xml ---\n const stylesXml = buildStylesXml(options);\n pkg.addPart('word/styles.xml', stylesXml, CONTENT_TYPE_DOCX_STYLES);\n\n // --- word/settings.xml ---\n const settingsXml = buildSettingsXml();\n pkg.addPart('word/settings.xml', settingsXml, CONTENT_TYPE_DOCX_SETTINGS);\n\n // --- word/fontTable.xml ---\n const fontTableXml = buildFontTableXml(options);\n pkg.addPart('word/fontTable.xml', fontTableXml, CONTENT_TYPE_DOCX_FONT_TABLE);\n\n // --- word/numbering.xml (only if lists present) ---\n if (ctx.hasLists) {\n const numberingXml = buildNumberingXml(ctx);\n pkg.addPart('word/numbering.xml', numberingXml, CONTENT_TYPE_DOCX_NUMBERING);\n }\n\n // --- word/footnotes.xml (only if footnotes present) ---\n if (ctx.hasFootnotes) {\n const footnotesXml = buildFootnotesXml(ctx);\n pkg.addPart('word/footnotes.xml', footnotesXml, CONTENT_TYPE_DOCX_FOOTNOTES);\n }\n\n // --- Root relationship: this package contains a word document ---\n pkg.addRelationship('', {\n id: 'rId1',\n type: REL_OFFICE_DOCUMENT,\n target: 'word/document.xml',\n });\n\n // --- Document relationships ---\n pkg.addRelationship('word/document.xml', {\n id: stylesRelId,\n type: REL_STYLES,\n target: 'styles.xml',\n });\n pkg.addRelationship('word/document.xml', {\n id: settingsRelId,\n type: REL_SETTINGS,\n target: 'settings.xml',\n });\n pkg.addRelationship('word/document.xml', {\n id: fontTableRelId,\n type: REL_FONT_TABLE,\n target: 'fontTable.xml',\n });\n\n if (ctx.hasLists) {\n pkg.addRelationship('word/document.xml', {\n id: numberingRelId,\n type: REL_NUMBERING,\n target: 'numbering.xml',\n });\n }\n\n if (ctx.hasFootnotes) {\n pkg.addRelationship('word/document.xml', {\n id: footnotesRelId,\n type: REL_FOOTNOTES,\n target: 'footnotes.xml',\n });\n }\n\n // --- Dynamic relationships (hyperlinks, images) ---\n for (const rel of ctx.relationships) {\n pkg.addRelationship('word/document.xml', {\n id: rel.id,\n type: rel.type,\n target: rel.target,\n ...(rel.targetMode ? { targetMode: rel.targetMode } : {}),\n });\n }\n\n // --- Embedded images ---\n for (const img of ctx.images) {\n pkg.addBinaryPart(img.path, img.data, img.contentType);\n }\n\n // --- Core properties ---\n if (options.title || options.author || options.description) {\n pkg.setCoreProperties({\n title: options.title,\n creator: options.author,\n description: options.description,\n created: new Date().toISOString(),\n modified: new Date().toISOString(),\n });\n }\n\n return pkg.toArrayBuffer();\n}\n\n// ============================================\n// XML Part Generators\n// ============================================\n\nfunction buildDocumentXml(bodyXml: string): string {\n return (\n xmlDeclaration() +\n `<w:document` +\n ` xmlns:wpc=\"http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas\"` +\n ` xmlns:mc=\"${NS_MC}\"` +\n ` xmlns:o=\"urn:schemas-microsoft-com:office:office\"` +\n ` xmlns:r=\"${NS_R}\"` +\n ` xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\"` +\n ` xmlns:v=\"urn:schemas-microsoft-com:vml\"` +\n ` xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\"` +\n ` xmlns:w10=\"urn:schemas-microsoft-com:office:word\"` +\n ` xmlns:w=\"${NS_WML}\"` +\n ` xmlns:wne=\"http://schemas.microsoft.com/office/word/2006/wordml\">` +\n `<w:body>` +\n bodyXml +\n `<w:sectPr>` +\n `<w:pgSz w:w=\"12240\" w:h=\"15840\"/>` +\n `<w:pgMar w:top=\"1440\" w:right=\"1440\" w:bottom=\"1440\" w:left=\"1440\" w:header=\"720\" w:footer=\"720\" w:gutter=\"0\"/>` +\n `<w:cols w:space=\"720\"/>` +\n `</w:sectPr>` +\n `</w:body>` +\n `</w:document>`\n );\n}\n\nfunction buildStylesXml(options: DocxExportOptions): string {\n const font = options.defaultFont ?? DEFAULT_FONT;\n const headingFont = DEFAULT_HEADING_FONT;\n\n return (\n xmlDeclaration() +\n `<w:styles xmlns:w=\"${NS_WML}\">` +\n // Default run properties\n `<w:docDefaults>` +\n `<w:rPrDefault><w:rPr>` +\n `<w:rFonts w:ascii=\"${escapeXml(font)}\" w:hAnsi=\"${escapeXml(font)}\" w:eastAsia=\"${escapeXml(font)}\" w:cs=\"${escapeXml(font)}\"/>` +\n `<w:sz w:val=\"${DEFAULT_FONT_SIZE_HALF_POINTS}\"/>` +\n `<w:szCs w:val=\"${DEFAULT_FONT_SIZE_HALF_POINTS}\"/>` +\n `</w:rPr></w:rPrDefault>` +\n `<w:pPrDefault/>` +\n `</w:docDefaults>` +\n // Normal style\n `<w:style w:type=\"paragraph\" w:default=\"1\" w:styleId=\"Normal\">` +\n `<w:name w:val=\"Normal\"/>` +\n `<w:qFormat/>` +\n `</w:style>` +\n // Heading styles\n buildHeadingStyles(headingFont) +\n // Quote style\n `<w:style w:type=\"paragraph\" w:styleId=\"Quote\">` +\n `<w:name w:val=\"Quote\"/>` +\n `<w:basedOn w:val=\"Normal\"/>` +\n `<w:pPr><w:ind w:left=\"720\"/></w:pPr>` +\n `<w:rPr><w:i/><w:color w:val=\"404040\"/></w:rPr>` +\n `</w:style>` +\n // Code style\n `<w:style w:type=\"paragraph\" w:styleId=\"Code\">` +\n `<w:name w:val=\"Code\"/>` +\n `<w:basedOn w:val=\"Normal\"/>` +\n `<w:rPr>` +\n `<w:rFonts w:ascii=\"${DEFAULT_CODE_FONT}\" w:hAnsi=\"${DEFAULT_CODE_FONT}\"/>` +\n `<w:sz w:val=\"${DEFAULT_CODE_FONT_SIZE}\"/>` +\n `</w:rPr>` +\n `</w:style>` +\n // ListParagraph style\n `<w:style w:type=\"paragraph\" w:styleId=\"ListParagraph\">` +\n `<w:name w:val=\"List Paragraph\"/>` +\n `<w:basedOn w:val=\"Normal\"/>` +\n `<w:pPr><w:ind w:left=\"720\"/></w:pPr>` +\n `</w:style>` +\n // Hyperlink character style\n `<w:style w:type=\"character\" w:styleId=\"Hyperlink\">` +\n `<w:name w:val=\"Hyperlink\"/>` +\n `<w:rPr><w:color w:val=\"${HYPERLINK_COLOR}\"/><w:u w:val=\"single\"/></w:rPr>` +\n `</w:style>` +\n // FootnoteReference character style\n `<w:style w:type=\"character\" w:styleId=\"FootnoteReference\">` +\n `<w:name w:val=\"footnote reference\"/>` +\n `<w:rPr><w:vertAlign w:val=\"superscript\"/></w:rPr>` +\n `</w:style>` +\n // Table Grid style\n `<w:style w:type=\"table\" w:styleId=\"TableGrid\">` +\n `<w:name w:val=\"Table Grid\"/>` +\n `<w:tblPr>` +\n `<w:tblBorders>` +\n `<w:top w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:left w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:right w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:insideH w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `<w:insideV w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"auto\"/>` +\n `</w:tblBorders>` +\n `</w:tblPr>` +\n `</w:style>` +\n `</w:styles>`\n );\n}\n\nfunction buildHeadingStyles(headingFont: string): string {\n let result = '';\n for (let depth = 1; depth <= 6; depth++) {\n const styleId = DEPTH_TO_STYLE_ID[depth];\n const fontSize = HEADING_FONT_SIZES[depth] ?? 22;\n result +=\n `<w:style w:type=\"paragraph\" w:styleId=\"${styleId}\">` +\n `<w:name w:val=\"heading ${depth}\"/>` +\n `<w:basedOn w:val=\"Normal\"/>` +\n `<w:next w:val=\"Normal\"/>` +\n `<w:qFormat/>` +\n `<w:pPr><w:outlineLvl w:val=\"${depth - 1}\"/><w:spacing w:before=\"240\" w:after=\"60\"/></w:pPr>` +\n `<w:rPr>` +\n `<w:rFonts w:ascii=\"${escapeXml(headingFont)}\" w:hAnsi=\"${escapeXml(headingFont)}\"/>` +\n `<w:b/>` +\n `<w:sz w:val=\"${fontSize}\"/>` +\n `<w:szCs w:val=\"${fontSize}\"/>` +\n `</w:rPr>` +\n `</w:style>`;\n }\n return result;\n}\n\nfunction buildSettingsXml(): string {\n return (\n xmlDeclaration() +\n `<w:settings xmlns:w=\"${NS_WML}\">` +\n `<w:defaultTabStop w:val=\"720\"/>` +\n `<w:characterSpacingControl w:val=\"doNotCompress\"/>` +\n `</w:settings>`\n );\n}\n\nfunction buildFontTableXml(options: DocxExportOptions): string {\n const font = options.defaultFont ?? DEFAULT_FONT;\n return (\n xmlDeclaration() +\n `<w:fonts xmlns:w=\"${NS_WML}\">` +\n `<w:font w:name=\"${escapeXml(font)}\">` +\n `<w:panose1 w:val=\"020F0502020204030204\"/>` +\n `<w:charset w:val=\"00\"/>` +\n `<w:family w:val=\"swiss\"/>` +\n `<w:pitch w:val=\"variable\"/>` +\n `</w:font>` +\n `<w:font w:name=\"${DEFAULT_HEADING_FONT}\">` +\n `<w:panose1 w:val=\"020F0302020204030204\"/>` +\n `<w:charset w:val=\"00\"/>` +\n `<w:family w:val=\"swiss\"/>` +\n `<w:pitch w:val=\"variable\"/>` +\n `</w:font>` +\n `<w:font w:name=\"${DEFAULT_CODE_FONT}\">` +\n `<w:charset w:val=\"00\"/>` +\n `<w:family w:val=\"modern\"/>` +\n `<w:pitch w:val=\"fixed\"/>` +\n `</w:font>` +\n `</w:fonts>`\n );\n}\n\nfunction buildNumberingXml(ctx: ExportContext): string {\n const abstract: string[] = [];\n const concrete: string[] = [];\n\n for (const def of ctx.numberingDefs) {\n const absId = def.numId;\n const levels: string[] = [];\n\n for (let lvl = 0; lvl < 9; lvl++) {\n if (def.ordered) {\n levels.push(\n `<w:lvl w:ilvl=\"${lvl}\">` +\n `<w:start w:val=\"1\"/>` +\n `<w:numFmt w:val=\"decimal\"/>` +\n `<w:lvlText w:val=\"%${lvl + 1}.\"/>` +\n `<w:lvlJc w:val=\"left\"/>` +\n `<w:pPr><w:ind w:left=\"${720 * (lvl + 1)}\" w:hanging=\"360\"/></w:pPr>` +\n `</w:lvl>`,\n );\n } else {\n const bullets = ['\\u2022', '\\u25E6', '\\u25AA']; // •, ◦, ▪\n const bullet = bullets[lvl % bullets.length];\n levels.push(\n `<w:lvl w:ilvl=\"${lvl}\">` +\n `<w:start w:val=\"1\"/>` +\n `<w:numFmt w:val=\"bullet\"/>` +\n `<w:lvlText w:val=\"${bullet}\"/>` +\n `<w:lvlJc w:val=\"left\"/>` +\n `<w:pPr><w:ind w:left=\"${720 * (lvl + 1)}\" w:hanging=\"360\"/></w:pPr>` +\n `<w:rPr><w:rFonts w:ascii=\"Symbol\" w:hAnsi=\"Symbol\"/></w:rPr>` +\n `</w:lvl>`,\n );\n }\n }\n\n abstract.push(\n `<w:abstractNum w:abstractNumId=\"${absId}\">` + levels.join('') + `</w:abstractNum>`,\n );\n concrete.push(\n `<w:num w:numId=\"${def.numId}\">` + `<w:abstractNumId w:val=\"${absId}\"/>` + `</w:num>`,\n );\n }\n\n return (\n xmlDeclaration() +\n `<w:numbering xmlns:w=\"${NS_WML}\">` +\n abstract.join('') +\n concrete.join('') +\n `</w:numbering>`\n );\n}\n\nfunction buildFootnotesXml(ctx: ExportContext): string {\n const footnotes: string[] = [];\n\n // Separator and continuation separator (required by Word)\n footnotes.push(\n `<w:footnote w:type=\"separator\" w:id=\"-1\">` +\n `<w:p><w:r><w:separator/></w:r></w:p>` +\n `</w:footnote>`,\n );\n footnotes.push(\n `<w:footnote w:type=\"continuationSeparator\" w:id=\"0\">` +\n `<w:p><w:r><w:continuationSeparator/></w:r></w:p>` +\n `</w:footnote>`,\n );\n\n // User footnotes\n for (const [_id, xml] of ctx.footnotes) {\n footnotes.push(xml);\n }\n\n return (\n xmlDeclaration() +\n `<w:footnotes xmlns:w=\"${NS_WML}\" xmlns:r=\"${NS_R}\">` +\n footnotes.join('') +\n `</w:footnotes>`\n );\n}\n\n// ============================================\n// Helpers\n// ============================================\n\n/**\n * Strip HTML tags from a string, keeping only text content.\n */\nfunction stripHtmlTags(html: string): string {\n return html.replace(/<[^>]*>/g, '');\n}\n","/**\n * DOCX Constants & Style Definitions\n *\n * Shared constants for DOCX import and export: built-in style IDs,\n * numbering format mappings, content type strings, and default\n * formatting values.\n */\n\n// ============================================\n// Built-in Style IDs\n// ============================================\n\n/**\n * Word built-in paragraph style IDs → heading depth.\n * These are the default style IDs in Word's Normal.dotm template.\n * Import uses these to detect heading levels from pStyle values.\n */\nexport const HEADING_STYLE_MAP: Record<string, number> = {\n Heading1: 1,\n Heading2: 2,\n Heading3: 3,\n Heading4: 4,\n Heading5: 5,\n Heading6: 6,\n heading1: 1,\n heading2: 2,\n heading3: 3,\n heading4: 4,\n heading5: 5,\n heading6: 6,\n // Some localized/legacy variants\n Title: 1,\n Subtitle: 2,\n};\n\n/**\n * Heading depth → Word style ID mapping (for export).\n */\nexport const DEPTH_TO_STYLE_ID: Record<number, string> = {\n 1: 'Heading1',\n 2: 'Heading2',\n 3: 'Heading3',\n 4: 'Heading4',\n 5: 'Heading5',\n 6: 'Heading6',\n};\n\n/**\n * Paragraph style IDs that map to blockquotes on import.\n */\nexport const QUOTE_STYLE_IDS = new Set(['Quote', 'IntenseQuote', 'quote', 'BlockQuote']);\n\n/**\n * Paragraph style ID for code blocks.\n */\nexport const CODE_STYLE_IDS = new Set(['Code', 'CodeBlock', 'HTMLCode', 'PlainText']);\n\n/**\n * Character style IDs that indicate inline code.\n */\nexport const INLINE_CODE_STYLE_IDS = new Set(['CodeChar', 'HTMLCodeChar', 'VerbatimChar']);\n\n// ============================================\n// Numbering Formats\n// ============================================\n\n/**\n * Word numbering format values that indicate an unordered (bullet) list.\n */\nexport const BULLET_NUM_FORMATS = new Set(['bullet', 'none']);\n\n/**\n * Word numbering format values that indicate an ordered (numbered) list.\n */\nexport const ORDERED_NUM_FORMATS = new Set([\n 'decimal',\n 'upperRoman',\n 'lowerRoman',\n 'upperLetter',\n 'lowerLetter',\n 'ordinal',\n 'cardinalText',\n 'ordinalText',\n]);\n\n// ============================================\n// Default Formatting\n// ============================================\n\n/** Default font family for document body text */\nexport const DEFAULT_FONT = 'Calibri';\n\n/** Default font family for headings */\nexport const DEFAULT_HEADING_FONT = 'Calibri Light';\n\n/** Default font size in half-points (22 = 11pt) */\nexport const DEFAULT_FONT_SIZE_HALF_POINTS = 22;\n\n/** Default code font */\nexport const DEFAULT_CODE_FONT = 'Consolas';\n\n/** Default code font size in half-points (20 = 10pt) */\nexport const DEFAULT_CODE_FONT_SIZE = 20;\n\n/**\n * Heading font sizes in half-points (Word default sizes).\n */\nexport const HEADING_FONT_SIZES: Record<number, number> = {\n 1: 32, // 16pt\n 2: 26, // 13pt\n 3: 24, // 12pt\n 4: 22, // 11pt\n 5: 22, // 11pt\n 6: 22, // 11pt\n};\n\n/** Hyperlink color (standard Word blue) */\nexport const HYPERLINK_COLOR = '0563C1';\n\n// ============================================\n// EMU (English Metric Units) Helpers\n// ============================================\n\n/**\n * Convert inches to EMUs (English Metric Units).\n * 1 inch = 914400 EMUs.\n */\nexport function inchesToEmu(inches: number): number {\n return Math.round(inches * 914400);\n}\n\n/**\n * Convert points to half-points (Word's internal unit for font sizes).\n */\nexport function pointsToHalfPoints(points: number): number {\n return Math.round(points * 2);\n}\n\n/**\n * Convert points to twentieths of a point (twips), used for spacing/margins.\n * 1 point = 20 twips.\n */\nexport function pointsToTwips(points: number): number {\n return Math.round(points * 20);\n}\n","/**\n * DOCX Import\n *\n * Parses a .docx file (Office Open XML WordprocessingML) and converts\n * its content into a squisq MarkdownDocument (or Doc).\n *\n * Uses JSZip + DOMParser to read the archive and parse the XML — no\n * third-party docx library. Handles headings, paragraphs, inline\n * formatting (bold, italic, strikethrough), hyperlinks, lists, tables,\n * blockquotes, code blocks, images, and footnotes.\n *\n * @example\n * ```ts\n * import { docxToMarkdownDoc } from '@bendyline/squisq-formats/docx';\n *\n * const response = await fetch('document.docx');\n * const data = await response.arrayBuffer();\n * const doc = await docxToMarkdownDoc(data);\n * ```\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { markdownToDoc } 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 MarkdownText,\n MarkdownEmphasis,\n MarkdownStrong,\n MarkdownStrikethrough,\n MarkdownInlineCode,\n MarkdownLink,\n MarkdownImage,\n MarkdownBreak,\n MarkdownFootnoteReference,\n MarkdownFootnoteDefinition,\n} from '@bendyline/squisq/markdown';\n\nimport { openPackage, getPartXml, getPartRelationships } from '../ooxml/reader.js';\nimport type { OoxmlPackage, Relationship } from '../ooxml/types.js';\nimport { NS_WML, NS_R } from '../ooxml/namespaces.js';\nimport {\n HEADING_STYLE_MAP,\n QUOTE_STYLE_IDS,\n CODE_STYLE_IDS,\n INLINE_CODE_STYLE_IDS,\n BULLET_NUM_FORMATS,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Options for DOCX import.\n */\nexport interface DocxImportOptions {\n /**\n * Whether to extract embedded images as base64 data URIs.\n * When false, images are represented as `[Image]` placeholders.\n * Default: false\n */\n extractImages?: boolean;\n}\n\n/**\n * Convert a .docx file to a MarkdownDocument.\n *\n * @param data - The raw .docx file as ArrayBuffer or Blob\n * @param options - Import options\n * @returns A MarkdownDocument representing the document content\n */\nexport async function docxToMarkdownDoc(\n data: ArrayBuffer | Blob,\n options: DocxImportOptions = {},\n): Promise<MarkdownDocument> {\n const pkg = await openPackage(data);\n const ctx = await buildImportContext(pkg, options);\n\n const documentXml = await getPartXml(pkg, 'word/document.xml');\n if (!documentXml) {\n return { type: 'document', children: [] };\n }\n\n const body = getFirstElement(documentXml, 'body');\n if (!body) {\n return { type: 'document', children: [] };\n }\n\n const blocks = await convertBody(body, ctx);\n\n return { type: 'document', children: blocks };\n}\n\n/**\n * Convert a .docx file to a squisq Doc.\n *\n * Convenience wrapper: DOCX → MarkdownDocument → Doc.\n *\n * @param data - The raw .docx file as ArrayBuffer or Blob\n * @param options - Import options\n * @returns A squisq Doc\n */\nexport async function docxToDoc(\n data: ArrayBuffer | Blob,\n options: DocxImportOptions = {},\n): Promise<Doc> {\n const markdownDoc = await docxToMarkdownDoc(data, options);\n return markdownToDoc(markdownDoc);\n}\n\n// ============================================\n// Import Context\n// ============================================\n\ninterface ImportContext {\n /** Style ID → heading depth mapping (from styles.xml) */\n headingStyles: Map<string, number>;\n /** Style IDs that represent blockquotes */\n quoteStyles: Set<string>;\n /** Style IDs that represent code blocks */\n codeStyles: Set<string>;\n /** Character style IDs that represent inline code */\n inlineCodeStyles: Set<string>;\n /** Document relationship map: rId → Relationship */\n documentRels: Map<string, Relationship>;\n /** Numbering definitions: numId → { levels: Map<ilvl, isOrdered> } */\n numbering: Map<string, NumberingInfo>;\n /** Footnote bodies: footnoteId → Element */\n footnotes: Map<string, Element>;\n /** Reference to the OOXML package (for extracting images) */\n pkg: OoxmlPackage;\n /** Import options */\n options: DocxImportOptions;\n}\n\ninterface NumberingInfo {\n levels: Map<number, boolean>; // ilvl → isOrdered\n}\n\nasync function buildImportContext(\n pkg: OoxmlPackage,\n options: DocxImportOptions,\n): Promise<ImportContext> {\n const ctx: ImportContext = {\n headingStyles: new Map(),\n quoteStyles: new Set(),\n codeStyles: new Set(),\n inlineCodeStyles: new Set(),\n documentRels: new Map(),\n numbering: new Map(),\n footnotes: new Map(),\n pkg,\n options,\n };\n\n // Initialize with built-in defaults\n for (const [id, depth] of Object.entries(HEADING_STYLE_MAP)) {\n ctx.headingStyles.set(id, depth);\n }\n for (const id of QUOTE_STYLE_IDS) {\n ctx.quoteStyles.add(id);\n }\n for (const id of CODE_STYLE_IDS) {\n ctx.codeStyles.add(id);\n }\n for (const id of INLINE_CODE_STYLE_IDS) {\n ctx.inlineCodeStyles.add(id);\n }\n\n // Parse styles.xml for custom heading/quote/code mappings\n await parseStyles(pkg, ctx);\n\n // Parse document relationships\n const rels = await getPartRelationships(pkg, 'word/document.xml');\n for (const rel of rels) {\n ctx.documentRels.set(rel.id, rel);\n }\n\n // Parse numbering.xml\n await parseNumbering(pkg, ctx);\n\n // Parse footnotes.xml\n await parseFootnotes(pkg, ctx);\n\n return ctx;\n}\n\n// ============================================\n// Styles Parsing\n// ============================================\n\nasync function parseStyles(pkg: OoxmlPackage, ctx: ImportContext): Promise<void> {\n const doc = await getPartXml(pkg, 'word/styles.xml');\n if (!doc) return;\n\n const styles = doc.getElementsByTagNameNS(NS_WML, 'style');\n // Fallback for documents that don't use namespace prefixes properly\n const stylesList = styles.length > 0 ? styles : doc.getElementsByTagName('style');\n\n for (let i = 0; i < stylesList.length; i++) {\n const style = stylesList[i];\n const styleId = style.getAttributeNS(NS_WML, 'styleId') ?? style.getAttribute('w:styleId');\n if (!styleId) continue;\n\n const nameEl = getFirstChildElement(style, 'name');\n const styleName = nameEl?.getAttributeNS(NS_WML, 'val') ?? nameEl?.getAttribute('w:val') ?? '';\n\n // Check if this is a heading style by name\n const headingMatch = styleName.match(/^heading\\s+(\\d+)$/i);\n if (headingMatch) {\n const depth = parseInt(headingMatch[1], 10);\n if (depth >= 1 && depth <= 6) {\n ctx.headingStyles.set(styleId, depth);\n }\n }\n\n // Check pPr > outlineLvl for heading detection\n const pPr = getFirstChildElement(style, 'pPr');\n if (pPr) {\n const outlineLvl = getFirstChildElement(pPr, 'outlineLvl');\n if (outlineLvl) {\n const val = outlineLvl.getAttributeNS(NS_WML, 'val') ?? outlineLvl.getAttribute('w:val');\n if (val !== null) {\n const depth = parseInt(val, 10) + 1;\n if (depth >= 1 && depth <= 6) {\n ctx.headingStyles.set(styleId, depth);\n }\n }\n }\n }\n }\n}\n\n// ============================================\n// Numbering Parsing\n// ============================================\n\nasync function parseNumbering(pkg: OoxmlPackage, ctx: ImportContext): Promise<void> {\n const doc = await getPartXml(pkg, 'word/numbering.xml');\n if (!doc) return;\n\n // Parse abstract numbering definitions\n const abstractNums = new Map<string, Map<number, boolean>>(); // abstractNumId → levels(ilvl → isOrdered)\n\n const abstractNumEls = getAllElements(doc, 'abstractNum');\n for (const absNum of abstractNumEls) {\n const absId = getAttr(absNum, 'abstractNumId');\n if (!absId) continue;\n\n const levels = new Map<number, boolean>();\n const lvlEls = getAllChildElements(absNum, 'lvl');\n for (const lvl of lvlEls) {\n const ilvlStr = getAttr(lvl, 'ilvl');\n if (ilvlStr === null) continue;\n const ilvl = parseInt(ilvlStr, 10);\n\n const numFmtEl = getFirstChildElement(lvl, 'numFmt');\n const numFmt = numFmtEl ? getAttr(numFmtEl, 'val') : null;\n\n const isOrdered = numFmt !== null && !BULLET_NUM_FORMATS.has(numFmt);\n levels.set(ilvl, isOrdered);\n }\n\n abstractNums.set(absId, levels);\n }\n\n // Parse concrete num → abstractNum mappings\n const numEls = getAllElements(doc, 'num');\n for (const num of numEls) {\n const numId = getAttr(num, 'numId');\n if (!numId) continue;\n\n const abstractNumIdEl = getFirstChildElement(num, 'abstractNumId');\n const absId = abstractNumIdEl ? getAttr(abstractNumIdEl, 'val') : null;\n if (!absId) continue;\n\n const levels = abstractNums.get(absId);\n if (levels) {\n ctx.numbering.set(numId, { levels });\n }\n }\n}\n\n// ============================================\n// Footnotes Parsing\n// ============================================\n\nasync function parseFootnotes(pkg: OoxmlPackage, ctx: ImportContext): Promise<void> {\n const doc = await getPartXml(pkg, 'word/footnotes.xml');\n if (!doc) return;\n\n const footnoteEls = getAllElements(doc, 'footnote');\n for (const fn of footnoteEls) {\n const id = getAttr(fn, 'id');\n const type = getAttr(fn, 'type');\n // Skip separator and continuation separator footnotes\n if (!id || type === 'separator' || type === 'continuationSeparator') continue;\n ctx.footnotes.set(id, fn);\n }\n}\n\n// ============================================\n// Body Conversion\n// ============================================\n\nasync function convertBody(body: Element, ctx: ImportContext): Promise<MarkdownBlockNode[]> {\n const result: MarkdownBlockNode[] = [];\n const children = Array.from(body.children);\n\n let i = 0;\n while (i < children.length) {\n const el = children[i];\n const localName = el.localName;\n\n if (localName === 'p') {\n // Check if this is part of a list\n const numPr = getNumPr(el);\n if (numPr) {\n // Collect consecutive list paragraphs\n const { node, consumed } = await collectList(children, i, ctx);\n result.push(node);\n i += consumed;\n continue;\n }\n\n const block = await convertParagraph(el, ctx);\n if (block) {\n result.push(block);\n }\n i++;\n } else if (localName === 'tbl') {\n const table = await convertTable(el, ctx);\n if (table) result.push(table);\n i++;\n } else {\n // Skip unknown elements (sectPr, bookmarkStart, etc.)\n i++;\n }\n }\n\n // Append footnote definitions at the end\n const footnoteNodes = await convertFootnoteDefinitions(ctx);\n result.push(...footnoteNodes);\n\n return result;\n}\n\n// ============================================\n// Paragraph Conversion\n// ============================================\n\nasync function convertParagraph(\n el: Element,\n ctx: ImportContext,\n): Promise<MarkdownBlockNode | null> {\n const pPr = getFirstChildElement(el, 'pPr');\n const styleId = getParagraphStyleId(pPr);\n\n // Check for heading\n if (styleId && ctx.headingStyles.has(styleId)) {\n const depth = ctx.headingStyles.get(styleId)!;\n const inlines = await convertRuns(el, ctx);\n if (inlines.length === 0) return null;\n return {\n type: 'heading',\n depth: Math.min(Math.max(depth, 1), 6) as 1 | 2 | 3 | 4 | 5 | 6,\n children: inlines,\n } satisfies MarkdownHeading;\n }\n\n // Check for blockquote\n if (styleId && ctx.quoteStyles.has(styleId)) {\n const inlines = await convertRuns(el, ctx);\n if (inlines.length === 0) return null;\n const paragraph: MarkdownParagraph = { type: 'paragraph', children: inlines };\n return { type: 'blockquote', children: [paragraph] } satisfies MarkdownBlockquote;\n }\n\n // Check for code block\n if (styleId && ctx.codeStyles.has(styleId)) {\n const text = getElementTextContent(el);\n return { type: 'code', value: text } satisfies MarkdownCodeBlock;\n }\n\n // Regular paragraph\n const inlines = await convertRuns(el, ctx);\n if (inlines.length === 0) return null;\n return { type: 'paragraph', children: inlines } satisfies MarkdownParagraph;\n}\n\n// ============================================\n// Run (Inline) Conversion\n// ============================================\n\nasync function convertRuns(\n paragraphEl: Element,\n ctx: ImportContext,\n): Promise<MarkdownInlineNode[]> {\n const result: MarkdownInlineNode[] = [];\n const children = Array.from(paragraphEl.children);\n\n for (const child of children) {\n const localName = child.localName;\n\n if (localName === 'r') {\n const inlines = await convertRun(child, ctx);\n result.push(...inlines);\n } else if (localName === 'hyperlink') {\n const link = await convertHyperlink(child, ctx);\n if (link) result.push(link);\n }\n // Skip pPr, bookmarkStart, bookmarkEnd, etc.\n }\n\n return mergeAdjacentText(result);\n}\n\nasync function convertRun(runEl: Element, ctx: ImportContext): Promise<MarkdownInlineNode[]> {\n const result: MarkdownInlineNode[] = [];\n const rPr = getFirstChildElement(runEl, 'rPr');\n const format = parseRunFormat(rPr, ctx);\n\n for (const child of Array.from(runEl.children)) {\n const localName = child.localName;\n\n if (localName === 't') {\n const text = child.textContent ?? '';\n if (!text) continue;\n\n if (format.code) {\n result.push({ type: 'inlineCode', value: text } satisfies MarkdownInlineCode);\n } else {\n let node: MarkdownInlineNode = { type: 'text', value: text } satisfies MarkdownText;\n if (format.strike) {\n node = { type: 'delete', children: [node] } satisfies MarkdownStrikethrough;\n }\n if (format.italic) {\n node = { type: 'emphasis', children: [node] } satisfies MarkdownEmphasis;\n }\n if (format.bold) {\n node = { type: 'strong', children: [node] } satisfies MarkdownStrong;\n }\n result.push(node);\n }\n } else if (localName === 'br') {\n result.push({ type: 'break' } satisfies MarkdownBreak);\n } else if (localName === 'footnoteReference') {\n const fnId = getAttr(child, 'id');\n if (fnId && fnId !== '0' && fnId !== '-1') {\n result.push({\n type: 'footnoteReference',\n identifier: `fn${fnId}`,\n } satisfies MarkdownFootnoteReference);\n }\n } else if (localName === 'drawing' || localName === 'pict') {\n const img = await extractImage(child, ctx);\n if (img) result.push(img);\n }\n }\n\n return result;\n}\n\ninterface RunFormat {\n bold: boolean;\n italic: boolean;\n strike: boolean;\n code: boolean;\n}\n\nfunction parseRunFormat(rPr: Element | null, ctx: ImportContext): RunFormat {\n if (!rPr) return { bold: false, italic: false, strike: false, code: false };\n\n const bold = hasChildElement(rPr, 'b') && !isFalseToggle(getFirstChildElement(rPr, 'b')!);\n const italic = hasChildElement(rPr, 'i') && !isFalseToggle(getFirstChildElement(rPr, 'i')!);\n const strike =\n hasChildElement(rPr, 'strike') && !isFalseToggle(getFirstChildElement(rPr, 'strike')!);\n\n // Check for inline code via character style\n const rStyle = getFirstChildElement(rPr, 'rStyle');\n const charStyleId = rStyle ? getAttr(rStyle, 'val') : null;\n const isCodeStyle = charStyleId ? ctx.inlineCodeStyles.has(charStyleId) : false;\n\n // Check for monospace font as a code indicator\n const rFonts = getFirstChildElement(rPr, 'rFonts');\n const fontName = rFonts ? (getAttr(rFonts, 'ascii') ?? getAttr(rFonts, 'hAnsi') ?? '') : '';\n const isMonospace = /consolas|courier|mono/i.test(fontName);\n\n return { bold, italic, strike, code: isCodeStyle || isMonospace };\n}\n\nfunction isFalseToggle(el: Element): boolean {\n const val = getAttr(el, 'val');\n return val === '0' || val === 'false';\n}\n\n// ============================================\n// Hyperlink Conversion\n// ============================================\n\nasync function convertHyperlink(el: Element, ctx: ImportContext): Promise<MarkdownLink | null> {\n const rId = el.getAttributeNS(NS_R, 'id') ?? el.getAttribute('r:id');\n\n let url = '';\n if (rId) {\n const rel = ctx.documentRels.get(rId);\n if (rel) url = rel.target;\n }\n\n // Also check for w:anchor (internal bookmarks)\n if (!url) {\n const anchor = el.getAttributeNS(NS_WML, 'anchor') ?? el.getAttribute('w:anchor');\n if (anchor) url = `#${anchor}`;\n }\n\n const inlines: MarkdownInlineNode[] = [];\n for (const child of Array.from(el.children)) {\n if (child.localName === 'r') {\n inlines.push(...(await convertRun(child, ctx)));\n }\n }\n\n if (inlines.length === 0) return null;\n\n return {\n type: 'link',\n url,\n children: mergeAdjacentText(inlines),\n };\n}\n\n// ============================================\n// Image Extraction\n// ============================================\n\nasync function extractImage(_el: Element, _ctx: ImportContext): Promise<MarkdownImage | null> {\n // Image extraction is complex (DrawingML with blip references).\n // For v1, emit a placeholder. Full implementation requires:\n // 1. Find <a:blip r:embed=\"rId_\"/> in the drawing tree\n // 2. Resolve the rId to a media path\n // 3. Extract the binary data and encode as data URI\n // This is planned for a future enhancement.\n return {\n type: 'image',\n url: '',\n alt: 'Image',\n };\n}\n\n// ============================================\n// List Collection\n// ============================================\n\ninterface ListResult {\n node: MarkdownList;\n consumed: number;\n}\n\ninterface NumPrInfo {\n numId: string;\n ilvl: number;\n}\n\nasync function collectList(\n elements: Element[],\n startIdx: number,\n ctx: ImportContext,\n): Promise<ListResult> {\n const firstNumPr = getNumPr(elements[startIdx])!;\n const { numId } = firstNumPr;\n\n // Determine if ordered from numbering definition\n const numInfo = ctx.numbering.get(numId);\n const isOrdered = numInfo?.levels.get(0) ?? false;\n\n const items: MarkdownListItem[] = [];\n let consumed = 0;\n\n let i = startIdx;\n while (i < elements.length) {\n const el = elements[i];\n if (el.localName !== 'p') break;\n\n const numPr = getNumPr(el);\n if (!numPr || numPr.numId !== numId) break;\n\n // Convert this paragraph's inline content\n const inlines = await convertRuns(el, ctx);\n if (inlines.length > 0) {\n const paragraph: MarkdownParagraph = { type: 'paragraph', children: inlines };\n\n // Check if this is a nested list item\n if (numPr.ilvl > firstNumPr.ilvl) {\n // Collect nested list items\n const nested = await collectNestedList(elements, i, ctx, firstNumPr.ilvl);\n if (items.length > 0) {\n // Attach nested list to the last item\n const lastItem = items[items.length - 1];\n const nestedIsOrdered = numInfo?.levels.get(numPr.ilvl) ?? false;\n const nestedList: MarkdownList = {\n type: 'list',\n ordered: nestedIsOrdered,\n children: nested.items,\n };\n lastItem.children.push(nestedList);\n }\n i += nested.consumed;\n consumed += nested.consumed;\n continue;\n }\n\n items.push({\n type: 'listItem',\n children: [paragraph],\n });\n }\n\n i++;\n consumed++;\n }\n\n return {\n node: {\n type: 'list',\n ordered: isOrdered,\n children: items,\n },\n consumed,\n };\n}\n\ninterface NestedListResult {\n items: MarkdownListItem[];\n consumed: number;\n}\n\nasync function collectNestedList(\n elements: Element[],\n startIdx: number,\n ctx: ImportContext,\n parentIlvl: number,\n): Promise<NestedListResult> {\n const items: MarkdownListItem[] = [];\n let consumed = 0;\n\n let i = startIdx;\n while (i < elements.length) {\n const el = elements[i];\n if (el.localName !== 'p') break;\n\n const numPr = getNumPr(el);\n if (!numPr) break;\n if (numPr.ilvl <= parentIlvl) break;\n\n const inlines = await convertRuns(el, ctx);\n if (inlines.length > 0) {\n const paragraph: MarkdownParagraph = { type: 'paragraph', children: inlines };\n items.push({ type: 'listItem', children: [paragraph] });\n }\n\n i++;\n consumed++;\n }\n\n return { items, consumed };\n}\n\nfunction getNumPr(el: Element): NumPrInfo | null {\n const pPr = getFirstChildElement(el, 'pPr');\n if (!pPr) return null;\n\n const numPr = getFirstChildElement(pPr, 'numPr');\n if (!numPr) return null;\n\n const ilvlEl = getFirstChildElement(numPr, 'ilvl');\n const numIdEl = getFirstChildElement(numPr, 'numId');\n\n const ilvlVal = ilvlEl ? getAttr(ilvlEl, 'val') : null;\n const numIdVal = numIdEl ? getAttr(numIdEl, 'val') : null;\n\n if (!numIdVal || numIdVal === '0') return null; // numId 0 means \"no list\"\n\n return {\n numId: numIdVal,\n ilvl: ilvlVal ? parseInt(ilvlVal, 10) : 0,\n };\n}\n\n// ============================================\n// Table Conversion\n// ============================================\n\nasync function convertTable(tblEl: Element, ctx: ImportContext): Promise<MarkdownTable | null> {\n const rows: MarkdownTableRow[] = [];\n\n const trEls = getAllChildElements(tblEl, 'tr');\n for (let ri = 0; ri < trEls.length; ri++) {\n const row = await convertTableRow(trEls[ri], ctx, ri === 0);\n rows.push(row);\n }\n\n if (rows.length === 0) return null;\n\n // If first row isn't explicitly a header, treat it as one anyway\n // (Markdown tables require a header row)\n return {\n type: 'table',\n children: rows,\n };\n}\n\nasync function convertTableRow(\n trEl: Element,\n ctx: ImportContext,\n isHeader: boolean,\n): Promise<MarkdownTableRow> {\n const cells: MarkdownTableCell[] = [];\n const tcEls = getAllChildElements(trEl, 'tc');\n\n for (const tc of tcEls) {\n const cell = await convertTableCell(tc, ctx, isHeader);\n cells.push(cell);\n }\n\n return { type: 'tableRow', children: cells };\n}\n\nasync function convertTableCell(\n tcEl: Element,\n ctx: ImportContext,\n isHeader: boolean,\n): Promise<MarkdownTableCell> {\n const inlines: MarkdownInlineNode[] = [];\n\n // A cell can contain multiple paragraphs; flatten them with breaks\n const paragraphs = getAllChildElements(tcEl, 'p');\n for (let pi = 0; pi < paragraphs.length; pi++) {\n if (pi > 0) {\n inlines.push({ type: 'break' } as MarkdownBreak);\n }\n const runs = await convertRuns(paragraphs[pi], ctx);\n inlines.push(...runs);\n }\n\n return {\n type: 'tableCell',\n isHeader,\n children: mergeAdjacentText(inlines),\n };\n}\n\n// ============================================\n// Footnote Definition Conversion\n// ============================================\n\nasync function convertFootnoteDefinitions(\n ctx: ImportContext,\n): Promise<MarkdownFootnoteDefinition[]> {\n const results: MarkdownFootnoteDefinition[] = [];\n\n for (const [id, el] of ctx.footnotes) {\n const children: MarkdownBlockNode[] = [];\n const paragraphs = getAllChildElements(el, 'p');\n\n for (const p of paragraphs) {\n const inlines = await convertRuns(p, ctx);\n if (inlines.length > 0) {\n children.push({\n type: 'paragraph',\n children: inlines,\n } satisfies MarkdownParagraph);\n }\n }\n\n if (children.length > 0) {\n results.push({\n type: 'footnoteDefinition',\n identifier: `fn${id}`,\n children,\n });\n }\n }\n\n return results;\n}\n\n// ============================================\n// XML Helper Utilities\n// ============================================\n\n/**\n * Get the first element child with a given local name.\n * Handles both namespaced and non-namespaced elements.\n */\nfunction getFirstChildElement(parent: Element | Document, localName: string): Element | null {\n for (const child of Array.from(parent.children ?? [])) {\n if (child.localName === localName) return child;\n }\n return null;\n}\n\n/**\n * Get all direct child elements with a given local name.\n */\nfunction getAllChildElements(parent: Element, localName: string): Element[] {\n const result: Element[] = [];\n for (const child of Array.from(parent.children)) {\n if (child.localName === localName) result.push(child);\n }\n return result;\n}\n\n/**\n * Get all elements with a given local name in the document.\n */\nfunction getAllElements(doc: Document | Element, localName: string): Element[] {\n // Try namespace-aware first\n const nsEls =\n 'getElementsByTagNameNS' in doc ? doc.getElementsByTagNameNS(NS_WML, localName) : null;\n if (nsEls && nsEls.length > 0) return Array.from(nsEls);\n\n // Fallback: try with w: prefix\n const prefixed = doc.getElementsByTagName(`w:${localName}`);\n if (prefixed.length > 0) return Array.from(prefixed);\n\n // Final fallback: bare name\n return Array.from(doc.getElementsByTagName(localName));\n}\n\n/**\n * Get the first element with given local name in the document or subtree.\n */\nfunction getFirstElement(doc: Document | Element, localName: string): Element | null {\n const els = getAllElements(doc, localName);\n return els.length > 0 ? els[0] : null;\n}\n\n/**\n * Get a w:-prefixed attribute, trying namespace-aware first then fallback.\n */\nfunction getAttr(el: Element, localName: string): string | null {\n return el.getAttributeNS(NS_WML, localName) || el.getAttribute(`w:${localName}`) || null;\n}\n\n/**\n * Check if an element has a direct child with the given local name.\n */\nfunction hasChildElement(parent: Element, localName: string): boolean {\n return getFirstChildElement(parent, localName) !== null;\n}\n\n/**\n * Get the paragraph style ID from a pPr element.\n */\nfunction getParagraphStyleId(pPr: Element | null): string | null {\n if (!pPr) return null;\n const pStyle = getFirstChildElement(pPr, 'pStyle');\n if (!pStyle) return null;\n return getAttr(pStyle, 'val');\n}\n\n/**\n * Get all text content from an element (concatenating all w:t descendants).\n */\nfunction getElementTextContent(el: Element): string {\n const parts: string[] = [];\n\n function walk(node: Element): void {\n if (node.localName === 't') {\n parts.push(node.textContent ?? '');\n }\n for (const child of Array.from(node.children)) {\n walk(child);\n }\n }\n\n walk(el);\n return parts.join('');\n}\n\n/**\n * Merge adjacent MarkdownText 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 (node.type === 'text' && prev?.type === 'text') {\n // Merge into previous text node\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":["convertParagraph","convertTable","inlines","convertTableRow","convertTableCell"]}
|
|
@@ -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 } from '@bendyline/squisq/schemas';\nimport { docToMarkdown } 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';\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\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);\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 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}\n\nasync function createExportContext(\n pdfDoc: PDFDocument,\n options: PdfExportOptions,\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 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 };\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 ?? COLOR_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: COLOR_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: COLOR_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: COLOR_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 ?? COLOR_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: COLOR_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(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_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(COLOR_LINK.r, COLOR_LINK.g, COLOR_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(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b),\n });\n ctx.y -= lineH + PARAGRAPH_SPACING;\n }\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 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} from '@bendyline/squisq/markdown';\n\nimport {\n DEFAULT_FONT_SIZE,\n IMPORT_HEADING_MIN_SIZE,\n IMPORT_HEADING_SIZE_RANGES,\n IMPORT_PARAGRAPH_GAP,\n IMPORT_BULLET_CHARS,\n IMPORT_ORDERED_PREFIX,\n IMPORT_COLUMN_TOLERANCE,\n IMPORT_TABLE_MIN_ROWS,\n IMPORT_URL_PATTERN,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Options for PDF import.\n */\nexport interface PdfImportOptions {\n /**\n * Hint for the body font size used in the PDF (in points).\n * Text items larger than this are considered headings.\n * If not provided, the importer detects the most common font size.\n */\n bodyFontSize?: number;\n\n /** Whether to detect tables from column-aligned text. Default: true. */\n detectTables?: boolean;\n\n /** Whether to detect code blocks from monospace fonts. Default: true. */\n detectCodeBlocks?: boolean;\n\n /** Whether to detect blockquotes from indentation. Default: true. */\n detectBlockquotes?: boolean;\n\n /** Whether to detect URLs in text and convert to links. Default: true. */\n detectLinks?: boolean;\n}\n\n/**\n * Convert a PDF file to a MarkdownDocument.\n *\n * Structure detection is heuristic — results are best-effort.\n *\n * @param data - The raw PDF file as ArrayBuffer, Uint8Array, or Blob\n * @param options - Import options\n * @returns A MarkdownDocument representing the detected content\n */\nexport async function pdfToMarkdownDoc(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<MarkdownDocument> {\n const bytes =\n data instanceof Blob\n ? new Uint8Array(await data.arrayBuffer())\n : data instanceof ArrayBuffer\n ? new Uint8Array(data)\n : data;\n\n const textLines = await extractTextLines(bytes);\n\n if (textLines.length === 0) {\n return { type: 'document', children: [] };\n }\n\n const bodySize = options.bodyFontSize ?? detectBodyFontSize(textLines);\n const blocks = classifyLines(textLines, bodySize, options);\n\n return { type: 'document', children: blocks };\n}\n\n/**\n * Convert a PDF file to a squisq Doc.\n *\n * Convenience wrapper: PDF → MarkdownDocument → Doc.\n */\nexport async function pdfToDoc(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<Doc> {\n const markdownDoc = await pdfToMarkdownDoc(data, options);\n return markdownToDoc(markdownDoc);\n}\n\n// ============================================\n// Internal Types\n// ============================================\n\n/** A single text item extracted from pdfjs. */\ninterface TextItem {\n str: string;\n x: number;\n y: number;\n width: number;\n height: number;\n /** Internal font ID from pdfjs (e.g. \"g_d0_f1\") */\n fontName: string;\n /** Resolved font family from pdfjs styles (e.g. \"sans-serif\", \"monospace\") */\n fontFamily: string;\n}\n\n/** A logical line: text items at roughly the same y-coordinate. */\ninterface TextLine {\n items: TextItem[];\n y: number;\n /** The page this line is on (0-based). */\n page: number;\n /** The predominant font size on this line. */\n fontSize: number;\n /** The predominant font family on this line. */\n fontFamily: string;\n /** The predominant font ID on this line (may contain bold/italic hints for embedded fonts). */\n fontName: string;\n /** The minimum x position (left edge). */\n minX: number;\n /** Full concatenated text. */\n text: string;\n}\n\n// ============================================\n// PDF Text Extraction (pdfjs-dist)\n// ============================================\n\n/**\n * Configure the pdfjs-dist PDF worker source URL.\n *\n * pdfjs-dist requires a worker for PDF parsing. In the **browser**, bundlers\n * (Vite, webpack) typically handle this automatically, or you can point to a\n * CDN-hosted worker script. In **Node.js / SSR / test** environments, call\n * this with a `file://` URL to the worker module **before** any import call.\n *\n * @example\n * ```ts\n * // Browser — CDN\n * configurePdfWorker('https://cdn.jsdelivr.net/npm/pdfjs-dist@4/legacy/build/pdf.worker.min.mjs');\n *\n * // Node / vitest — file URL\n * import { pathToFileURL } from 'url';\n * configurePdfWorker(pathToFileURL(require.resolve('pdfjs-dist/legacy/build/pdf.worker.mjs')).href);\n * ```\n */\nexport function configurePdfWorker(workerSrc: string): void {\n _workerSrc = workerSrc;\n}\n\n/** Module-level storage for the worker source URL. */\nlet _workerSrc: string | undefined;\n\n/** Minimal typed surface of the pdfjs-dist library used by the import path. */\ninterface PdfjsLib {\n GlobalWorkerOptions?: { workerSrc?: string };\n getDocument(params: { data: Uint8Array; isEvalSupported?: boolean; useSystemFonts?: boolean }): {\n promise: Promise<PdfjsDocument>;\n };\n}\n\ninterface PdfjsDocument {\n numPages: number;\n getPage(pageNum: number): Promise<PdfjsPage>;\n}\n\ninterface PdfjsPage {\n getTextContent(): Promise<{\n items: Array<{\n str: string;\n transform: number[];\n height: number;\n width?: number;\n fontName?: string;\n }>;\n styles?: Record<string, { fontFamily?: string }>;\n }>;\n}\n\nasync function applyWorkerConfig(pdfjsLib: PdfjsLib): Promise<void> {\n if (!pdfjsLib.GlobalWorkerOptions) return;\n if (pdfjsLib.GlobalWorkerOptions.workerSrc) return;\n\n if (_workerSrc) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = _workerSrc;\n }\n // If no workerSrc is set, pdfjs-dist's legacy build will attempt its\n // built-in fake-worker fallback. In browsers this usually works; in\n // Node.js the caller must have called configurePdfWorker() first.\n}\n\nasync function extractTextLines(data: Uint8Array): Promise<TextLine[]> {\n // Dynamic import — the legacy build bundles a fake-worker fallback\n // that avoids a real Web Worker in environments that don't support it.\n let pdfjsLib: PdfjsLib;\n try {\n pdfjsLib = (await import('pdfjs-dist/legacy/build/pdf.mjs')) as unknown as PdfjsLib;\n } catch {\n pdfjsLib = (await import('pdfjs-dist')) as unknown as PdfjsLib;\n }\n\n await applyWorkerConfig(pdfjsLib);\n\n const loadingTask = pdfjsLib.getDocument({\n data,\n isEvalSupported: false,\n useSystemFonts: true,\n });\n\n const pdf = await loadingTask.promise;\n const allLines: TextLine[] = [];\n\n for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {\n const page = await pdf.getPage(pageNum);\n const content = await page.getTextContent();\n\n // Build a fontName → fontFamily lookup from pdfjs styles\n const styleMap = content.styles || {};\n\n // Group text items into lines by y-coordinate\n const items: TextItem[] = [];\n for (const item of content.items) {\n if (!item.str || item.str.trim().length === 0) continue;\n const transform = item.transform || [1, 0, 0, 1, 0, 0];\n const x = transform[4];\n const y = transform[5];\n const height = Math.abs(transform[3]) || item.height || 12;\n const width = item.width || 0;\n const fontName = item.fontName || '';\n const fontFamily = styleMap[fontName]?.fontFamily || '';\n items.push({ str: item.str, x, y, width, height, fontName, fontFamily });\n }\n\n // Group into lines (items within 2pt of same y are same line)\n const lineMap = new Map<number, TextItem[]>();\n for (const item of items) {\n const roundedY = Math.round(item.y * 2) / 2;\n let foundKey: number | undefined;\n for (const key of lineMap.keys()) {\n if (Math.abs(key - roundedY) < 2) {\n foundKey = key;\n break;\n }\n }\n if (foundKey !== undefined) {\n lineMap.get(foundKey)!.push(item);\n } else {\n lineMap.set(roundedY, [item]);\n }\n }\n\n // Sort lines top-to-bottom (highest y first), items left-to-right\n const sortedKeys = [...lineMap.keys()].sort((a, b) => b - a);\n for (const key of sortedKeys) {\n const lineItems = lineMap.get(key)!.sort((a, b) => a.x - b.x);\n\n const fontSizes = lineItems.map((i) => i.height);\n const fontSize = mode(fontSizes) || 12;\n const fontFamilies = lineItems.map((i) => i.fontFamily);\n const fontFamily = modeStr(fontFamilies) || '';\n const fontNames = lineItems.map((i) => i.fontName);\n const fontName = modeStr(fontNames) || '';\n const minX = Math.min(...lineItems.map((i) => i.x));\n const text = lineItems.map((i) => i.str).join(' ');\n\n allLines.push({\n items: lineItems,\n y: key,\n page: pageNum - 1,\n fontSize,\n fontFamily,\n fontName,\n minX,\n text,\n });\n }\n }\n\n return allLines;\n}\n\n// ============================================\n// Font Size Detection\n// ============================================\n\nfunction detectBodyFontSize(lines: TextLine[]): number {\n const sizes = lines.map((l) => Math.round(l.fontSize * 2) / 2);\n return mode(sizes) || DEFAULT_FONT_SIZE;\n}\n\nfunction mode(arr: number[]): number {\n const freq = new Map<number, number>();\n for (const v of arr) freq.set(v, (freq.get(v) || 0) + 1);\n let maxCount = 0;\n let maxVal = 0;\n for (const [v, c] of freq) {\n if (c > maxCount) {\n maxCount = c;\n maxVal = v;\n }\n }\n return maxVal;\n}\n\nfunction modeStr(arr: string[]): string {\n const freq = new Map<string, number>();\n for (const v of arr) freq.set(v, (freq.get(v) || 0) + 1);\n let maxCount = 0;\n let maxVal = '';\n for (const [v, c] of freq) {\n if (c > maxCount) {\n maxCount = c;\n maxVal = v;\n }\n }\n return maxVal;\n}\n\n// ============================================\n// Line Classification → MarkdownBlockNode[]\n// ============================================\n\nfunction classifyLines(\n lines: TextLine[],\n bodySize: number,\n options: PdfImportOptions,\n): MarkdownBlockNode[] {\n const blocks: MarkdownBlockNode[] = [];\n const detectTables = options.detectTables !== false;\n const detectCodeBlocks = options.detectCodeBlocks !== false;\n const detectBlockquotes = options.detectBlockquotes !== false;\n const _detectLinks = options.detectLinks !== false;\n\n // Determine typical left margin (most common minX)\n const leftMargins = lines.map((l) => Math.round(l.minX));\n const typicalLeftMargin = mode(leftMargins) || 72;\n\n let i = 0;\n while (i < lines.length) {\n const line = lines[i];\n\n // --- Heading detection ---\n if (line.fontSize >= IMPORT_HEADING_MIN_SIZE && line.fontSize > bodySize + 1) {\n const depth = sizeToHeadingDepth(line.fontSize);\n blocks.push({\n type: 'heading',\n depth,\n children: buildInlineNodes(line, options),\n } as MarkdownHeading);\n i++;\n continue;\n }\n\n // --- Code block detection (monospace font runs) ---\n if (detectCodeBlocks && isMonospaceLine(line)) {\n const codeLines: string[] = [];\n while (i < lines.length && isMonospaceLine(lines[i])) {\n codeLines.push(lines[i].text);\n i++;\n }\n blocks.push({\n type: 'code',\n value: codeLines.join('\\n'),\n } as MarkdownCodeBlock);\n continue;\n }\n\n // --- Table detection (column-aligned consecutive lines) ---\n if (detectTables && i + 1 < lines.length) {\n const tableLines = tryDetectTable(lines, i, typicalLeftMargin);\n if (tableLines > 0) {\n const table = buildTable(lines.slice(i, i + tableLines), options);\n if (table) {\n blocks.push(table);\n i += tableLines;\n continue;\n }\n }\n }\n\n // --- List detection ---\n const bulletMatch = tryMatchBullet(line.text);\n const orderedMatch = line.text.match(IMPORT_ORDERED_PREFIX);\n if (bulletMatch || orderedMatch) {\n const listResult = consumeList(lines, i, typicalLeftMargin, bodySize, options);\n blocks.push(listResult.list);\n i = listResult.nextIndex;\n continue;\n }\n\n // --- Blockquote detection (indented text) ---\n if (detectBlockquotes && line.minX > typicalLeftMargin + 20) {\n const quoteLines: TextLine[] = [];\n while (\n i < lines.length &&\n lines[i].minX > typicalLeftMargin + 20 &&\n !isMonospaceLine(lines[i]) &&\n lines[i].fontSize <= bodySize + 1\n ) {\n quoteLines.push(lines[i]);\n i++;\n }\n const quoteBlocks: MarkdownBlockNode[] = quoteLines.map(\n (ql) =>\n ({\n type: 'paragraph',\n children: buildInlineNodes(ql, options),\n }) as MarkdownParagraph,\n );\n blocks.push({\n type: 'blockquote',\n children: quoteBlocks,\n } as MarkdownBlockquote);\n continue;\n }\n\n // --- Regular paragraph ---\n // Merge consecutive body-sized lines on the same page with small y-gaps\n const paraLines: TextLine[] = [line];\n i++;\n while (i < lines.length) {\n const next = lines[i];\n // Same page, same-ish font size, close y (within line-height gap), not bullet/heading\n if (\n next.page === line.page &&\n Math.abs(next.fontSize - bodySize) <= 1 &&\n !isMonospaceLine(next) &&\n next.minX <= typicalLeftMargin + 15 &&\n !tryMatchBullet(next.text) &&\n !next.text.match(IMPORT_ORDERED_PREFIX)\n ) {\n // Check y-gap: lines are sorted top-to-bottom so y decreases\n const yGap = paraLines[paraLines.length - 1].y - next.y;\n const lineHeight = bodySize * 1.6;\n if (yGap > 0 && yGap < lineHeight + IMPORT_PARAGRAPH_GAP) {\n paraLines.push(next);\n i++;\n } else {\n break;\n }\n } else {\n break;\n }\n }\n\n // Build paragraph from merged lines\n const allInlines: MarkdownInlineNode[] = [];\n for (let j = 0; j < paraLines.length; j++) {\n if (j > 0) {\n allInlines.push({ type: 'text', value: ' ' } as MarkdownText);\n }\n allInlines.push(...buildInlineNodes(paraLines[j], options));\n }\n\n if (allInlines.length > 0) {\n blocks.push({\n type: 'paragraph',\n children: mergeAdjacentText(allInlines),\n } as MarkdownParagraph);\n }\n }\n\n return blocks;\n}\n\n// ============================================\n// Heading Depth Mapping\n// ============================================\n\nfunction sizeToHeadingDepth(fontSize: number): 1 | 2 | 3 | 4 | 5 | 6 {\n for (const range of IMPORT_HEADING_SIZE_RANGES) {\n if (fontSize >= range.min) return range.depth as 1 | 2 | 3 | 4 | 5 | 6;\n }\n return 6;\n}\n\n// ============================================\n// Font Heuristics\n// ============================================\n\n/**\n * Check if a line is predominantly monospace.\n * Uses the resolved fontFamily from pdfjs styles first,\n * falls back to fontName pattern matching for embedded fonts.\n */\nfunction isMonospaceLine(line: TextLine): boolean {\n return isMonospaceFamily(line.fontFamily) || isMonospaceName(line.fontName);\n}\n\n/**\n * Check if a text item is monospace.\n */\nfunction isMonospaceItem(item: TextItem): boolean {\n return isMonospaceFamily(item.fontFamily) || isMonospaceName(item.fontName);\n}\n\nfunction isMonospaceFamily(fontFamily: string): boolean {\n const lower = fontFamily.toLowerCase();\n return lower === 'monospace' || lower.includes('monospace');\n}\n\nfunction isMonospaceName(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return (\n lower.includes('courier') ||\n lower.includes('mono') ||\n lower.includes('consolas') ||\n lower.includes('menlo') ||\n lower.includes('inconsolata') ||\n lower.includes('firacode') ||\n lower.includes('source code') ||\n lower.includes('dejavu sans mono')\n );\n}\n\nfunction isBoldFont(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return lower.includes('bold') || lower.includes('black') || lower.includes('heavy');\n}\n\nfunction isItalicFont(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return lower.includes('italic') || lower.includes('oblique') || lower.includes('slanted');\n}\n\n// ============================================\n// Inline Node Construction\n// ============================================\n\nfunction buildInlineNodes(line: TextLine, options: PdfImportOptions): MarkdownInlineNode[] {\n const nodes: MarkdownInlineNode[] = [];\n const detectLinksOpt = options.detectLinks !== false;\n\n for (const item of line.items) {\n const text = item.str;\n if (!text || text.trim().length === 0) continue;\n\n const bold = isBoldFont(item.fontName);\n const italic = isItalicFont(item.fontName);\n const mono = isMonospaceItem(item);\n\n let inlineNodes: MarkdownInlineNode[];\n\n if (mono) {\n inlineNodes = [{ type: 'inlineCode', value: text } as MarkdownInlineCode];\n } else if (detectLinksOpt) {\n inlineNodes = splitTextWithLinks(text);\n } else {\n inlineNodes = [{ type: 'text', value: text } as MarkdownText];\n }\n\n // Wrap in formatting\n for (const node of inlineNodes) {\n let wrapped: MarkdownInlineNode = node;\n if (italic) {\n wrapped = { type: 'emphasis', children: [wrapped] } as MarkdownEmphasis;\n }\n if (bold) {\n wrapped = { type: 'strong', children: [wrapped] } as MarkdownStrong;\n }\n nodes.push(wrapped);\n }\n }\n\n return nodes;\n}\n\n/**\n * Split a text string into text nodes and link nodes wherever\n * URL patterns are found.\n */\nfunction splitTextWithLinks(text: string): MarkdownInlineNode[] {\n const nodes: MarkdownInlineNode[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n IMPORT_URL_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = IMPORT_URL_PATTERN.exec(text)) !== null) {\n // Text before URL\n if (match.index > lastIndex) {\n nodes.push({ type: 'text', value: text.slice(lastIndex, match.index) } as MarkdownText);\n }\n // URL as link\n const url = match[0];\n nodes.push({\n type: 'link',\n url,\n children: [{ type: 'text', value: url } as MarkdownText],\n } as MarkdownLink);\n lastIndex = match.index + url.length;\n }\n\n // Remaining text\n if (lastIndex < text.length) {\n nodes.push({ type: 'text', value: text.slice(lastIndex) } as MarkdownText);\n }\n\n return nodes.length > 0 ? nodes : [{ type: 'text', value: text } as MarkdownText];\n}\n\n// ============================================\n// List Detection\n// ============================================\n\nfunction tryMatchBullet(text: string): boolean {\n if (text.length === 0) return false;\n return IMPORT_BULLET_CHARS.has(text[0]) || IMPORT_BULLET_CHARS.has(text.trimStart()[0]);\n}\n\nfunction stripBullet(text: string): string {\n const trimmed = text.trimStart();\n if (IMPORT_BULLET_CHARS.has(trimmed[0])) {\n return trimmed.slice(1).trimStart();\n }\n return text;\n}\n\nfunction stripOrderedPrefix(text: string): string {\n return text.replace(IMPORT_ORDERED_PREFIX, '');\n}\n\ninterface ListResult {\n list: MarkdownList;\n nextIndex: number;\n}\n\nfunction consumeList(\n lines: TextLine[],\n startIdx: number,\n _typicalLeftMargin: number,\n _bodySize: number,\n _options: PdfImportOptions,\n): ListResult {\n const firstLine = lines[startIdx];\n const isOrdered = !!firstLine.text.match(IMPORT_ORDERED_PREFIX);\n const items: MarkdownListItem[] = [];\n let i = startIdx;\n\n while (i < lines.length) {\n const line = lines[i];\n const isBullet = tryMatchBullet(line.text);\n const isOrd = !!line.text.match(IMPORT_ORDERED_PREFIX);\n\n if (!isBullet && !isOrd) break;\n // All items in one list should be same type\n if (isOrdered && !isOrd) break;\n if (!isOrdered && !isBullet) break;\n\n const cleanText = isOrdered ? stripOrderedPrefix(line.text) : stripBullet(line.text);\n const para: MarkdownParagraph = {\n type: 'paragraph',\n children: splitTextWithLinks(cleanText),\n };\n items.push({\n type: 'listItem',\n children: [para],\n } as MarkdownListItem);\n i++;\n }\n\n return {\n list: {\n type: 'list',\n ordered: isOrdered,\n children: items,\n } as MarkdownList,\n nextIndex: i,\n };\n}\n\n// ============================================\n// Table Detection\n// ============================================\n\n/**\n * Look ahead from index `start` and return the number of consecutive\n * lines that form an aligned table, or 0 if no table detected.\n */\nfunction tryDetectTable(lines: TextLine[], start: number, _typicalLeftMargin: number): number {\n // A table needs multiple items per line (columns) on consecutive lines\n // with roughly the same x-alignment pattern.\n\n const firstLine = lines[start];\n if (firstLine.items.length < 2) return 0;\n\n const cols = getColumnPositions(firstLine);\n if (cols.length < 2) return 0;\n\n let count = 1;\n for (let i = start + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.items.length < 2) break;\n\n // Check if this line's columns align with the first line's\n const lineCols = getColumnPositions(line);\n if (lineCols.length !== cols.length) break;\n\n let aligned = true;\n for (let c = 0; c < cols.length; c++) {\n if (Math.abs(lineCols[c] - cols[c]) > IMPORT_COLUMN_TOLERANCE) {\n aligned = false;\n break;\n }\n }\n if (!aligned) break;\n count++;\n }\n\n return count >= IMPORT_TABLE_MIN_ROWS ? count : 0;\n}\n\nfunction getColumnPositions(line: TextLine): number[] {\n // Cluster item x-positions\n const positions: number[] = [];\n for (const item of line.items) {\n const x = Math.round(item.x);\n // Check if this x is close to an existing column\n let found = false;\n for (const p of positions) {\n if (Math.abs(p - x) < IMPORT_COLUMN_TOLERANCE) {\n found = true;\n break;\n }\n }\n if (!found) positions.push(x);\n }\n return positions.sort((a, b) => a - b);\n}\n\nfunction buildTable(lines: TextLine[], _options: PdfImportOptions): MarkdownTable | null {\n if (lines.length === 0) return null;\n\n // Use the first line's column positions as anchors\n const cols = getColumnPositions(lines[0]);\n if (cols.length < 2) return null;\n\n const rows: MarkdownTableRow[] = [];\n\n for (let ri = 0; ri < lines.length; ri++) {\n const line = lines[ri];\n const cells: MarkdownTableCell[] = [];\n\n for (let ci = 0; ci < cols.length; ci++) {\n const colLeft = cols[ci] - IMPORT_COLUMN_TOLERANCE;\n const colRight = ci + 1 < cols.length ? cols[ci + 1] - IMPORT_COLUMN_TOLERANCE : Infinity;\n\n // Collect items in this column\n const cellItems = line.items.filter((item) => item.x >= colLeft && item.x < colRight);\n const text = cellItems\n .map((i) => i.str)\n .join(' ')\n .trim();\n\n cells.push({\n type: 'tableCell',\n isHeader: ri === 0,\n children: text.length > 0 ? [{ type: 'text', value: text } as MarkdownText] : [],\n } as MarkdownTableCell);\n }\n\n rows.push({\n type: 'tableRow',\n children: cells,\n } as MarkdownTableRow);\n }\n\n return {\n type: 'table',\n children: rows,\n } as MarkdownTable;\n}\n\n// ============================================\n// Text Merging\n// ============================================\n\n/**\n * Merge adjacent text nodes to reduce fragmentation.\n */\nfunction mergeAdjacentText(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {\n if (nodes.length <= 1) return nodes;\n\n const result: MarkdownInlineNode[] = [];\n for (const node of nodes) {\n const prev = result[result.length - 1];\n if (prev && prev.type === 'text' && node.type === 'text') {\n (prev as MarkdownText).value += (node as MarkdownText).value;\n } else {\n result.push(node);\n }\n }\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/html/index.ts","../src/html/imageUtils.ts","../src/html/htmlTemplate.ts"],"sourcesContent":["/**\n * HTML Export Module — @bendyline/squisq-formats/html\n *\n * Exports squisq documents as self-contained HTML files or ZIP archives.\n *\n * Two export modes:\n *\n * 1. **Single HTML** (`docToHtml`) — Everything embedded in one file:\n * - SquisqPlayer IIFE bundle inlined in `<script>`\n * - Images base64-encoded as data URIs\n * - Timer-based playback (no audio — too large for inline)\n *\n * 2. **ZIP Archive** (`docToHtmlZip`) — Multi-file package:\n * - `index.html` referencing external `squisq-player.js`\n * - `squisq-player.js` — the IIFE bundle\n * - `images/` — extracted image files\n * - `audio/` — optional audio segment files (enables full playback)\n *\n * @example\n * ```ts\n * import { PLAYER_BUNDLE } from '@bendyline/squisq-react/standalone-source';\n * import { docToHtml, docToHtmlZip } from '@bendyline/squisq-formats/html';\n *\n * // Single HTML file\n * const html = docToHtml(doc, { playerScript: PLAYER_BUNDLE, images });\n *\n * // ZIP archive\n * const zipBlob = await docToHtmlZip(doc, { playerScript: PLAYER_BUNDLE, images, audio });\n * ```\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport JSZip from 'jszip';\nimport {\n generateInlineHtml,\n generateExternalHtml,\n type HtmlExportOptions,\n} from './htmlTemplate.js';\nimport { extractFilename } from './imageUtils.js';\n\n// ── Public Types ───────────────────────────────────────────────────\n\nexport type { HtmlExportOptions };\n\nexport interface HtmlZipExportOptions extends HtmlExportOptions {\n /**\n * Map of audio segment identifiers to binary audio data.\n * Keys should match the audio segment `name` or `url` fields in the Doc.\n * When provided, audio files are included in the ZIP and full playback is enabled.\n */\n audio?: Map<string, ArrayBuffer>;\n}\n\n// ── Single HTML Export ─────────────────────────────────────────────\n\n/**\n * Export a Doc as a single, self-contained HTML file.\n *\n * The player JS is embedded inline, images are base64-encoded as data URIs,\n * and playback uses a timer (no audio) for compact file size.\n *\n * @param doc - The Doc to export\n * @param options - Export options (must include `playerScript`)\n * @returns Complete HTML document as a string\n *\n * @example\n * ```ts\n * const html = docToHtml(myDoc, {\n * playerScript: PLAYER_BUNDLE,\n * images: imageMap,\n * title: 'My Document',\n * mode: 'slideshow',\n * });\n * const blob = new Blob([html], { type: 'text/html' });\n * ```\n */\nexport function docToHtml(doc: Doc, options: HtmlExportOptions): string {\n return generateInlineHtml(doc, options);\n}\n\n// ── ZIP Archive Export ─────────────────────────────────────────────\n\n/**\n * Export a Doc as a ZIP archive containing HTML, JS, images, and optionally audio.\n *\n * The archive structure:\n * ```\n * document.zip\n * ├── index.html # HTML page referencing squisq-player.js\n * ├── squisq-player.js # Standalone IIFE bundle\n * ├── images/ # Extracted image files\n * │ ├── hero.jpg\n * │ └── ...\n * └── audio/ # Optional audio segment files\n * ├── intro.mp3\n * └── ...\n * ```\n *\n * @param doc - The Doc to export\n * @param options - Export options (must include `playerScript`)\n * @returns A Promise resolving to a ZIP Blob\n *\n * @example\n * ```ts\n * const blob = await docToHtmlZip(myDoc, {\n * playerScript: PLAYER_BUNDLE,\n * images: imageMap,\n * audio: audioMap,\n * });\n * // Trigger browser download\n * const url = URL.createObjectURL(blob);\n * ```\n */\nexport async function docToHtmlZip(doc: Doc, options: HtmlZipExportOptions): Promise<Blob> {\n const { playerScript, images, audio, mode = 'slideshow', title, autoPlay } = options;\n\n const zip = new JSZip();\n\n // 1. Add player JS as a separate file\n zip.file('squisq-player.js', playerScript);\n\n // 2. Add images to images/ folder and build path mapping\n const imagePathMap: Record<string, string> = {};\n if (images) {\n for (const [originalPath, buffer] of images.entries()) {\n const filename = extractFilename(originalPath);\n const zipPath = `images/${filename}`;\n zip.file(zipPath, buffer);\n imagePathMap[originalPath] = zipPath;\n }\n }\n\n // 3. Add audio to audio/ folder and build path mapping\n const audioPathMap: Record<string, string> = {};\n if (audio) {\n for (const [segmentKey, buffer] of audio.entries()) {\n const filename = extractFilename(segmentKey);\n // Ensure .mp3 extension\n const finalName = filename.includes('.') ? filename : `${filename}.mp3`;\n const zipPath = `audio/${finalName}`;\n zip.file(zipPath, buffer);\n audioPathMap[segmentKey] = zipPath;\n }\n }\n\n // 4. Generate HTML that references external files\n const html = generateExternalHtml(doc, {\n playerScriptPath: 'squisq-player.js',\n imagePathMap: Object.keys(imagePathMap).length > 0 ? imagePathMap : undefined,\n audioPathMap: Object.keys(audioPathMap).length > 0 ? audioPathMap : undefined,\n mode,\n title,\n autoPlay,\n });\n zip.file('index.html', html);\n\n // 5. Generate ZIP blob\n return zip.generateAsync({\n type: 'blob',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n });\n}\n\n// ── Re-exports ─────────────────────────────────────────────────────\n\nexport { collectImagePaths } from './htmlTemplate.js';\nexport { inferMimeType, arrayBufferToBase64DataUrl, extractFilename } from './imageUtils.js';\n","/**\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\n/** Map of file extensions to MIME types */\nconst MIME_MAP: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n ico: 'image/x-icon',\n bmp: 'image/bmp',\n avif: 'image/avif',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n mp4: 'video/mp4',\n webm: 'video/webm',\n};\n\n/**\n * Infer a MIME type from a filename's extension.\n * Returns 'application/octet-stream' for unknown types.\n */\nexport function inferMimeType(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase() ?? '';\n return MIME_MAP[ext] ?? 'application/octet-stream';\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 let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n return `data:${mimeType};base64,${base64}`;\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","/**\n * HTML Template Generation for SquisqPlayer Exports\n *\n * Generates complete, self-contained HTML documents that load the SquisqPlayer\n * IIFE bundle and render a Doc as either an interactive slideshow or a static\n * scrollable document.\n *\n * Two variants:\n * 1. **Inline** — All JS, CSS, and images are embedded in the HTML (single file).\n * 2. **External** — JS is referenced via `<script src>`, images via relative paths.\n */\n\nimport type { Doc, Layer, Block } from '@bendyline/squisq/schemas';\nimport { arrayBufferToBase64DataUrl, inferMimeType } from './imageUtils.js';\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface HtmlExportOptions {\n /** The IIFE player bundle source code (from @bendyline/squisq-react/standalone-source) */\n playerScript: string;\n\n /**\n * Map of relative image paths (as they appear in the Doc) to binary image data.\n * For inline HTML export, these are converted to base64 data URIs.\n * For ZIP export, these are written as separate files.\n */\n images?: Map<string, ArrayBuffer>;\n\n /**\n * Map of audio segment identifiers to binary audio data.\n * Keys should match the audio segment `name` or `url` fields in the Doc.\n * Only used in ZIP exports — single HTML uses timer-based playback.\n */\n audio?: Map<string, ArrayBuffer>;\n\n /** Rendering mode: 'slideshow' (interactive, default) or 'static' (scrollable) */\n mode?: 'slideshow' | 'static';\n\n /** HTML page title (default: 'Squisq Document') */\n title?: string;\n\n /** Auto-play slideshow on load (default: false) */\n autoPlay?: boolean;\n}\n\n// ── Image Path Collection ──────────────────────────────────────────\n\n/**\n * Collect all relative image paths referenced in a Doc's layers and template blocks.\n * Returns a Set of unique relative paths that need to be resolved.\n */\nexport function collectImagePaths(doc: Doc): Set<string> {\n const paths = new Set<string>();\n\n function addIfRelative(src: string | undefined) {\n if (\n !src ||\n src.startsWith('data:') ||\n src.startsWith('blob:') ||\n src.startsWith('http://') ||\n src.startsWith('https://')\n ) {\n return;\n }\n paths.add(src);\n }\n\n function scanLayers(layers: Layer[] | undefined) {\n if (!layers) return;\n for (const layer of layers) {\n if (layer.type === 'image') addIfRelative(layer.content.src);\n if (layer.type === 'video') {\n addIfRelative(layer.content.src);\n addIfRelative(layer.content.posterSrc);\n }\n if (layer.type === 'map') addIfRelative(layer.content.staticSrc);\n }\n }\n\n function scanBlock(block: Block) {\n scanLayers(block.layers);\n if (block.children) block.children.forEach(scanBlock);\n }\n\n // Scan all blocks\n doc.blocks.forEach(scanBlock);\n\n // Scan start block hero\n if (doc.startBlock?.heroSrc) addIfRelative(doc.startBlock.heroSrc);\n\n // Scan persistent layers\n if (doc.persistentLayers) {\n for (const pl of doc.persistentLayers.bottomLayers ?? []) {\n if ('src' in pl && typeof pl.src === 'string') addIfRelative(pl.src);\n }\n for (const pl of doc.persistentLayers.topLayers ?? []) {\n if ('src' in pl && typeof pl.src === 'string') addIfRelative(pl.src);\n }\n }\n\n // Scan template block fields that reference images\n for (const block of doc.blocks) {\n scanTemplateImageFields(block as unknown as Record<string, unknown>, paths);\n }\n\n return paths;\n}\n\n/**\n * Recursively scan any object for known image-bearing field names.\n */\nfunction scanTemplateImageFields(obj: Record<string, unknown>, paths: Set<string>): void {\n if (!obj || typeof obj !== 'object') return;\n\n const imageFieldNames = [\n 'imageSrc',\n 'src',\n 'heroSrc',\n 'backgroundImage',\n 'posterSrc',\n 'staticSrc',\n 'videoSrc',\n 'thumbnailSrc',\n ];\n\n for (const key of imageFieldNames) {\n const val = obj[key];\n if (typeof val === 'string' && val && !val.startsWith('data:') && !val.startsWith('http')) {\n paths.add(val);\n }\n // Handle nested objects like { src: '...' }\n if (val && typeof val === 'object' && !Array.isArray(val)) {\n scanTemplateImageFields(val as Record<string, unknown>, paths);\n }\n }\n\n // Handle arrays (e.g., images: [{ src: '...' }])\n if (Array.isArray(obj)) {\n for (const item of obj) {\n if (item && typeof item === 'object') {\n scanTemplateImageFields(item as Record<string, unknown>, paths);\n }\n }\n }\n\n // Recurse into known array/object fields\n for (const key of ['images', 'accentImage', 'backgroundVideo', 'children', 'blocks']) {\n const val = obj[key];\n if (Array.isArray(val)) {\n for (const item of val) {\n if (item && typeof item === 'object') {\n scanTemplateImageFields(item as Record<string, unknown>, paths);\n }\n }\n } else if (val && typeof val === 'object') {\n scanTemplateImageFields(val as Record<string, unknown>, paths);\n }\n }\n}\n\n// ── HTML Generation ────────────────────────────────────────────────\n\n/**\n * Escape a string for safe embedding in a `<script>` tag.\n * Prevents `</script>` from closing the tag prematurely.\n */\nfunction escapeForScript(str: string): string {\n return str.replace(/<\\/(script)/gi, '<\\\\/$1');\n}\n\n/**\n * Generate a complete inline HTML document with all JS and images embedded.\n *\n * @param doc - The Doc to render\n * @param options - Export options\n * @returns Complete HTML string\n */\nexport function generateInlineHtml(doc: Doc, options: HtmlExportOptions): string {\n const {\n playerScript,\n images,\n mode = 'slideshow',\n title = 'Squisq Document',\n autoPlay = false,\n } = options;\n\n // Build base64 image map\n const imageMap: Record<string, string> = {};\n if (images) {\n for (const [path, buffer] of images.entries()) {\n const mimeType = inferMimeType(path);\n imageMap[path] = arrayBufferToBase64DataUrl(buffer, mimeType);\n }\n }\n\n const docJson = escapeForScript(JSON.stringify(doc));\n const imageMapJson = escapeForScript(JSON.stringify(imageMap));\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>${escapeHtml(title)}</title>\n<style>\n*,*::before,*::after{box-sizing:border-box}\nhtml,body{margin:0;padding:0;height:100%;background:#1a1a2e;color:#e0e0e0;font-family:system-ui,-apple-system,sans-serif}\n#squisq-root{width:100%;height:100%;display:flex;align-items:center;justify-content:center}\n${mode === 'static' ? '#squisq-root{align-items:flex-start;overflow-y:auto;background:#fff;color:#1f2937}' : ''}\n</style>\n</head>\n<body>\n<div id=\"squisq-root\"></div>\n<script>${escapeForScript(playerScript)}</script>\n<script>\n(function(){\n var doc = JSON.parse(${JSON.stringify(docJson)});\n var images = JSON.parse(${JSON.stringify(imageMapJson)});\n SquisqPlayer.mount(document.getElementById(\"squisq-root\"), doc, {\n mode: ${JSON.stringify(mode)},\n images: images,\n autoPlay: ${JSON.stringify(autoPlay)},\n basePath: \".\"\n });\n})();\n</script>\n</body>\n</html>`;\n}\n\n/**\n * Generate an HTML document that references external JS and image files.\n * Used for ZIP exports where files sit alongside the HTML.\n *\n * @param doc - The Doc to render (image/audio paths should already be rewritten to relative)\n * @param options - Export options (playerScript is not embedded, referenced via src)\n * @returns Complete HTML string\n */\nexport function generateExternalHtml(\n doc: Doc,\n options: Pick<HtmlExportOptions, 'mode' | 'title' | 'autoPlay'> & {\n /** Relative path to the player JS file (e.g., 'squisq-player.js') */\n playerScriptPath: string;\n /** Map of original image paths to their rewritten relative paths in the ZIP */\n imagePathMap?: Record<string, string>;\n /** Map of audio segment IDs to their rewritten relative paths in the ZIP */\n audioPathMap?: Record<string, string>;\n },\n): string {\n const {\n playerScriptPath,\n imagePathMap,\n audioPathMap,\n mode = 'slideshow',\n title = 'Squisq Document',\n autoPlay = false,\n } = options;\n\n const docJson = escapeForScript(JSON.stringify(doc));\n const imageMapJson = imagePathMap ? escapeForScript(JSON.stringify(imagePathMap)) : '{}';\n const audioMapJson = audioPathMap ? escapeForScript(JSON.stringify(audioPathMap)) : 'null';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>${escapeHtml(title)}</title>\n<style>\n*,*::before,*::after{box-sizing:border-box}\nhtml,body{margin:0;padding:0;height:100%;background:#1a1a2e;color:#e0e0e0;font-family:system-ui,-apple-system,sans-serif}\n#squisq-root{width:100%;height:100%;display:flex;align-items:center;justify-content:center}\n${mode === 'static' ? '#squisq-root{align-items:flex-start;overflow-y:auto;background:#fff;color:#1f2937}' : ''}\n</style>\n</head>\n<body>\n<div id=\"squisq-root\"></div>\n<script src=\"${escapeHtml(playerScriptPath)}\"></script>\n<script>\n(function(){\n var doc = JSON.parse(${JSON.stringify(docJson)});\n var images = JSON.parse(${JSON.stringify(imageMapJson)});\n var audio = ${audioMapJson};\n SquisqPlayer.mount(document.getElementById(\"squisq-root\"), doc, {\n mode: ${JSON.stringify(mode)},\n images: images,\n audio: audio,\n autoPlay: ${JSON.stringify(autoPlay)},\n basePath: \".\"\n });\n})();\n</script>\n</body>\n</html>`;\n}\n\n/**\n * Escape HTML special characters to prevent injection.\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n"],"mappings":";AAgCA,OAAO,WAAW;;;ACxBlB,IAAM,WAAmC;AAAA,EACvC,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AACR;AAMO,SAAS,cAAc,UAA0B;AACtD,QAAM,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY,KAAK;AACxD,SAAO,SAAS,GAAG,KAAK;AAC1B;AAUO,SAAS,2BAA2B,QAAqB,UAA0B;AACxF,QAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EACxC;AACA,QAAM,SAAS,KAAK,MAAM;AAC1B,SAAO,QAAQ,QAAQ,WAAW,MAAM;AAC1C;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;;;ACdO,SAAS,kBAAkB,KAAuB;AACvD,QAAM,QAAQ,oBAAI,IAAY;AAE9B,WAAS,cAAc,KAAyB;AAC9C,QACE,CAAC,OACD,IAAI,WAAW,OAAO,KACtB,IAAI,WAAW,OAAO,KACtB,IAAI,WAAW,SAAS,KACxB,IAAI,WAAW,UAAU,GACzB;AACA;AAAA,IACF;AACA,UAAM,IAAI,GAAG;AAAA,EACf;AAEA,WAAS,WAAW,QAA6B;AAC/C,QAAI,CAAC,OAAQ;AACb,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,SAAS,QAAS,eAAc,MAAM,QAAQ,GAAG;AAC3D,UAAI,MAAM,SAAS,SAAS;AAC1B,sBAAc,MAAM,QAAQ,GAAG;AAC/B,sBAAc,MAAM,QAAQ,SAAS;AAAA,MACvC;AACA,UAAI,MAAM,SAAS,MAAO,eAAc,MAAM,QAAQ,SAAS;AAAA,IACjE;AAAA,EACF;AAEA,WAAS,UAAU,OAAc;AAC/B,eAAW,MAAM,MAAM;AACvB,QAAI,MAAM,SAAU,OAAM,SAAS,QAAQ,SAAS;AAAA,EACtD;AAGA,MAAI,OAAO,QAAQ,SAAS;AAG5B,MAAI,IAAI,YAAY,QAAS,eAAc,IAAI,WAAW,OAAO;AAGjE,MAAI,IAAI,kBAAkB;AACxB,eAAW,MAAM,IAAI,iBAAiB,gBAAgB,CAAC,GAAG;AACxD,UAAI,SAAS,MAAM,OAAO,GAAG,QAAQ,SAAU,eAAc,GAAG,GAAG;AAAA,IACrE;AACA,eAAW,MAAM,IAAI,iBAAiB,aAAa,CAAC,GAAG;AACrD,UAAI,SAAS,MAAM,OAAO,GAAG,QAAQ,SAAU,eAAc,GAAG,GAAG;AAAA,IACrE;AAAA,EACF;AAGA,aAAW,SAAS,IAAI,QAAQ;AAC9B,4BAAwB,OAA6C,KAAK;AAAA,EAC5E;AAEA,SAAO;AACT;AAKA,SAAS,wBAAwB,KAA8B,OAA0B;AACvF,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AAErC,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,OAAO,iBAAiB;AACjC,UAAM,MAAM,IAAI,GAAG;AACnB,QAAI,OAAO,QAAQ,YAAY,OAAO,CAAC,IAAI,WAAW,OAAO,KAAK,CAAC,IAAI,WAAW,MAAM,GAAG;AACzF,YAAM,IAAI,GAAG;AAAA,IACf;AAEA,QAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;AACzD,8BAAwB,KAAgC,KAAK;AAAA,IAC/D;AAAA,EACF;AAGA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,eAAW,QAAQ,KAAK;AACtB,UAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,gCAAwB,MAAiC,KAAK;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAGA,aAAW,OAAO,CAAC,UAAU,eAAe,mBAAmB,YAAY,QAAQ,GAAG;AACpF,UAAM,MAAM,IAAI,GAAG;AACnB,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,iBAAW,QAAQ,KAAK;AACtB,YAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,kCAAwB,MAAiC,KAAK;AAAA,QAChE;AAAA,MACF;AAAA,IACF,WAAW,OAAO,OAAO,QAAQ,UAAU;AACzC,8BAAwB,KAAgC,KAAK;AAAA,IAC/D;AAAA,EACF;AACF;AAQA,SAAS,gBAAgB,KAAqB;AAC5C,SAAO,IAAI,QAAQ,iBAAiB,QAAQ;AAC9C;AASO,SAAS,mBAAmB,KAAU,SAAoC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,EACb,IAAI;AAGJ,QAAM,WAAmC,CAAC;AAC1C,MAAI,QAAQ;AACV,eAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,GAAG;AAC7C,YAAM,WAAW,cAAc,IAAI;AACnC,eAAS,IAAI,IAAI,2BAA2B,QAAQ,QAAQ;AAAA,IAC9D;AAAA,EACF;AAEA,QAAM,UAAU,gBAAgB,KAAK,UAAU,GAAG,CAAC;AACnD,QAAM,eAAe,gBAAgB,KAAK,UAAU,QAAQ,CAAC;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,SAKA,WAAW,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,SAAS,WAAW,uFAAuF,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,UAKrG,gBAAgB,YAAY,CAAC;AAAA;AAAA;AAAA,yBAGd,KAAK,UAAU,OAAO,CAAC;AAAA,4BACpB,KAAK,UAAU,YAAY,CAAC;AAAA;AAAA,YAE5C,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,gBAEhB,KAAK,UAAU,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOxC;AAUO,SAAS,qBACd,KACA,SAQQ;AACR,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,EACb,IAAI;AAEJ,QAAM,UAAU,gBAAgB,KAAK,UAAU,GAAG,CAAC;AACnD,QAAM,eAAe,eAAe,gBAAgB,KAAK,UAAU,YAAY,CAAC,IAAI;AACpF,QAAM,eAAe,eAAe,gBAAgB,KAAK,UAAU,YAAY,CAAC,IAAI;AAEpF,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,SAKA,WAAW,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,SAAS,WAAW,uFAAuF,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,eAKhG,WAAW,gBAAgB,CAAC;AAAA;AAAA;AAAA,yBAGlB,KAAK,UAAU,OAAO,CAAC;AAAA,4BACpB,KAAK,UAAU,YAAY,CAAC;AAAA,gBACxC,YAAY;AAAA;AAAA,YAEhB,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA;AAAA,gBAGhB,KAAK,UAAU,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOxC;AAKA,SAAS,WAAW,KAAqB;AACvC,SAAO,IACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;;;AFtOO,SAAS,UAAU,KAAU,SAAoC;AACtE,SAAO,mBAAmB,KAAK,OAAO;AACxC;AAmCA,eAAsB,aAAa,KAAU,SAA8C;AACzF,QAAM,EAAE,cAAc,QAAQ,OAAO,OAAO,aAAa,OAAO,SAAS,IAAI;AAE7E,QAAM,MAAM,IAAI,MAAM;AAGtB,MAAI,KAAK,oBAAoB,YAAY;AAGzC,QAAM,eAAuC,CAAC;AAC9C,MAAI,QAAQ;AACV,eAAW,CAAC,cAAc,MAAM,KAAK,OAAO,QAAQ,GAAG;AACrD,YAAM,WAAW,gBAAgB,YAAY;AAC7C,YAAM,UAAU,UAAU,QAAQ;AAClC,UAAI,KAAK,SAAS,MAAM;AACxB,mBAAa,YAAY,IAAI;AAAA,IAC/B;AAAA,EACF;AAGA,QAAM,eAAuC,CAAC;AAC9C,MAAI,OAAO;AACT,eAAW,CAAC,YAAY,MAAM,KAAK,MAAM,QAAQ,GAAG;AAClD,YAAM,WAAW,gBAAgB,UAAU;AAE3C,YAAM,YAAY,SAAS,SAAS,GAAG,IAAI,WAAW,GAAG,QAAQ;AACjE,YAAM,UAAU,SAAS,SAAS;AAClC,UAAI,KAAK,SAAS,MAAM;AACxB,mBAAa,UAAU,IAAI;AAAA,IAC7B;AAAA,EACF;AAGA,QAAM,OAAO,qBAAqB,KAAK;AAAA,IACrC,kBAAkB;AAAA,IAClB,cAAc,OAAO,KAAK,YAAY,EAAE,SAAS,IAAI,eAAe;AAAA,IACpE,cAAc,OAAO,KAAK,YAAY,EAAE,SAAS,IAAI,eAAe;AAAA,IACpE;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,KAAK,cAAc,IAAI;AAG3B,SAAO,IAAI,cAAc;AAAA,IACvB,MAAM;AAAA,IACN,aAAa;AAAA,IACb,oBAAoB,EAAE,OAAO,EAAE;AAAA,EACjC,CAAC;AACH;","names":[]}
|