@bendyline/squisq-formats 1.4.0 → 1.4.1
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 +180 -43
- package/dist/{chunk-CRTC6DEX.js → chunk-46FMDOWZ.js} +2 -2
- package/dist/{chunk-ON232HJR.js → chunk-5LSSWZPU.js} +2 -2
- package/dist/{chunk-APSIAEXL.js → chunk-D7IFPWNZ.js} +87 -43
- package/dist/chunk-D7IFPWNZ.js.map +1 -0
- package/dist/chunk-DQAZR57U.js +33 -0
- package/dist/chunk-DQAZR57U.js.map +1 -0
- package/dist/chunk-EHLGMSTE.js +273 -0
- package/dist/chunk-EHLGMSTE.js.map +1 -0
- package/dist/{chunk-A3FHLTY5.js → chunk-MJGRI6XR.js} +6 -19
- package/dist/chunk-MJGRI6XR.js.map +1 -0
- package/dist/{chunk-JBNVE2GV.js → chunk-RFAPOKHJ.js} +9 -2
- package/dist/chunk-RFAPOKHJ.js.map +1 -0
- package/dist/chunk-RTS5XBZ7.js +526 -0
- package/dist/chunk-RTS5XBZ7.js.map +1 -0
- package/dist/{chunk-FMOIGA5T.js → chunk-SHLPSADL.js} +178 -5
- package/dist/chunk-SHLPSADL.js.map +1 -0
- package/dist/chunk-SSUPBUF5.js +15 -0
- package/dist/chunk-SSUPBUF5.js.map +1 -0
- package/dist/{chunk-BJQIJ4S4.js → chunk-WWBIKRNP.js} +17 -41
- package/dist/chunk-WWBIKRNP.js.map +1 -0
- package/dist/{chunk-I2GFTEOV.js → chunk-YESS7VY3.js} +77 -15
- package/dist/{chunk-I2GFTEOV.js.map → chunk-YESS7VY3.js.map} +1 -1
- package/dist/csv/index.d.ts +14 -1
- package/dist/csv/index.js +1 -1
- package/dist/docx/index.js +4 -3
- package/dist/epub/index.js +3 -2
- package/dist/html/index.d.ts +3 -0
- package/dist/html/index.js +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26 -11
- package/dist/ooxml/index.d.ts +5 -1
- package/dist/ooxml/index.js +16 -12
- package/dist/pdf/index.js +2 -1
- package/dist/pptx/index.d.ts +18 -3
- package/dist/pptx/index.js +7 -4
- package/dist/registry/index.d.ts +167 -0
- package/dist/registry/index.js +17 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/xlsx/index.d.ts +44 -14
- package/dist/xlsx/index.js +4 -2
- package/package.json +7 -2
- package/src/__tests__/convert.test.ts +186 -0
- package/src/__tests__/csvImport.test.ts +34 -1
- package/src/__tests__/formatRegistry.test.ts +174 -0
- package/src/__tests__/lossyWarnings.test.ts +105 -0
- package/src/__tests__/pdfImport.test.ts +95 -1
- package/src/__tests__/pptxImport.test.ts +46 -1
- package/src/__tests__/roundTripAssets.test.ts +50 -0
- package/src/__tests__/roundTripMatrix.test.ts +10 -4
- package/src/__tests__/sharedContainer.test.ts +41 -0
- package/src/__tests__/sharedImages.test.ts +61 -0
- package/src/__tests__/xlsxExport.test.ts +164 -0
- package/src/csv/index.ts +25 -2
- package/src/docx/import.ts +6 -37
- package/src/html/imageUtils.ts +5 -19
- package/src/index.ts +29 -5
- package/src/ooxml/index.ts +3 -0
- package/src/ooxml/namespaces.ts +10 -0
- package/src/pdf/import.ts +145 -50
- package/src/pptx/import.ts +127 -10
- package/src/pptx/index.ts +1 -1
- package/src/registry/convert.ts +318 -0
- package/src/registry/defaultFormats.ts +323 -0
- package/src/registry/errors.ts +46 -0
- package/src/registry/index.ts +38 -0
- package/src/registry/registry.ts +48 -0
- package/src/registry/types.ts +107 -0
- package/src/shared/container.ts +28 -0
- package/src/shared/images.ts +44 -0
- package/src/xlsx/export.ts +253 -0
- package/src/xlsx/index.ts +10 -36
- package/dist/chunk-A3FHLTY5.js.map +0 -1
- package/dist/chunk-APSIAEXL.js.map +0 -1
- package/dist/chunk-BJQIJ4S4.js.map +0 -1
- package/dist/chunk-FMOIGA5T.js.map +0 -1
- package/dist/chunk-JBNVE2GV.js.map +0 -1
- package/dist/chunk-L53YIGYS.js +0 -141
- package/dist/chunk-L53YIGYS.js.map +0 -1
- package/dist/chunk-QSE7EWE7.js +0 -181
- package/dist/chunk-QSE7EWE7.js.map +0 -1
- /package/dist/{chunk-CRTC6DEX.js.map → chunk-46FMDOWZ.js.map} +0 -0
- /package/dist/{chunk-ON232HJR.js.map → chunk-5LSSWZPU.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pdf/export.ts","../src/pdf/styles.ts","../src/pdf/import.ts"],"sourcesContent":["/**\n * PDF Export\n *\n * Converts a squisq MarkdownDocument (or Doc) into a PDF file\n * using pdf-lib. Generates paginated, styled output with support for\n * headings, paragraphs, inline formatting, lists, code blocks,\n * blockquotes, tables, thematic breaks, and hyperlinks.\n *\n * Uses only the 14 standard PDF fonts (no font embedding required),\n * keeping output size small and rendering fast.\n *\n * @example\n * ```ts\n * import { parseMarkdown } from '@bendyline/squisq/markdown';\n * import { markdownDocToPdf } from '@bendyline/squisq-formats/pdf';\n *\n * const md = parseMarkdown('# Hello\\n\\nWorld **bold** text');\n * const buffer = await markdownDocToPdf(md);\n * ```\n */\n\nimport { PDFDocument, StandardFonts, rgb, PDFFont, PDFPage } from 'pdf-lib';\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { docToMarkdown, resolveThemeForDoc } from '@bendyline/squisq/doc';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownHeading,\n MarkdownParagraph,\n MarkdownBlockquote,\n MarkdownList,\n MarkdownListItem,\n MarkdownCodeBlock,\n MarkdownTable,\n MarkdownTableRow,\n MarkdownTableCell,\n MarkdownHtmlBlock,\n MarkdownMathBlock,\n MarkdownFootnoteDefinition,\n MarkdownText,\n MarkdownEmphasis,\n MarkdownStrong,\n MarkdownStrikethrough,\n MarkdownInlineCode,\n MarkdownLink,\n MarkdownImage,\n MarkdownInlineHtml,\n MarkdownInlineMath,\n MarkdownFootnoteReference,\n} from '@bendyline/squisq/markdown';\nimport { readFrontmatterThemeId } from '@bendyline/squisq/markdown';\n\nimport {\n PAGE_WIDTH_LETTER,\n PAGE_HEIGHT_LETTER,\n PAGE_WIDTH_A4,\n PAGE_HEIGHT_A4,\n DEFAULT_MARGIN,\n DEFAULT_FONT_SIZE,\n HEADING_SIZES,\n CODE_FONT_SIZE,\n LINE_HEIGHT_FACTOR,\n HEADING_SPACE_BEFORE,\n HEADING_SPACE_AFTER,\n PARAGRAPH_SPACING,\n LIST_INDENT,\n BULLET_CHAR,\n BLOCKQUOTE_INDENT,\n BLOCKQUOTE_BAR_WIDTH,\n COLOR_TEXT,\n COLOR_HEADING,\n COLOR_LINK,\n COLOR_CODE_TEXT,\n COLOR_BLOCKQUOTE_BAR,\n COLOR_BLOCKQUOTE_TEXT,\n COLOR_THEMATIC_BREAK,\n COLOR_TABLE_BORDER,\n COLOR_TABLE_HEADER_BG,\n TABLE_CELL_PAD_X,\n TABLE_CELL_PAD_Y,\n TABLE_BORDER_WIDTH,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/** Page size presets. */\nexport type PdfPageSize = 'letter' | 'a4';\n\n/**\n * Options for PDF export.\n */\nexport interface PdfExportOptions {\n /** Document title (PDF metadata). */\n title?: string;\n /** Document author (PDF metadata). */\n author?: string;\n /** Page size preset. Default: \"letter\". */\n pageSize?: PdfPageSize;\n /** Page margins in points. Default: 72 (1 inch). */\n margin?: number;\n /** Default body font size in points. Default: 11. */\n defaultFontSize?: number;\n /**\n * Squisq theme ID to apply (e.g., 'documentary', 'cinematic').\n * When set, overrides heading, text, and link colors with the theme palette.\n * Font changes are not supported (pdf-lib uses standard 14 PDF fonts only).\n */\n themeId?: string;\n}\n\n/**\n * Convert a MarkdownDocument to a PDF ArrayBuffer.\n */\nexport async function markdownDocToPdf(\n doc: MarkdownDocument,\n options: PdfExportOptions = {},\n): Promise<ArrayBuffer> {\n const pdfDoc = await PDFDocument.create();\n\n // Metadata\n if (options.title) pdfDoc.setTitle(options.title);\n if (options.author) pdfDoc.setAuthor(options.author);\n pdfDoc.setCreationDate(new Date());\n pdfDoc.setModificationDate(new Date());\n\n const ctx = await createExportContext(pdfDoc, options, doc);\n\n renderBlocks(doc.children, ctx, 0);\n\n const bytes = await pdfDoc.save();\n return bytes.buffer as ArrayBuffer;\n}\n\n/**\n * Convert a squisq Doc to a PDF ArrayBuffer.\n *\n * Convenience wrapper: Doc → MarkdownDocument → PDF.\n */\nexport async function docToPdf(doc: Doc, options: PdfExportOptions = {}): Promise<ArrayBuffer> {\n const markdownDoc = docToMarkdown(doc);\n return markdownDocToPdf(markdownDoc, options);\n}\n\n// ============================================\n// Export Context — tracks cursor, fonts, pages\n// ============================================\n\ninterface FontSet {\n regular: PDFFont;\n bold: PDFFont;\n italic: PDFFont;\n boldItalic: PDFFont;\n mono: PDFFont;\n monoBold: PDFFont;\n}\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\ninterface ExportContext {\n pdfDoc: PDFDocument;\n fonts: FontSet;\n pageWidth: number;\n pageHeight: number;\n margin: number;\n fontSize: number;\n /** Current page being drawn on. */\n page: PDFPage;\n /** Current y position (from top of page, decreasing). */\n y: number;\n /** Content area width = pageWidth - 2*margin. */\n contentWidth: number;\n /** Bottom margin y position. */\n bottomY: number;\n /** Resolved colors (may be overridden by theme). */\n colors: {\n text: RgbColor;\n heading: RgbColor;\n link: RgbColor;\n };\n}\n\nasync function createExportContext(\n pdfDoc: PDFDocument,\n options: PdfExportOptions,\n doc: MarkdownDocument,\n): Promise<ExportContext> {\n const [regular, bold, italic, boldItalic, mono, monoBold] = await Promise.all([\n pdfDoc.embedFont(StandardFonts.Helvetica),\n pdfDoc.embedFont(StandardFonts.HelveticaBold),\n pdfDoc.embedFont(StandardFonts.HelveticaOblique),\n pdfDoc.embedFont(StandardFonts.HelveticaBoldOblique),\n pdfDoc.embedFont(StandardFonts.Courier),\n pdfDoc.embedFont(StandardFonts.CourierBold),\n ]);\n\n const isA4 = options.pageSize === 'a4';\n const pageWidth = isA4 ? PAGE_WIDTH_A4 : PAGE_WIDTH_LETTER;\n const pageHeight = isA4 ? PAGE_HEIGHT_A4 : PAGE_HEIGHT_LETTER;\n const margin = options.margin ?? DEFAULT_MARGIN;\n const fontSize = options.defaultFontSize ?? DEFAULT_FONT_SIZE;\n\n const page = pdfDoc.addPage([pageWidth, pageHeight]);\n\n // Resolve theme colors if themeId is set\n let colorText = COLOR_TEXT;\n let colorHeading = COLOR_HEADING;\n let colorLink = COLOR_LINK;\n\n // Honor an explicit themeId, else the doc's frontmatter theme (`squisq-theme`\n // / legacy) — mirroring PPTX/DOCX/HTML so every format themes consistently,\n // including inline custom themes (resolved doc-scoped).\n const themeId = options.themeId ?? readFrontmatterThemeId(doc.frontmatter);\n if (themeId) {\n const theme = resolveThemeForDoc(doc, themeId);\n if (theme.colors) {\n colorText = hexToRgb(theme.colors.text) ?? COLOR_TEXT;\n colorHeading = hexToRgb(theme.colors.primary) ?? COLOR_HEADING;\n colorLink = hexToRgb(theme.colors.highlight || theme.colors.secondary) ?? COLOR_LINK;\n }\n }\n\n return {\n pdfDoc,\n fonts: { regular, bold, italic, boldItalic, mono, monoBold },\n pageWidth,\n pageHeight,\n margin,\n fontSize,\n page,\n y: pageHeight - margin,\n contentWidth: pageWidth - 2 * margin,\n bottomY: margin,\n colors: { text: colorText, heading: colorHeading, link: colorLink },\n };\n}\n\n// ============================================\n// Page Break Management\n// ============================================\n\nfunction ensureSpace(ctx: ExportContext, needed: number): void {\n if (ctx.y - needed < ctx.bottomY) {\n newPage(ctx);\n }\n}\n\nfunction newPage(ctx: ExportContext): void {\n const page = ctx.pdfDoc.addPage([ctx.pageWidth, ctx.pageHeight]);\n ctx.page = page;\n ctx.y = ctx.pageHeight - ctx.margin;\n}\n\n// ============================================\n// Inline \"Span\" Model\n// ============================================\n\ninterface TextSpan {\n text: string;\n font: PDFFont;\n fontSize: number;\n color: { r: number; g: number; b: number };\n link?: string;\n strikethrough?: boolean;\n}\n\n/**\n * Flatten an inline node tree into a flat list of TextSpans,\n * accumulating bold/italic/code state as we recurse.\n */\nfunction flattenInlines(\n nodes: MarkdownInlineNode[],\n ctx: ExportContext,\n state: {\n bold: boolean;\n italic: boolean;\n code: boolean;\n link?: string;\n color?: { r: number; g: number; b: number };\n strikethrough?: boolean;\n },\n): TextSpan[] {\n const spans: TextSpan[] = [];\n\n for (const node of nodes) {\n switch (node.type) {\n case 'text': {\n const font = state.code\n ? state.bold\n ? ctx.fonts.monoBold\n : ctx.fonts.mono\n : pickFont(ctx, state.bold, state.italic);\n spans.push({\n text: (node as MarkdownText).value,\n font,\n fontSize: state.code ? CODE_FONT_SIZE : ctx.fontSize,\n color: state.code ? COLOR_CODE_TEXT : (state.color ?? ctx.colors.text),\n link: state.link,\n strikethrough: state.strikethrough,\n });\n break;\n }\n\n case 'strong':\n spans.push(\n ...flattenInlines((node as MarkdownStrong).children, ctx, { ...state, bold: true }),\n );\n break;\n\n case 'emphasis':\n spans.push(\n ...flattenInlines((node as MarkdownEmphasis).children, ctx, { ...state, italic: true }),\n );\n break;\n\n case 'delete':\n spans.push(\n ...flattenInlines((node as MarkdownStrikethrough).children, ctx, {\n ...state,\n strikethrough: true,\n }),\n );\n break;\n\n case 'inlineCode': {\n spans.push({\n text: (node as MarkdownInlineCode).value,\n font: ctx.fonts.mono,\n fontSize: CODE_FONT_SIZE,\n color: COLOR_CODE_TEXT,\n link: state.link,\n });\n break;\n }\n\n case 'link': {\n const linkNode = node as MarkdownLink;\n spans.push(\n ...flattenInlines(linkNode.children, ctx, {\n ...state,\n link: linkNode.url,\n color: ctx.colors.link,\n }),\n );\n break;\n }\n\n case 'image': {\n const imgNode = node as MarkdownImage;\n spans.push({\n text: imgNode.alt ? `[Image: ${imgNode.alt}]` : '[Image]',\n font: ctx.fonts.italic,\n fontSize: ctx.fontSize,\n color: COLOR_BLOCKQUOTE_TEXT,\n });\n break;\n }\n\n case 'break':\n spans.push({\n text: '\\n',\n font: ctx.fonts.regular,\n fontSize: ctx.fontSize,\n color: ctx.colors.text,\n });\n break;\n\n case 'htmlInline': {\n const html = (node as MarkdownInlineHtml).rawHtml;\n if (html) {\n spans.push({\n text: html,\n font: ctx.fonts.mono,\n fontSize: CODE_FONT_SIZE,\n color: COLOR_CODE_TEXT,\n });\n }\n break;\n }\n\n case 'inlineMath':\n spans.push({\n text: (node as MarkdownInlineMath).value,\n font: ctx.fonts.mono,\n fontSize: CODE_FONT_SIZE,\n color: COLOR_CODE_TEXT,\n });\n break;\n\n case 'footnoteReference': {\n const ref = node as MarkdownFootnoteReference;\n spans.push({\n text: `[${ref.identifier}]`,\n font: ctx.fonts.regular,\n fontSize: ctx.fontSize * 0.75,\n color: ctx.colors.link,\n });\n break;\n }\n\n // linkReference, imageReference, textDirective — render children or identifier\n default: {\n const fallback = node as unknown as { children?: MarkdownInlineNode[]; value?: string };\n if (fallback.children) {\n spans.push(...flattenInlines(fallback.children, ctx, state));\n } else if (fallback.value) {\n spans.push({\n text: fallback.value,\n font: pickFont(ctx, state.bold, state.italic),\n fontSize: ctx.fontSize,\n color: state.color ?? ctx.colors.text,\n });\n }\n break;\n }\n }\n }\n\n return spans;\n}\n\nfunction pickFont(ctx: ExportContext, bold: boolean, italic: boolean): PDFFont {\n if (bold && italic) return ctx.fonts.boldItalic;\n if (bold) return ctx.fonts.bold;\n if (italic) return ctx.fonts.italic;\n return ctx.fonts.regular;\n}\n\n// ============================================\n// Word-Wrap & Draw\n// ============================================\n\n/**\n * Word-wraps and draws a flat list of TextSpans within the given\n * available width, starting at ctx.y. Updates ctx.y after drawing.\n * Returns the y position after the last line.\n */\nfunction drawSpans(\n spans: TextSpan[],\n ctx: ExportContext,\n availableWidth: number,\n x0: number,\n): void {\n if (spans.length === 0) return;\n\n // Split spans at \\n and word boundaries, then wrap into lines\n const lines = wrapSpans(spans, availableWidth);\n\n for (const line of lines) {\n const lineHeight = getLineHeight(line);\n ensureSpace(ctx, lineHeight);\n\n let x = x0;\n for (const span of line) {\n ctx.page.drawText(span.text, {\n x,\n y: ctx.y - span.fontSize, // pdf-lib y is baseline\n size: span.fontSize,\n font: span.font,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n\n const textWidth = span.font.widthOfTextAtSize(span.text, span.fontSize);\n\n // Underline for links\n if (span.link) {\n ctx.page.drawLine({\n start: { x, y: ctx.y - span.fontSize - 1 },\n end: { x: x + textWidth, y: ctx.y - span.fontSize - 1 },\n thickness: 0.5,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n }\n\n // Strikethrough\n if (span.strikethrough) {\n const midY = ctx.y - span.fontSize * 0.6;\n ctx.page.drawLine({\n start: { x, y: midY },\n end: { x: x + textWidth, y: midY },\n thickness: 0.5,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n }\n\n x += textWidth;\n }\n\n ctx.y -= lineHeight;\n }\n}\n\n/**\n * Break a flat list of spans into wrapped lines that fit within\n * `maxWidth`. Respects explicit \\n characters.\n */\nfunction wrapSpans(spans: TextSpan[], maxWidth: number): TextSpan[][] {\n const lines: TextSpan[][] = [];\n let currentLine: TextSpan[] = [];\n let lineWidth = 0;\n\n for (const span of spans) {\n // Handle explicit newlines\n if (span.text === '\\n') {\n lines.push(currentLine);\n currentLine = [];\n lineWidth = 0;\n continue;\n }\n\n // Split by whitespace for word-wrapping\n const words = splitIntoWords(span.text);\n\n for (const word of words) {\n const wordWidth = span.font.widthOfTextAtSize(word, span.fontSize);\n\n if (lineWidth + wordWidth > maxWidth && currentLine.length > 0 && word.trim().length > 0) {\n // Wrap to next line\n lines.push(currentLine);\n currentLine = [];\n lineWidth = 0;\n }\n\n // Trim leading space on new line\n const trimmedWord = currentLine.length === 0 ? word.replace(/^\\s+/, '') : word;\n if (trimmedWord.length > 0) {\n const tw = span.font.widthOfTextAtSize(trimmedWord, span.fontSize);\n currentLine.push({ ...span, text: trimmedWord });\n lineWidth += tw;\n }\n }\n }\n\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [[]];\n}\n\n/**\n * Split text into \"words\" preserving whitespace as separate tokens\n * so the wrapping logic can break at whitespace.\n */\nfunction splitIntoWords(text: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let inSpace = false;\n\n for (const ch of text) {\n const isSpace = ch === ' ' || ch === '\\t';\n if (isSpace !== inSpace && current.length > 0) {\n tokens.push(current);\n current = '';\n }\n current += ch;\n inSpace = isSpace;\n }\n if (current.length > 0) tokens.push(current);\n return tokens;\n}\n\nfunction getLineHeight(line: TextSpan[]): number {\n let maxSize = 0;\n for (const span of line) {\n if (span.fontSize > maxSize) maxSize = span.fontSize;\n }\n return (maxSize || DEFAULT_FONT_SIZE) * LINE_HEIGHT_FACTOR;\n}\n\n// ============================================\n// Block Renderers\n// ============================================\n\nfunction renderBlocks(nodes: MarkdownBlockNode[], ctx: ExportContext, extraIndent: number): void {\n for (const node of nodes) {\n renderBlock(node, ctx, extraIndent);\n }\n}\n\nfunction renderBlock(node: MarkdownBlockNode, ctx: ExportContext, extraIndent: number): void {\n switch (node.type) {\n case 'heading':\n renderHeading(node as MarkdownHeading, ctx, extraIndent);\n break;\n case 'paragraph':\n renderParagraph(node as MarkdownParagraph, ctx, extraIndent);\n break;\n case 'blockquote':\n renderBlockquote(node as MarkdownBlockquote, ctx, extraIndent);\n break;\n case 'list':\n renderList(node as MarkdownList, ctx, extraIndent, 0);\n break;\n case 'code':\n renderCodeBlock(node as MarkdownCodeBlock, ctx, extraIndent);\n break;\n case 'table':\n renderTable(node as MarkdownTable, ctx, extraIndent);\n break;\n case 'thematicBreak':\n renderThematicBreak(ctx, extraIndent);\n break;\n case 'htmlBlock':\n renderHtmlBlock(node as MarkdownHtmlBlock, ctx, extraIndent);\n break;\n case 'math':\n renderMathBlock(node as MarkdownMathBlock, ctx, extraIndent);\n break;\n case 'footnoteDefinition':\n renderFootnoteDefinition(node as MarkdownFootnoteDefinition, ctx, extraIndent);\n break;\n default:\n // containerDirective, leafDirective, definitionList, etc.\n // Render any children or value as best-effort\n renderFallbackBlock(node, ctx, extraIndent);\n break;\n }\n}\n\n// ---- Heading ----\n\nfunction renderHeading(node: MarkdownHeading, ctx: ExportContext, extraIndent: number): void {\n const origFontSize = ctx.fontSize;\n ctx.fontSize = HEADING_SIZES[node.depth] ?? DEFAULT_FONT_SIZE;\n\n ctx.y -= HEADING_SPACE_BEFORE;\n\n const x0 = ctx.margin + extraIndent;\n const w = ctx.contentWidth - extraIndent;\n\n const spans = flattenInlines(node.children, ctx, {\n bold: true,\n italic: false,\n code: false,\n color: ctx.colors.heading,\n });\n\n drawSpans(spans, ctx, w, x0);\n\n ctx.y -= HEADING_SPACE_AFTER;\n ctx.fontSize = origFontSize;\n}\n\n// ---- Paragraph ----\n\nfunction renderParagraph(\n node: MarkdownParagraph,\n ctx: ExportContext,\n extraIndent: number,\n colorOverride?: { r: number; g: number; b: number },\n): void {\n const x0 = ctx.margin + extraIndent;\n const w = ctx.contentWidth - extraIndent;\n\n const spans = flattenInlines(node.children, ctx, {\n bold: false,\n italic: false,\n code: false,\n color: colorOverride,\n });\n\n drawSpans(spans, ctx, w, x0);\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Blockquote ----\n\nfunction renderBlockquote(node: MarkdownBlockquote, ctx: ExportContext, extraIndent: number): void {\n const barX = ctx.margin + extraIndent + 4;\n const indent = extraIndent + BLOCKQUOTE_INDENT;\n const startY = ctx.y;\n\n for (const child of node.children) {\n if (child.type === 'paragraph') {\n renderParagraph(child as MarkdownParagraph, ctx, indent, COLOR_BLOCKQUOTE_TEXT);\n } else {\n renderBlock(child, ctx, indent);\n }\n }\n\n // Draw left bar from startY to ctx.y\n const endY = ctx.y + PARAGRAPH_SPACING; // undo last paragraph spacing\n if (startY > endY) {\n // Bar might span pages — draw on current page only\n ctx.page.drawRectangle({\n x: barX,\n y: endY,\n width: BLOCKQUOTE_BAR_WIDTH,\n height: startY - endY,\n color: rgb(COLOR_BLOCKQUOTE_BAR.r, COLOR_BLOCKQUOTE_BAR.g, COLOR_BLOCKQUOTE_BAR.b),\n });\n }\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- List ----\n\nfunction renderList(\n node: MarkdownList,\n ctx: ExportContext,\n extraIndent: number,\n depth: number,\n): void {\n const ordered = node.ordered ?? false;\n let counter = node.start ?? 1;\n\n for (const child of node.children) {\n if (child.type === 'listItem') {\n renderListItem(child as MarkdownListItem, ctx, extraIndent, depth, ordered, counter);\n if (ordered) counter++;\n }\n }\n}\n\nfunction renderListItem(\n item: MarkdownListItem,\n ctx: ExportContext,\n extraIndent: number,\n depth: number,\n ordered: boolean,\n counter: number,\n): void {\n const indent = extraIndent + depth * LIST_INDENT;\n const bullet = ordered ? `${counter}.` : BULLET_CHAR;\n const bulletFont = ctx.fonts.regular;\n const bulletWidth = bulletFont.widthOfTextAtSize(bullet + ' ', ctx.fontSize);\n\n // Draw bullet\n const lineHeight = ctx.fontSize * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineHeight);\n\n ctx.page.drawText(bullet, {\n x: ctx.margin + indent,\n y: ctx.y - ctx.fontSize,\n size: ctx.fontSize,\n font: bulletFont,\n color: rgb(ctx.colors.text.r, ctx.colors.text.g, ctx.colors.text.b),\n });\n\n const textIndent = indent + bulletWidth + 4;\n const textWidth = ctx.contentWidth - textIndent;\n\n // Render children at the text indent\n let isFirstChild = true;\n for (const child of item.children) {\n if (child.type === 'paragraph' && isFirstChild) {\n // First paragraph: render on same line as bullet\n const spans = flattenInlines((child as MarkdownParagraph).children, ctx, {\n bold: false,\n italic: false,\n code: false,\n });\n drawSpans(spans, ctx, textWidth, ctx.margin + textIndent);\n ctx.y -= PARAGRAPH_SPACING / 2;\n } else if (child.type === 'list') {\n renderList(child as MarkdownList, ctx, extraIndent, depth + 1);\n } else {\n renderBlock(child, ctx, textIndent);\n }\n isFirstChild = false;\n }\n}\n\n// ---- Code Block ----\n\nfunction renderCodeBlock(node: MarkdownCodeBlock, ctx: ExportContext, extraIndent: number): void {\n const x0 = ctx.margin + extraIndent;\n const _w = ctx.contentWidth - extraIndent;\n const lines = node.value.split('\\n');\n const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;\n const totalHeight = lines.length * lineH + 12; // 12 = vertical padding\n\n ensureSpace(ctx, Math.min(totalHeight, ctx.y - ctx.bottomY));\n\n const _bgTop = ctx.y;\n\n // Draw background first (we'll adjust after we know the final y)\n const _bgStartY = ctx.y;\n ctx.y -= 6; // top padding\n\n for (const line of lines) {\n ensureSpace(ctx, lineH);\n if (line.length > 0) {\n ctx.page.drawText(line, {\n x: x0 + 8,\n y: ctx.y - CODE_FONT_SIZE,\n size: CODE_FONT_SIZE,\n font: ctx.fonts.mono,\n color: rgb(COLOR_CODE_TEXT.r, COLOR_CODE_TEXT.g, COLOR_CODE_TEXT.b),\n });\n }\n ctx.y -= lineH;\n }\n\n ctx.y -= 6; // bottom padding\n\n // Draw background rectangle (behind text — pdf-lib draws on top,\n // so we could draw it first on a separate pass, but since we can't\n // easily re-order, we leave it as a visual approximation).\n // For a production implementation, you'd use a two-pass approach.\n // Here we draw the bg rect at the saved position. Text drawn after\n // will already be on the page. Since pdf-lib paints in order, we\n // accept that the bg goes on top — but only if bg is on same page.\n // A simpler approach: draw bg *before* text per page.\n\n // Actually, let's use a cleaner approach: draw line by line with bg\n // We already drew the text. For the background box, we'll just\n // not draw it behind (it would overlay). This is a known limitation\n // with pdf-lib's immediate mode. In practice the light grey bg makes\n // text hard to read if drawn on top. So we skip the bg for now and\n // rely on the monospace font to visually distinguish code.\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Table ----\n\nfunction renderTable(node: MarkdownTable, ctx: ExportContext, extraIndent: number): void {\n if (node.children.length === 0) return;\n\n const x0 = ctx.margin + extraIndent;\n const tableWidth = ctx.contentWidth - extraIndent;\n\n // Measure columns: get max width per column across all rows\n const numCols = Math.max(\n ...node.children.map((row) => (row as MarkdownTableRow).children.length),\n );\n if (numCols === 0) return;\n\n // Simple equal-width columns\n const colWidth = tableWidth / numCols;\n\n for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {\n const row = node.children[rowIdx] as MarkdownTableRow;\n const isHeader = rowIdx === 0 && row.children.some((c) => (c as MarkdownTableCell).isHeader);\n\n // Calculate row height\n const rowCellHeights = row.children.map((cell) => {\n const cellNode = cell as MarkdownTableCell;\n const spans = flattenInlines(cellNode.children, ctx, {\n bold: isHeader,\n italic: false,\n code: false,\n });\n const lines = wrapSpans(spans, colWidth - 2 * TABLE_CELL_PAD_X);\n return lines.length * ctx.fontSize * LINE_HEIGHT_FACTOR + 2 * TABLE_CELL_PAD_Y;\n });\n const rowHeight = Math.max(\n ...rowCellHeights,\n ctx.fontSize * LINE_HEIGHT_FACTOR + 2 * TABLE_CELL_PAD_Y,\n );\n\n ensureSpace(ctx, rowHeight);\n\n // Draw header background\n if (isHeader) {\n ctx.page.drawRectangle({\n x: x0,\n y: ctx.y - rowHeight,\n width: tableWidth,\n height: rowHeight,\n color: rgb(COLOR_TABLE_HEADER_BG.r, COLOR_TABLE_HEADER_BG.g, COLOR_TABLE_HEADER_BG.b),\n });\n }\n\n // Draw cell borders and text\n for (let colIdx = 0; colIdx < numCols; colIdx++) {\n const cellX = x0 + colIdx * colWidth;\n\n // Draw cell border\n ctx.page.drawRectangle({\n x: cellX,\n y: ctx.y - rowHeight,\n width: colWidth,\n height: rowHeight,\n borderColor: rgb(COLOR_TABLE_BORDER.r, COLOR_TABLE_BORDER.g, COLOR_TABLE_BORDER.b),\n borderWidth: TABLE_BORDER_WIDTH,\n });\n\n if (colIdx < row.children.length) {\n const cellNode = row.children[colIdx] as MarkdownTableCell;\n const spans = flattenInlines(cellNode.children, ctx, {\n bold: isHeader,\n italic: false,\n code: false,\n });\n\n // Draw text inside cell\n const savedY = ctx.y;\n ctx.y = ctx.y - TABLE_CELL_PAD_Y;\n\n const cellAvailableWidth = colWidth - 2 * TABLE_CELL_PAD_X;\n const wrappedLines = wrapSpans(spans, cellAvailableWidth);\n\n for (const line of wrappedLines) {\n let lx = cellX + TABLE_CELL_PAD_X;\n for (const span of line) {\n ctx.page.drawText(span.text, {\n x: lx,\n y: ctx.y - span.fontSize,\n size: span.fontSize,\n font: span.font,\n color: rgb(span.color.r, span.color.g, span.color.b),\n });\n lx += span.font.widthOfTextAtSize(span.text, span.fontSize);\n }\n ctx.y -= getLineHeight(line);\n }\n\n ctx.y = savedY; // restore for next cell\n }\n }\n\n ctx.y -= rowHeight;\n }\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Thematic Break ----\n\nfunction renderThematicBreak(ctx: ExportContext, extraIndent: number): void {\n ctx.y -= PARAGRAPH_SPACING;\n ensureSpace(ctx, 10);\n\n const x0 = ctx.margin + extraIndent;\n const x1 = ctx.margin + ctx.contentWidth;\n\n ctx.page.drawLine({\n start: { x: x0, y: ctx.y },\n end: { x: x1, y: ctx.y },\n thickness: 1,\n color: rgb(COLOR_THEMATIC_BREAK.r, COLOR_THEMATIC_BREAK.g, COLOR_THEMATIC_BREAK.b),\n });\n\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- HTML Block ----\n\nfunction renderHtmlBlock(node: MarkdownHtmlBlock, ctx: ExportContext, extraIndent: number): void {\n if (!node.rawHtml) return;\n // Render raw HTML as monospace text (best effort)\n const lines = node.rawHtml.split('\\n');\n const x0 = ctx.margin + extraIndent;\n for (const line of lines) {\n if (line.trim().length === 0) continue;\n const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n ctx.page.drawText(line, {\n x: x0,\n y: ctx.y - CODE_FONT_SIZE,\n size: CODE_FONT_SIZE,\n font: ctx.fonts.mono,\n color: rgb(COLOR_CODE_TEXT.r, COLOR_CODE_TEXT.g, COLOR_CODE_TEXT.b),\n });\n ctx.y -= lineH;\n }\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Math Block ----\n\nfunction renderMathBlock(node: MarkdownMathBlock, ctx: ExportContext, extraIndent: number): void {\n // Render LaTeX source in monospace as fallback\n const lines = node.value.split('\\n');\n const x0 = ctx.margin + extraIndent;\n for (const line of lines) {\n const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n if (line.length > 0) {\n ctx.page.drawText(line, {\n x: x0,\n y: ctx.y - CODE_FONT_SIZE,\n size: CODE_FONT_SIZE,\n font: ctx.fonts.mono,\n color: rgb(COLOR_CODE_TEXT.r, COLOR_CODE_TEXT.g, COLOR_CODE_TEXT.b),\n });\n }\n ctx.y -= lineH;\n }\n ctx.y -= PARAGRAPH_SPACING;\n}\n\n// ---- Footnote Definition ----\n\nfunction renderFootnoteDefinition(\n node: MarkdownFootnoteDefinition,\n ctx: ExportContext,\n extraIndent: number,\n): void {\n // Draw footnote identifier\n const label = `[${node.identifier}]`;\n const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n\n ctx.page.drawText(label, {\n x: ctx.margin + extraIndent,\n y: ctx.y - ctx.fontSize * 0.75,\n size: ctx.fontSize * 0.75,\n font: ctx.fonts.bold,\n color: rgb(ctx.colors.link.r, ctx.colors.link.g, ctx.colors.link.b),\n });\n\n // Render children indented\n renderBlocks(node.children, ctx, extraIndent + LIST_INDENT);\n}\n\n// ---- Fallback ----\n\nfunction renderFallbackBlock(\n node: MarkdownBlockNode,\n ctx: ExportContext,\n extraIndent: number,\n): void {\n const fallback = node as unknown as {\n children?: (MarkdownBlockNode | MarkdownInlineNode)[];\n value?: string;\n };\n if (fallback.children && Array.isArray(fallback.children)) {\n // Could be block children or inline children\n if (fallback.children.length > 0 && typeof fallback.children[0]?.type === 'string') {\n const firstType = fallback.children[0].type;\n // Heuristic: if first child looks like an inline node, wrap as paragraph\n const inlineTypes = new Set([\n 'text',\n 'strong',\n 'emphasis',\n 'delete',\n 'inlineCode',\n 'link',\n 'image',\n 'break',\n ]);\n if (inlineTypes.has(firstType)) {\n const spans = flattenInlines(fallback.children as MarkdownInlineNode[], ctx, {\n bold: false,\n italic: false,\n code: false,\n });\n drawSpans(spans, ctx, ctx.contentWidth - extraIndent, ctx.margin + extraIndent);\n ctx.y -= PARAGRAPH_SPACING;\n return;\n }\n }\n renderBlocks(fallback.children as MarkdownBlockNode[], ctx, extraIndent);\n } else if (fallback.value && typeof fallback.value === 'string') {\n const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;\n ensureSpace(ctx, lineH);\n ctx.page.drawText(fallback.value, {\n x: ctx.margin + extraIndent,\n y: ctx.y - ctx.fontSize,\n size: ctx.fontSize,\n font: ctx.fonts.regular,\n color: rgb(ctx.colors.text.r, ctx.colors.text.g, ctx.colors.text.b),\n });\n ctx.y -= lineH + PARAGRAPH_SPACING;\n }\n}\n\n// ============================================\n// Color Helpers\n// ============================================\n\nfunction hexToRgb(hex: string): RgbColor | undefined {\n const h = hex.startsWith('#') ? hex.slice(1) : hex;\n if (h.length !== 6) return undefined;\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n if (isNaN(r) || isNaN(g) || isNaN(b)) return undefined;\n return { r: r / 255, g: g / 255, b: b / 255 };\n}\n","/**\n * PDF Style / Layout Constants\n *\n * Shared constants used by both PDF export and import. Defines page\n * dimensions, margins, font sizes, colours, and spacing for rendering\n * MarkdownDocument content as a paginated PDF.\n */\n\n// ============================================\n// Page Layout\n// ============================================\n\n/** Standard US Letter page width in points (8.5 in × 72 pt/in). */\nexport const PAGE_WIDTH_LETTER = 612;\n\n/** Standard US Letter page height in points (11 in × 72 pt/in). */\nexport const PAGE_HEIGHT_LETTER = 792;\n\n/** A4 page width in points (210 mm ≈ 595 pt). */\nexport const PAGE_WIDTH_A4 = 595.28;\n\n/** A4 page height in points (297 mm ≈ 842 pt). */\nexport const PAGE_HEIGHT_A4 = 841.89;\n\n/** Default page margins in points (1 inch = 72 pt). */\nexport const DEFAULT_MARGIN = 72;\n\n// ============================================\n// Fonts — pdf-lib standard font names\n// ============================================\n\nexport const FONT_REGULAR = 'Helvetica';\nexport const FONT_BOLD = 'Helvetica-Bold';\nexport const FONT_ITALIC = 'Helvetica-Oblique';\nexport const FONT_BOLD_ITALIC = 'Helvetica-BoldOblique';\nexport const FONT_MONO = 'Courier';\nexport const FONT_MONO_BOLD = 'Courier-Bold';\n\n// ============================================\n// Font Sizes\n// ============================================\n\n/** Default body font size in points. */\nexport const DEFAULT_FONT_SIZE = 11;\n\n/** Heading font sizes indexed by depth (1-based). */\nexport const HEADING_SIZES: Record<number, number> = {\n 1: 24,\n 2: 20,\n 3: 16,\n 4: 14,\n 5: 13,\n 6: 12,\n};\n\n/** Font size used for code blocks and inline code. */\nexport const CODE_FONT_SIZE = 10;\n\n// ============================================\n// Spacing (in points)\n// ============================================\n\n/** Line height multiplier (leading). */\nexport const LINE_HEIGHT_FACTOR = 1.4;\n\n/** Space above a heading. */\nexport const HEADING_SPACE_BEFORE = 18;\n\n/** Space below a heading. */\nexport const HEADING_SPACE_AFTER = 6;\n\n/** Space between paragraphs. */\nexport const PARAGRAPH_SPACING = 8;\n\n/** Extra indent per list nesting level. */\nexport const LIST_INDENT = 24;\n\n/** Bullet character for unordered lists. */\nexport const BULLET_CHAR = '\\u2022'; // •\n\n/** Indent for blockquotes. */\nexport const BLOCKQUOTE_INDENT = 24;\n\n/** Width of the blockquote left bar. */\nexport const BLOCKQUOTE_BAR_WIDTH = 3;\n\n// ============================================\n// Colours (RGB 0-1)\n// ============================================\n\nexport const COLOR_TEXT = { r: 0.12, g: 0.12, b: 0.14 };\nexport const COLOR_HEADING = { r: 0.08, g: 0.08, b: 0.1 };\nexport const COLOR_LINK = { r: 0.05, g: 0.27, b: 0.73 };\nexport const COLOR_CODE_BG = { r: 0.95, g: 0.95, b: 0.97 };\nexport const COLOR_CODE_TEXT = { r: 0.2, g: 0.2, b: 0.22 };\nexport const COLOR_BLOCKQUOTE_BAR = { r: 0.75, g: 0.75, b: 0.78 };\nexport const COLOR_BLOCKQUOTE_TEXT = { r: 0.35, g: 0.35, b: 0.38 };\nexport const COLOR_THEMATIC_BREAK = { r: 0.78, g: 0.78, b: 0.8 };\nexport const COLOR_TABLE_BORDER = { r: 0.75, g: 0.75, b: 0.78 };\nexport const COLOR_TABLE_HEADER_BG = { r: 0.93, g: 0.93, b: 0.95 };\n\n// ============================================\n// Table Constants\n// ============================================\n\n/** Table cell horizontal padding in points. */\nexport const TABLE_CELL_PAD_X = 6;\n\n/** Table cell vertical padding in points. */\nexport const TABLE_CELL_PAD_Y = 4;\n\n/** Table border line width. */\nexport const TABLE_BORDER_WIDTH = 0.5;\n\n// ============================================\n// Import Heuristic Thresholds\n// ============================================\n\n/**\n * Minimum font size (in PDF points) to consider as a heading.\n * Anything ≥ this and larger than the detected body size is a heading.\n */\nexport const IMPORT_HEADING_MIN_SIZE = 13;\n\n/**\n * Font size ranges mapped to heading depth for import heuristics.\n * Applied when a text item's font size exceeds the body size.\n */\nexport const IMPORT_HEADING_SIZE_RANGES: Array<{ min: number; depth: number }> = [\n { min: 22, depth: 1 },\n { min: 18, depth: 2 },\n { min: 15, depth: 3 },\n { min: 13.5, depth: 4 },\n { min: 12.5, depth: 5 },\n { min: 12, depth: 6 },\n];\n\n/**\n * Y-distance (in points) between lines that implies a paragraph break\n * rather than a continuation of the same paragraph.\n */\nexport const IMPORT_PARAGRAPH_GAP = 4;\n\n/**\n * Characters that indicate an unordered list bullet at the start of a line.\n */\nexport const IMPORT_BULLET_CHARS = new Set([\n '\\u2022',\n '\\u2023',\n '\\u25E6',\n '\\u2043',\n '-',\n '*',\n '\\u2013',\n '\\u2014',\n '\\u25AA',\n '\\u25AB',\n]);\n\n/**\n * Regex to detect an ordered list prefix (e.g. \"1.\", \"2)\", \"iv.\").\n */\nexport const IMPORT_ORDERED_PREFIX = /^(\\d+|[a-z]+|[ivxlcdm]+)[.)]\\s/i;\n\n/**\n * Column alignment tolerance — text items whose x-positions differ by\n * less than this are considered to be in the same column (for table detection).\n */\nexport const IMPORT_COLUMN_TOLERANCE = 8;\n\n/**\n * Minimum number of consecutive rows with the same column count to\n * consider them a table.\n */\nexport const IMPORT_TABLE_MIN_ROWS = 2;\n\n/**\n * URL pattern for detecting links in extracted text.\n */\nexport const IMPORT_URL_PATTERN = /https?:\\/\\/[^\\s)>\\]]+/g;\n","/**\n * PDF Import\n *\n * Parses a PDF file and converts its content into a squisq\n * MarkdownDocument (or Doc) using heuristic detection of headings,\n * lists, code blocks, tables, blockquotes, and hyperlinks.\n *\n * Uses pdfjs-dist (Mozilla pdf.js) for text extraction — a battle-tested,\n * browser-compatible PDF parser. Since PDFs encode positioned glyphs\n * rather than semantic structure, all structure detection is inherently\n * heuristic and works best on simply-formatted documents.\n *\n * @example\n * ```ts\n * import { pdfToMarkdownDoc } from '@bendyline/squisq-formats/pdf';\n *\n * const response = await fetch('document.pdf');\n * const data = await response.arrayBuffer();\n * const doc = await pdfToMarkdownDoc(data);\n * ```\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { markdownToDoc } from '@bendyline/squisq/doc';\nimport { stringifyMarkdown } from '@bendyline/squisq/markdown';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownInlineNode,\n MarkdownHeading,\n MarkdownParagraph,\n MarkdownBlockquote,\n MarkdownList,\n MarkdownListItem,\n MarkdownCodeBlock,\n MarkdownTable,\n MarkdownTableRow,\n MarkdownTableCell,\n MarkdownText,\n MarkdownEmphasis,\n MarkdownStrong,\n MarkdownInlineCode,\n MarkdownLink,\n MarkdownImage,\n} from '@bendyline/squisq/markdown';\n\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport { buildContainer } from '../shared/container.js';\n\nimport {\n DEFAULT_FONT_SIZE,\n IMPORT_HEADING_MIN_SIZE,\n IMPORT_HEADING_SIZE_RANGES,\n IMPORT_PARAGRAPH_GAP,\n IMPORT_BULLET_CHARS,\n IMPORT_ORDERED_PREFIX,\n IMPORT_COLUMN_TOLERANCE,\n IMPORT_TABLE_MIN_ROWS,\n IMPORT_URL_PATTERN,\n} from './styles.js';\n\n// ============================================\n// Public API\n// ============================================\n\n/**\n * Options for PDF import.\n */\nexport interface PdfImportOptions {\n /**\n * Hint for the body font size used in the PDF (in points).\n * Text items larger than this are considered headings.\n * If not provided, the importer detects the most common font size.\n */\n bodyFontSize?: number;\n\n /** Whether to detect tables from column-aligned text. Default: true. */\n detectTables?: boolean;\n\n /** Whether to detect code blocks from monospace fonts. Default: true. */\n detectCodeBlocks?: boolean;\n\n /** Whether to detect blockquotes from indentation. Default: true. */\n detectBlockquotes?: boolean;\n\n /** Whether to detect URLs in text and convert to links. Default: true. */\n detectLinks?: boolean;\n}\n\n/**\n * Convert a PDF file to a MarkdownDocument.\n *\n * Structure detection is heuristic — results are best-effort.\n *\n * @param data - The raw PDF file as ArrayBuffer, Uint8Array, or Blob\n * @param options - Import options\n * @returns A MarkdownDocument representing the detected content\n */\nexport async function pdfToMarkdownDoc(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<MarkdownDocument> {\n const bytes =\n data instanceof Blob\n ? new Uint8Array(await data.arrayBuffer())\n : data instanceof ArrayBuffer\n ? new Uint8Array(data)\n : data;\n\n const textLines = await extractTextLines(bytes);\n\n if (textLines.length === 0) {\n return { type: 'document', children: [] };\n }\n\n const bodySize = options.bodyFontSize ?? detectBodyFontSize(textLines);\n const blocks = classifyLines(textLines, bodySize, options);\n\n return { type: 'document', children: blocks };\n}\n\n/**\n * Convert a PDF file to a squisq Doc.\n *\n * Convenience wrapper: PDF → MarkdownDocument → Doc.\n */\nexport async function pdfToDoc(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<Doc> {\n const markdownDoc = await pdfToMarkdownDoc(data, options);\n return markdownToDoc(markdownDoc);\n}\n\n/**\n * Convert a PDF file to a ContentContainer with markdown + extracted images.\n *\n * The container will contain:\n * - The primary markdown document (index.md)\n * - Any embedded images under images/ (e.g., images/image1.png)\n *\n * Image extraction uses pdfjs-dist's operator list API and requires a browser\n * environment (canvas is used to encode pixel data to PNG).\n *\n * @param data - The raw PDF file as ArrayBuffer, Uint8Array, or Blob\n * @param options - Import options\n * @returns A ContentContainer with the document and its media\n */\nexport async function pdfToContainer(\n data: ArrayBuffer | Uint8Array | Blob,\n options: PdfImportOptions = {},\n): Promise<ContentContainer> {\n const bytes =\n data instanceof Blob\n ? new Uint8Array(await data.arrayBuffer())\n : data instanceof ArrayBuffer\n ? new Uint8Array(data)\n : data;\n\n const textLines = await extractTextLines(bytes);\n const images = await extractImages(bytes);\n\n // Under Node there is no DOM/canvas, so extractImages() returns [] via its\n // environment guard and embedded images can't be decoded. The registry's\n // `importContainer` wrapper surfaces this (typeof document === 'undefined')\n // as a ConversionResult warning; direct callers of pdfToContainer get no\n // signal, which is an accepted trade-off for the degraded Node path.\n\n const bodySize = options.bodyFontSize ?? detectBodyFontSize(textLines);\n\n // Classify text into blocks, capturing the originating page of each block\n // (parallel array) so images can be placed on their own page rather than\n // dumped at the document end.\n const blockPages: number[] = [];\n let blocks = classifyLines(textLines, bodySize, options, blockPages);\n if (images.length > 0) {\n blocks = insertImageBlocks(blocks, blockPages, images);\n }\n\n const markdownDoc: MarkdownDocument = { type: 'document', children: blocks };\n\n // pdf image extraction only ever produces PNG (canvas re-encode), so every\n // entry gets image/png.\n return buildContainer(\n stringifyMarkdown(markdownDoc),\n images.map((img) => [img.path, { data: img.data, mimeType: 'image/png' }] as const),\n );\n}\n\n/**\n * Extracted image with position info for placement.\n *\n * `page` is captured reliably from the operator-list walk. `y` is currently\n * always 0 — recovering a real y within the page would require tracking the\n * current transformation matrix (CTM) during the paint operator, which is out\n * of scope here (see the placement note in `insertImageBlocks`).\n *\n * Exported for direct unit testing of `insertImageBlocks`.\n */\nexport interface ExtractedImage {\n path: string;\n data: ArrayBuffer;\n page: number;\n y: number;\n}\n\n/**\n * Extract embedded images from a PDF using pdfjs-dist operator list API.\n * Requires browser canvas for PNG encoding.\n */\nasync function extractImages(data: Uint8Array): Promise<ExtractedImage[]> {\n // Canvas is required for PNG encoding — skip in non-browser environments\n if (typeof document === 'undefined') return [];\n\n let pdfjsLib: PdfjsLib & { OPS?: Record<string, number> };\n try {\n pdfjsLib = (await import('pdfjs-dist/legacy/build/pdf.mjs')) as unknown as PdfjsLib & {\n OPS?: Record<string, number>;\n };\n } catch {\n pdfjsLib = (await import('pdfjs-dist')) as unknown as PdfjsLib & {\n OPS?: Record<string, number>;\n };\n }\n\n await applyWorkerConfig(pdfjsLib);\n\n const OPS_paintImageXObject = pdfjsLib.OPS?.paintImageXObject ?? 85;\n\n const loadingTask = pdfjsLib.getDocument({\n data,\n isEvalSupported: false,\n useSystemFonts: true,\n });\n\n const pdf = await loadingTask.promise;\n const images: ExtractedImage[] = [];\n let counter = 0;\n\n for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {\n const page = (await pdf.getPage(pageNum)) as PdfjsPageFull;\n if (!page.getOperatorList) continue;\n\n const opList = await page.getOperatorList();\n const seen = new Set<string>();\n\n for (let i = 0; i < opList.fnArray.length; i++) {\n if (opList.fnArray[i] !== OPS_paintImageXObject) continue;\n\n const imgName = opList.argsArray[i]?.[0];\n if (!imgName || typeof imgName !== 'string' || seen.has(imgName)) continue;\n seen.add(imgName);\n\n try {\n const imgData = page.objs?.get(imgName) as PdfjsImageData | null;\n if (!imgData?.data || !imgData.width || !imgData.height) continue;\n\n const pngData = imageDataToPng(imgData);\n if (!pngData) continue;\n\n counter++;\n images.push({\n path: `images/image${counter}.png`,\n data: pngData,\n page: pageNum - 1,\n y: 0,\n });\n } catch {\n // Skip images that fail to extract\n }\n }\n }\n\n return images;\n}\n\n/** Minimal pdfjs image data shape. */\ninterface PdfjsImageData {\n width: number;\n height: number;\n data: Uint8ClampedArray;\n kind?: number;\n}\n\n/** Extended PdfjsPage with operator list and objs access. */\ninterface PdfjsPageFull extends PdfjsPage {\n getOperatorList(): Promise<{ fnArray: number[]; argsArray: unknown[][] }>;\n objs?: { get(name: string): unknown; has?(name: string): boolean };\n}\n\n/** Encode pdfjs image data to PNG using a canvas element. */\nfunction imageDataToPng(img: PdfjsImageData): ArrayBuffer | null {\n try {\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n\n // pdfjs kind=1 is GRAYSCALE, kind=2 is RGB, kind=3 is RGBA\n let imageData: ImageData;\n if (img.kind === 3 || img.data.length === img.width * img.height * 4) {\n // RGBA — use directly\n imageData = new ImageData(new Uint8ClampedArray(img.data), img.width, img.height);\n } else if (img.kind === 2 || img.data.length === img.width * img.height * 3) {\n // RGB — expand to RGBA\n const rgba = new Uint8ClampedArray(img.width * img.height * 4);\n for (let j = 0, k = 0; j < img.data.length; j += 3, k += 4) {\n rgba[k] = img.data[j];\n rgba[k + 1] = img.data[j + 1];\n rgba[k + 2] = img.data[j + 2];\n rgba[k + 3] = 255;\n }\n imageData = new ImageData(rgba, img.width, img.height);\n } else if (img.kind === 1 || img.data.length === img.width * img.height) {\n // Grayscale — expand to RGBA\n const rgba = new Uint8ClampedArray(img.width * img.height * 4);\n for (let j = 0, k = 0; j < img.data.length; j++, k += 4) {\n rgba[k] = img.data[j];\n rgba[k + 1] = img.data[j];\n rgba[k + 2] = img.data[j];\n rgba[k + 3] = 255;\n }\n imageData = new ImageData(rgba, img.width, img.height);\n } else {\n return null;\n }\n\n ctx.putImageData(imageData, 0, 0);\n\n // Convert canvas to PNG ArrayBuffer\n const dataUrl = canvas.toDataURL('image/png');\n const base64 = dataUrl.split(',')[1];\n const binaryStr = atob(base64);\n const bytes = new Uint8Array(binaryStr.length);\n for (let i = 0; i < binaryStr.length; i++) {\n bytes[i] = binaryStr.charCodeAt(i);\n }\n return bytes.buffer;\n } catch {\n return null;\n }\n}\n\n/** Wrap an extracted image as a paragraph containing an image node. */\nfunction imageParagraph(img: ExtractedImage): MarkdownParagraph {\n const imgNode: MarkdownImage = {\n type: 'image',\n url: img.path,\n alt: `Image ${img.path.replace('images/image', '').replace('.png', '')}`,\n };\n return { type: 'paragraph', children: [imgNode] };\n}\n\n/**\n * Insert image reference blocks among the text blocks, page by page.\n *\n * Each image is placed immediately after the LAST content block that\n * originated from the same page (per the parallel `blockPages` array).\n * Images on a page that produced no text blocks (e.g. an image-only page)\n * fall back to the last block of the nearest preceding page that did; if no\n * such page exists, they are appended at the document end.\n *\n * Placement is intentionally page-level only. Ordering *within* a page follows\n * the extraction order of the images (roughly the paint-operator order) — real\n * vertical (y) ordering within a page is future work, since `ExtractedImage.y`\n * is not yet populated (it needs CTM tracking during the paint operator).\n *\n * @param blocks - The classified content blocks, in document order.\n * @param blockPages - Parallel array: `blockPages[i]` is the 0-based page that\n * `blocks[i]` came from. Must be the same length as `blocks`.\n * @param images - Extracted images with a reliable `page` field.\n */\nexport function insertImageBlocks(\n blocks: MarkdownBlockNode[],\n blockPages: number[],\n images: ExtractedImage[],\n): MarkdownBlockNode[] {\n if (images.length === 0) return blocks;\n\n // No text blocks at all → every image simply appends in order.\n if (blocks.length === 0) {\n return images.map(imageParagraph);\n }\n\n // Map each page → index of its LAST block, and remember which pages have\n // blocks (sorted ascending) for the image-only-page fallback lookup.\n const lastBlockIndexByPage = new Map<number, number>();\n for (let i = 0; i < blocks.length; i++) {\n lastBlockIndexByPage.set(blockPages[i], i);\n }\n const pagesWithBlocks = [...lastBlockIndexByPage.keys()].sort((a, b) => a - b);\n\n const lastIndex = blocks.length - 1;\n\n /** Resolve the block index after which an image on `page` should be inserted. */\n const anchorFor = (page: number): number => {\n const direct = lastBlockIndexByPage.get(page);\n if (direct !== undefined) return direct;\n // Image-only page: fall back to the nearest preceding page with blocks.\n let anchor = -1;\n for (const p of pagesWithBlocks) {\n if (p < page) anchor = lastBlockIndexByPage.get(p)!;\n else break;\n }\n // No preceding page with blocks → append at the document end.\n return anchor === -1 ? lastIndex : anchor;\n };\n\n // Group image paragraphs by the block index they should follow, preserving\n // image order within each group.\n const insertAfter = new Map<number, MarkdownParagraph[]>();\n for (const img of images) {\n const anchor = anchorFor(img.page);\n const group = insertAfter.get(anchor);\n if (group) group.push(imageParagraph(img));\n else insertAfter.set(anchor, [imageParagraph(img)]);\n }\n\n const result: MarkdownBlockNode[] = [];\n for (let i = 0; i < blocks.length; i++) {\n result.push(blocks[i]);\n const imgs = insertAfter.get(i);\n if (imgs) result.push(...imgs);\n }\n return result;\n}\n\n// ============================================\n// Internal Types\n// ============================================\n\n/** A single text item extracted from pdfjs. */\ninterface TextItem {\n str: string;\n x: number;\n y: number;\n width: number;\n height: number;\n /** Internal font ID from pdfjs (e.g. \"g_d0_f1\") */\n fontName: string;\n /** Resolved font family from pdfjs styles (e.g. \"sans-serif\", \"monospace\") */\n fontFamily: string;\n}\n\n/** A logical line: text items at roughly the same y-coordinate. */\ninterface TextLine {\n items: TextItem[];\n y: number;\n /** The page this line is on (0-based). */\n page: number;\n /** The predominant font size on this line. */\n fontSize: number;\n /** The predominant font family on this line. */\n fontFamily: string;\n /** The predominant font ID on this line (may contain bold/italic hints for embedded fonts). */\n fontName: string;\n /** The minimum x position (left edge). */\n minX: number;\n /** Full concatenated text. */\n text: string;\n}\n\n// ============================================\n// PDF Text Extraction (pdfjs-dist)\n// ============================================\n\n/**\n * Configure the pdfjs-dist PDF worker source URL.\n *\n * pdfjs-dist requires a worker for PDF parsing. In the **browser**, bundlers\n * (Vite, webpack) typically handle this automatically, or you can point to a\n * CDN-hosted worker script. In **Node.js / SSR / test** environments, call\n * this with a `file://` URL to the worker module **before** any import call.\n *\n * @example\n * ```ts\n * // Browser — CDN\n * configurePdfWorker('https://cdn.jsdelivr.net/npm/pdfjs-dist@4/legacy/build/pdf.worker.min.mjs');\n *\n * // Node / vitest — file URL\n * import { pathToFileURL } from 'url';\n * configurePdfWorker(pathToFileURL(require.resolve('pdfjs-dist/legacy/build/pdf.worker.mjs')).href);\n * ```\n */\nexport function configurePdfWorker(workerSrc: string): void {\n _workerSrc = workerSrc;\n}\n\n/** Module-level storage for the worker source URL. */\nlet _workerSrc: string | undefined;\n\n/** Minimal typed surface of the pdfjs-dist library used by the import path. */\ninterface PdfjsLib {\n GlobalWorkerOptions?: { workerSrc?: string };\n getDocument(params: { data: Uint8Array; isEvalSupported?: boolean; useSystemFonts?: boolean }): {\n promise: Promise<PdfjsDocument>;\n };\n}\n\ninterface PdfjsDocument {\n numPages: number;\n getPage(pageNum: number): Promise<PdfjsPage>;\n}\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 /**\n * Optional out-parameter: when provided, receives one entry per produced\n * block giving the 0-based page that block originated from. Kept parallel\n * to the returned block array so image placement can be page-aware without\n * mutating the (strictly-typed) block nodes themselves.\n */\n blockPages?: number[],\n): MarkdownBlockNode[] {\n const blocks: MarkdownBlockNode[] = [];\n /** Push a block and record its originating page in the parallel array. */\n const pushBlock = (block: MarkdownBlockNode, page: number): void => {\n blocks.push(block);\n if (blockPages) blockPages.push(page);\n };\n const detectTables = options.detectTables !== false;\n const detectCodeBlocks = options.detectCodeBlocks !== false;\n const detectBlockquotes = options.detectBlockquotes !== false;\n const _detectLinks = options.detectLinks !== false;\n\n // Determine typical left margin (most common minX)\n const leftMargins = lines.map((l) => Math.round(l.minX));\n const typicalLeftMargin = mode(leftMargins) || 72;\n\n let i = 0;\n while (i < lines.length) {\n const line = lines[i];\n\n // --- Heading detection ---\n if (line.fontSize >= IMPORT_HEADING_MIN_SIZE && line.fontSize > bodySize + 1) {\n const depth = sizeToHeadingDepth(line.fontSize);\n pushBlock(\n {\n type: 'heading',\n depth,\n children: buildInlineNodes(line, options),\n } as MarkdownHeading,\n line.page,\n );\n i++;\n continue;\n }\n\n // --- Code block detection (monospace font runs) ---\n if (detectCodeBlocks && isMonospaceLine(line)) {\n const codeLines: string[] = [];\n while (i < lines.length && isMonospaceLine(lines[i])) {\n codeLines.push(lines[i].text);\n i++;\n }\n pushBlock(\n {\n type: 'code',\n value: codeLines.join('\\n'),\n } as MarkdownCodeBlock,\n line.page,\n );\n continue;\n }\n\n // --- Table detection (column-aligned consecutive lines) ---\n if (detectTables && i + 1 < lines.length) {\n const tableLines = tryDetectTable(lines, i, typicalLeftMargin);\n if (tableLines > 0) {\n const table = buildTable(lines.slice(i, i + tableLines), options);\n if (table) {\n pushBlock(table, line.page);\n i += tableLines;\n continue;\n }\n }\n }\n\n // --- List detection ---\n const bulletMatch = tryMatchBullet(line.text);\n const orderedMatch = line.text.match(IMPORT_ORDERED_PREFIX);\n if (bulletMatch || orderedMatch) {\n const listResult = consumeList(lines, i, typicalLeftMargin, bodySize, options);\n pushBlock(listResult.list, line.page);\n i = listResult.nextIndex;\n continue;\n }\n\n // --- Blockquote detection (indented text) ---\n if (detectBlockquotes && line.minX > typicalLeftMargin + 20) {\n const quoteLines: TextLine[] = [];\n while (\n i < lines.length &&\n lines[i].minX > typicalLeftMargin + 20 &&\n !isMonospaceLine(lines[i]) &&\n lines[i].fontSize <= bodySize + 1\n ) {\n quoteLines.push(lines[i]);\n i++;\n }\n const quoteBlocks: MarkdownBlockNode[] = quoteLines.map(\n (ql) =>\n ({\n type: 'paragraph',\n children: buildInlineNodes(ql, options),\n }) as MarkdownParagraph,\n );\n pushBlock(\n {\n type: 'blockquote',\n children: quoteBlocks,\n } as MarkdownBlockquote,\n line.page,\n );\n continue;\n }\n\n // --- Regular paragraph ---\n // Merge consecutive body-sized lines on the same page with small y-gaps\n const paraLines: TextLine[] = [line];\n i++;\n while (i < lines.length) {\n const next = lines[i];\n // Same page, same-ish font size, close y (within line-height gap), not bullet/heading\n if (\n next.page === line.page &&\n Math.abs(next.fontSize - bodySize) <= 1 &&\n !isMonospaceLine(next) &&\n next.minX <= typicalLeftMargin + 15 &&\n !tryMatchBullet(next.text) &&\n !next.text.match(IMPORT_ORDERED_PREFIX)\n ) {\n // Check y-gap: lines are sorted top-to-bottom so y decreases\n const yGap = paraLines[paraLines.length - 1].y - next.y;\n const lineHeight = bodySize * 1.6;\n if (yGap > 0 && yGap < lineHeight + IMPORT_PARAGRAPH_GAP) {\n paraLines.push(next);\n i++;\n } else {\n break;\n }\n } else {\n break;\n }\n }\n\n // Build paragraph from merged lines\n const allInlines: MarkdownInlineNode[] = [];\n for (let j = 0; j < paraLines.length; j++) {\n if (j > 0) {\n allInlines.push({ type: 'text', value: ' ' } as MarkdownText);\n }\n allInlines.push(...buildInlineNodes(paraLines[j], options));\n }\n\n if (allInlines.length > 0) {\n pushBlock(\n {\n type: 'paragraph',\n children: mergeAdjacentText(allInlines),\n } as MarkdownParagraph,\n line.page,\n );\n }\n }\n\n return blocks;\n}\n\n// ============================================\n// Heading Depth Mapping\n// ============================================\n\nfunction sizeToHeadingDepth(fontSize: number): 1 | 2 | 3 | 4 | 5 | 6 {\n for (const range of IMPORT_HEADING_SIZE_RANGES) {\n if (fontSize >= range.min) return range.depth as 1 | 2 | 3 | 4 | 5 | 6;\n }\n return 6;\n}\n\n// ============================================\n// Font Heuristics\n// ============================================\n\n/**\n * Check if a line is predominantly monospace.\n * Uses the resolved fontFamily from pdfjs styles first,\n * falls back to fontName pattern matching for embedded fonts.\n */\nfunction isMonospaceLine(line: TextLine): boolean {\n return isMonospaceFamily(line.fontFamily) || isMonospaceName(line.fontName);\n}\n\n/**\n * Check if a text item is monospace.\n */\nfunction isMonospaceItem(item: TextItem): boolean {\n return isMonospaceFamily(item.fontFamily) || isMonospaceName(item.fontName);\n}\n\nfunction isMonospaceFamily(fontFamily: string): boolean {\n const lower = fontFamily.toLowerCase();\n return lower === 'monospace' || lower.includes('monospace');\n}\n\nfunction isMonospaceName(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return (\n lower.includes('courier') ||\n lower.includes('mono') ||\n lower.includes('consolas') ||\n lower.includes('menlo') ||\n lower.includes('inconsolata') ||\n lower.includes('firacode') ||\n lower.includes('source code') ||\n lower.includes('dejavu sans mono')\n );\n}\n\nfunction isBoldFont(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return lower.includes('bold') || lower.includes('black') || lower.includes('heavy');\n}\n\nfunction isItalicFont(fontName: string): boolean {\n const lower = fontName.toLowerCase();\n return lower.includes('italic') || lower.includes('oblique') || lower.includes('slanted');\n}\n\n// ============================================\n// Inline Node Construction\n// ============================================\n\nfunction buildInlineNodes(line: TextLine, options: PdfImportOptions): MarkdownInlineNode[] {\n const nodes: MarkdownInlineNode[] = [];\n const detectLinksOpt = options.detectLinks !== false;\n\n for (const item of line.items) {\n const text = item.str;\n if (!text || text.trim().length === 0) continue;\n\n const bold = isBoldFont(item.fontName);\n const italic = isItalicFont(item.fontName);\n const mono = isMonospaceItem(item);\n\n let inlineNodes: MarkdownInlineNode[];\n\n if (mono) {\n inlineNodes = [{ type: 'inlineCode', value: text } as MarkdownInlineCode];\n } else if (detectLinksOpt) {\n inlineNodes = splitTextWithLinks(text);\n } else {\n inlineNodes = [{ type: 'text', value: text } as MarkdownText];\n }\n\n // Wrap in formatting\n for (const node of inlineNodes) {\n let wrapped: MarkdownInlineNode = node;\n if (italic) {\n wrapped = { type: 'emphasis', children: [wrapped] } as MarkdownEmphasis;\n }\n if (bold) {\n wrapped = { type: 'strong', children: [wrapped] } as MarkdownStrong;\n }\n nodes.push(wrapped);\n }\n }\n\n return nodes;\n}\n\n/**\n * Split a text string into text nodes and link nodes wherever\n * URL patterns are found.\n */\nfunction splitTextWithLinks(text: string): MarkdownInlineNode[] {\n const nodes: MarkdownInlineNode[] = [];\n let lastIndex = 0;\n\n // Reset regex state\n IMPORT_URL_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = IMPORT_URL_PATTERN.exec(text)) !== null) {\n // Text before URL\n if (match.index > lastIndex) {\n nodes.push({ type: 'text', value: text.slice(lastIndex, match.index) } as MarkdownText);\n }\n // URL as link\n const url = match[0];\n nodes.push({\n type: 'link',\n url,\n children: [{ type: 'text', value: url } as MarkdownText],\n } as MarkdownLink);\n lastIndex = match.index + url.length;\n }\n\n // Remaining text\n if (lastIndex < text.length) {\n nodes.push({ type: 'text', value: text.slice(lastIndex) } as MarkdownText);\n }\n\n return nodes.length > 0 ? nodes : [{ type: 'text', value: text } as MarkdownText];\n}\n\n// ============================================\n// List Detection\n// ============================================\n\nfunction tryMatchBullet(text: string): boolean {\n if (text.length === 0) return false;\n return IMPORT_BULLET_CHARS.has(text[0]) || IMPORT_BULLET_CHARS.has(text.trimStart()[0]);\n}\n\nfunction stripBullet(text: string): string {\n const trimmed = text.trimStart();\n if (IMPORT_BULLET_CHARS.has(trimmed[0])) {\n return trimmed.slice(1).trimStart();\n }\n return text;\n}\n\nfunction stripOrderedPrefix(text: string): string {\n return text.replace(IMPORT_ORDERED_PREFIX, '');\n}\n\ninterface ListResult {\n list: MarkdownList;\n nextIndex: number;\n}\n\nfunction consumeList(\n lines: TextLine[],\n startIdx: number,\n _typicalLeftMargin: number,\n _bodySize: number,\n _options: PdfImportOptions,\n): ListResult {\n const firstLine = lines[startIdx];\n const isOrdered = !!firstLine.text.match(IMPORT_ORDERED_PREFIX);\n const items: MarkdownListItem[] = [];\n let i = startIdx;\n\n while (i < lines.length) {\n const line = lines[i];\n const isBullet = tryMatchBullet(line.text);\n const isOrd = !!line.text.match(IMPORT_ORDERED_PREFIX);\n\n if (!isBullet && !isOrd) break;\n // All items in one list should be same type\n if (isOrdered && !isOrd) break;\n if (!isOrdered && !isBullet) break;\n\n const cleanText = isOrdered ? stripOrderedPrefix(line.text) : stripBullet(line.text);\n const para: MarkdownParagraph = {\n type: 'paragraph',\n children: splitTextWithLinks(cleanText),\n };\n items.push({\n type: 'listItem',\n children: [para],\n } as MarkdownListItem);\n i++;\n }\n\n return {\n list: {\n type: 'list',\n ordered: isOrdered,\n children: items,\n } as MarkdownList,\n nextIndex: i,\n };\n}\n\n// ============================================\n// Table Detection\n// ============================================\n\n/**\n * Look ahead from index `start` and return the number of consecutive\n * lines that form an aligned table, or 0 if no table detected.\n */\nfunction tryDetectTable(lines: TextLine[], start: number, _typicalLeftMargin: number): number {\n // A table needs multiple items per line (columns) on consecutive lines\n // with roughly the same x-alignment pattern.\n\n const firstLine = lines[start];\n if (firstLine.items.length < 2) return 0;\n\n const cols = getColumnPositions(firstLine);\n if (cols.length < 2) return 0;\n\n let count = 1;\n for (let i = start + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.items.length < 2) break;\n\n // Check if this line's columns align with the first line's\n const lineCols = getColumnPositions(line);\n if (lineCols.length !== cols.length) break;\n\n let aligned = true;\n for (let c = 0; c < cols.length; c++) {\n if (Math.abs(lineCols[c] - cols[c]) > IMPORT_COLUMN_TOLERANCE) {\n aligned = false;\n break;\n }\n }\n if (!aligned) break;\n count++;\n }\n\n return count >= IMPORT_TABLE_MIN_ROWS ? count : 0;\n}\n\nfunction getColumnPositions(line: TextLine): number[] {\n // Cluster item x-positions\n const positions: number[] = [];\n for (const item of line.items) {\n const x = Math.round(item.x);\n // Check if this x is close to an existing column\n let found = false;\n for (const p of positions) {\n if (Math.abs(p - x) < IMPORT_COLUMN_TOLERANCE) {\n found = true;\n break;\n }\n }\n if (!found) positions.push(x);\n }\n return positions.sort((a, b) => a - b);\n}\n\nfunction buildTable(lines: TextLine[], _options: PdfImportOptions): MarkdownTable | null {\n if (lines.length === 0) return null;\n\n // Use the first line's column positions as anchors\n const cols = getColumnPositions(lines[0]);\n if (cols.length < 2) return null;\n\n const rows: MarkdownTableRow[] = [];\n\n for (let ri = 0; ri < lines.length; ri++) {\n const line = lines[ri];\n const cells: MarkdownTableCell[] = [];\n\n for (let ci = 0; ci < cols.length; ci++) {\n const colLeft = cols[ci] - IMPORT_COLUMN_TOLERANCE;\n const colRight = ci + 1 < cols.length ? cols[ci + 1] - IMPORT_COLUMN_TOLERANCE : Infinity;\n\n // Collect items in this column\n const cellItems = line.items.filter((item) => item.x >= colLeft && item.x < colRight);\n const text = cellItems\n .map((i) => i.str)\n .join(' ')\n .trim();\n\n cells.push({\n type: 'tableCell',\n isHeader: ri === 0,\n children: text.length > 0 ? [{ type: 'text', value: text } as MarkdownText] : [],\n } as MarkdownTableCell);\n }\n\n rows.push({\n type: 'tableRow',\n children: cells,\n } as MarkdownTableRow);\n }\n\n return {\n type: 'table',\n children: rows,\n } as MarkdownTable;\n}\n\n// ============================================\n// Text Merging\n// ============================================\n\n/**\n * Merge adjacent text nodes to reduce fragmentation.\n */\nfunction mergeAdjacentText(nodes: MarkdownInlineNode[]): MarkdownInlineNode[] {\n if (nodes.length <= 1) return nodes;\n\n const result: MarkdownInlineNode[] = [];\n for (const node of nodes) {\n const prev = result[result.length - 1];\n if (prev && prev.type === 'text' && node.type === 'text') {\n (prev as MarkdownText).value += (node as MarkdownText).value;\n } else {\n result.push(node);\n }\n }\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/shared/images.ts
|
|
2
|
+
var EXT_TO_MIME = {
|
|
3
|
+
// Raster / vector images
|
|
4
|
+
png: "image/png",
|
|
5
|
+
jpg: "image/jpeg",
|
|
6
|
+
jpeg: "image/jpeg",
|
|
7
|
+
gif: "image/gif",
|
|
8
|
+
webp: "image/webp",
|
|
9
|
+
bmp: "image/bmp",
|
|
10
|
+
svg: "image/svg+xml",
|
|
11
|
+
tiff: "image/tiff",
|
|
12
|
+
tif: "image/tiff",
|
|
13
|
+
ico: "image/x-icon",
|
|
14
|
+
avif: "image/avif",
|
|
15
|
+
// Office vector formats (docx/pptx embedded metafiles)
|
|
16
|
+
emf: "image/emf",
|
|
17
|
+
wmf: "image/wmf",
|
|
18
|
+
// Audio / video (html export inlines these as data URIs)
|
|
19
|
+
mp3: "audio/mpeg",
|
|
20
|
+
wav: "audio/wav",
|
|
21
|
+
ogg: "audio/ogg",
|
|
22
|
+
mp4: "video/mp4",
|
|
23
|
+
webm: "video/webm"
|
|
24
|
+
};
|
|
25
|
+
function extToMime(ext) {
|
|
26
|
+
const key = ext.replace(/^\./, "").toLowerCase();
|
|
27
|
+
return EXT_TO_MIME[key] ?? "application/octet-stream";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
extToMime
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-DQAZR57U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/images.ts"],"sourcesContent":["/**\n * Shared extension → MIME-type mapping for embedded media.\n *\n * Previously triplicated with subtle drift: the docx and pptx importers each\n * carried a byte-identical dotted-key map (png/jpg/.../emf/wmf), while the html\n * exporter's `inferMimeType` used no-dot keys and a different key set (added\n * audio/video/ico/avif, omitted tiff/emf/wmf). This module is the single union\n * of all of them, so a newly-supported type only has to be added once.\n */\n\n/** Union of every extension→MIME entry the format modules need. */\nconst EXT_TO_MIME: Record<string, string> = {\n // Raster / vector images\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n tiff: 'image/tiff',\n tif: 'image/tiff',\n ico: 'image/x-icon',\n avif: 'image/avif',\n // Office vector formats (docx/pptx embedded metafiles)\n emf: 'image/emf',\n wmf: 'image/wmf',\n // Audio / video (html export inlines these as data URIs)\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n mp4: 'video/mp4',\n webm: 'video/webm',\n};\n\n/**\n * Map a file extension to its MIME type. Tolerates a leading dot and any\n * casing (`.PNG`, `png`, `.png` all resolve). Returns\n * `application/octet-stream` for unknown extensions.\n */\nexport function extToMime(ext: string): string {\n const key = ext.replace(/^\\./, '').toLowerCase();\n return EXT_TO_MIME[key] ?? 'application/octet-stream';\n}\n"],"mappings":";AAWA,IAAM,cAAsC;AAAA;AAAA,EAE1C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA;AAAA,EAEN,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAEL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AACR;AAOO,SAAS,UAAU,KAAqB;AAC7C,QAAM,MAAM,IAAI,QAAQ,OAAO,EAAE,EAAE,YAAY;AAC/C,SAAO,YAAY,GAAG,KAAK;AAC7B;","names":[]}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractPlainText
|
|
3
|
+
} from "./chunk-PN52A5AA.js";
|
|
4
|
+
import {
|
|
5
|
+
CONTENT_TYPE_XLSX_STYLES,
|
|
6
|
+
CONTENT_TYPE_XLSX_WORKBOOK,
|
|
7
|
+
CONTENT_TYPE_XLSX_WORKSHEET,
|
|
8
|
+
NS_R,
|
|
9
|
+
NS_SML,
|
|
10
|
+
REL_OFFICE_DOCUMENT,
|
|
11
|
+
REL_STYLES,
|
|
12
|
+
REL_WORKSHEET,
|
|
13
|
+
createPackage,
|
|
14
|
+
getPartRelationships,
|
|
15
|
+
getPartXml,
|
|
16
|
+
openPackage
|
|
17
|
+
} from "./chunk-SHLPSADL.js";
|
|
18
|
+
import {
|
|
19
|
+
escapeXml,
|
|
20
|
+
xmlDeclaration
|
|
21
|
+
} from "./chunk-U4MRIFKL.js";
|
|
22
|
+
|
|
23
|
+
// src/xlsx/index.ts
|
|
24
|
+
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
25
|
+
|
|
26
|
+
// src/xlsx/import.ts
|
|
27
|
+
function attrNS(el, ns, local, fallback) {
|
|
28
|
+
return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
|
|
29
|
+
}
|
|
30
|
+
function resolveTarget(baseDir, target) {
|
|
31
|
+
if (target.startsWith("/")) return target.replace(/^\//, "");
|
|
32
|
+
const stack = baseDir ? baseDir.split("/") : [];
|
|
33
|
+
for (const seg of target.split("/")) {
|
|
34
|
+
if (seg === "..") stack.pop();
|
|
35
|
+
else if (seg !== ".") stack.push(seg);
|
|
36
|
+
}
|
|
37
|
+
return stack.join("/");
|
|
38
|
+
}
|
|
39
|
+
async function readSheets(pkg) {
|
|
40
|
+
const wb = await getPartXml(pkg, "xl/workbook.xml");
|
|
41
|
+
if (!wb) return [];
|
|
42
|
+
const rels = await getPartRelationships(pkg, "xl/workbook.xml");
|
|
43
|
+
const relById = new Map(rels.map((r) => [r.id, r.target]));
|
|
44
|
+
const out = [];
|
|
45
|
+
const sheetEls = wb.getElementsByTagNameNS(NS_SML, "sheet");
|
|
46
|
+
for (let i = 0; i < sheetEls.length; i++) {
|
|
47
|
+
const el = sheetEls[i];
|
|
48
|
+
const name = el.getAttribute("name") ?? `Sheet${i + 1}`;
|
|
49
|
+
const rid = attrNS(el, NS_R, "id", "r:id");
|
|
50
|
+
const target = rid ? relById.get(rid) : void 0;
|
|
51
|
+
if (target) out.push({ name, path: resolveTarget("xl", target) });
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
async function readSharedStrings(pkg) {
|
|
56
|
+
const doc = await getPartXml(pkg, "xl/sharedStrings.xml");
|
|
57
|
+
if (!doc) return [];
|
|
58
|
+
const siEls = doc.getElementsByTagNameNS(NS_SML, "si");
|
|
59
|
+
const out = [];
|
|
60
|
+
for (let i = 0; i < siEls.length; i++) out.push(siEls[i].textContent ?? "");
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
function colIndex(ref) {
|
|
64
|
+
const m = /^([A-Za-z]+)/.exec(ref);
|
|
65
|
+
if (!m) return 0;
|
|
66
|
+
let n = 0;
|
|
67
|
+
for (const ch of m[1].toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
68
|
+
return n - 1;
|
|
69
|
+
}
|
|
70
|
+
function cellText(cell, shared) {
|
|
71
|
+
const t = cell.getAttribute("t");
|
|
72
|
+
if (t === "inlineStr") {
|
|
73
|
+
const is = cell.getElementsByTagNameNS(NS_SML, "t");
|
|
74
|
+
return is.length ? is[0].textContent ?? "" : "";
|
|
75
|
+
}
|
|
76
|
+
const vEls = cell.getElementsByTagNameNS(NS_SML, "v");
|
|
77
|
+
const v = vEls.length ? vEls[0].textContent ?? "" : "";
|
|
78
|
+
if (t === "s") return shared[Number.parseInt(v, 10)] ?? "";
|
|
79
|
+
if (t === "b") return v === "1" ? "TRUE" : "FALSE";
|
|
80
|
+
return v;
|
|
81
|
+
}
|
|
82
|
+
async function sheetToGrid(pkg, path, shared) {
|
|
83
|
+
const doc = await getPartXml(pkg, path);
|
|
84
|
+
if (!doc) return [];
|
|
85
|
+
const rowEls = doc.getElementsByTagNameNS(NS_SML, "row");
|
|
86
|
+
const grid = [];
|
|
87
|
+
for (let r = 0; r < rowEls.length; r++) {
|
|
88
|
+
const cells = rowEls[r].getElementsByTagNameNS(NS_SML, "c");
|
|
89
|
+
const rowArr = [];
|
|
90
|
+
for (let c = 0; c < cells.length; c++) {
|
|
91
|
+
const cell = cells[c];
|
|
92
|
+
const ref = cell.getAttribute("r");
|
|
93
|
+
const idx = ref ? colIndex(ref) : rowArr.length;
|
|
94
|
+
while (rowArr.length < idx) rowArr.push("");
|
|
95
|
+
rowArr[idx] = cellText(cell, shared);
|
|
96
|
+
}
|
|
97
|
+
grid.push(rowArr);
|
|
98
|
+
}
|
|
99
|
+
return grid;
|
|
100
|
+
}
|
|
101
|
+
function gridToTable(grid) {
|
|
102
|
+
const maxCols = grid.reduce((m, r) => Math.max(m, r.length), 1);
|
|
103
|
+
const rows = grid.map((cells, rowIdx) => {
|
|
104
|
+
const children = [];
|
|
105
|
+
for (let c = 0; c < maxCols; c++) {
|
|
106
|
+
const value = cells[c] ?? "";
|
|
107
|
+
children.push({
|
|
108
|
+
type: "tableCell",
|
|
109
|
+
...rowIdx === 0 ? { isHeader: true } : {},
|
|
110
|
+
children: value ? [{ type: "text", value }] : []
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return { type: "tableRow", children };
|
|
114
|
+
});
|
|
115
|
+
return { type: "table", children: rows };
|
|
116
|
+
}
|
|
117
|
+
async function xlsxToMarkdownDoc(data, options = {}) {
|
|
118
|
+
const pkg = await openPackage(data);
|
|
119
|
+
const sheets = await readSheets(pkg);
|
|
120
|
+
const shared = await readSharedStrings(pkg);
|
|
121
|
+
let selected = sheets;
|
|
122
|
+
if (options.sheet !== void 0) {
|
|
123
|
+
const picked = typeof options.sheet === "number" ? sheets[options.sheet] : sheets.find((s) => s.name === options.sheet);
|
|
124
|
+
selected = picked ? [picked] : [];
|
|
125
|
+
}
|
|
126
|
+
const children = [];
|
|
127
|
+
const single = selected.length === 1 && options.sheet !== void 0;
|
|
128
|
+
for (const sheet of selected) {
|
|
129
|
+
const grid = await sheetToGrid(pkg, sheet.path, shared);
|
|
130
|
+
if (!single) {
|
|
131
|
+
children.push({ type: "heading", depth: 1, children: [{ type: "text", value: sheet.name }] });
|
|
132
|
+
}
|
|
133
|
+
if (grid.length > 0) children.push(gridToTable(grid));
|
|
134
|
+
}
|
|
135
|
+
return { type: "document", children };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/xlsx/export.ts
|
|
139
|
+
import { docToMarkdown } from "@bendyline/squisq/doc";
|
|
140
|
+
var NUMERIC_RE = /^-?\d+(\.\d+)?$/;
|
|
141
|
+
function sanitizeSheetName(raw, used, fallback) {
|
|
142
|
+
let name = raw.replace(/[[\]:*?/\\]/g, "").trim();
|
|
143
|
+
if (name.length === 0) name = fallback;
|
|
144
|
+
name = name.slice(0, 31);
|
|
145
|
+
if (!used.has(name)) {
|
|
146
|
+
used.add(name);
|
|
147
|
+
return name;
|
|
148
|
+
}
|
|
149
|
+
for (let n = 2; ; n++) {
|
|
150
|
+
const suffix = String(n);
|
|
151
|
+
const base = name.slice(0, 31 - suffix.length);
|
|
152
|
+
const candidate = `${base}${suffix}`;
|
|
153
|
+
if (!used.has(candidate)) {
|
|
154
|
+
used.add(candidate);
|
|
155
|
+
return candidate;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function tableToGrid(table) {
|
|
160
|
+
return table.children.map(
|
|
161
|
+
(row) => row.children.map((cell) => extractPlainText(cell.children))
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function collectSheets(doc, prefix) {
|
|
165
|
+
const used = /* @__PURE__ */ new Set();
|
|
166
|
+
const sheets = [];
|
|
167
|
+
let pendingHeading = null;
|
|
168
|
+
const visit = (nodes) => {
|
|
169
|
+
for (const node of nodes) {
|
|
170
|
+
if (node.type === "heading") {
|
|
171
|
+
pendingHeading = extractPlainText(node.children);
|
|
172
|
+
} else if (node.type === "table") {
|
|
173
|
+
const fallback = `${prefix}${sheets.length + 1}`;
|
|
174
|
+
const name = sanitizeSheetName(pendingHeading ?? fallback, used, fallback);
|
|
175
|
+
sheets.push({ name, grid: tableToGrid(node) });
|
|
176
|
+
pendingHeading = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
visit(doc.children);
|
|
181
|
+
return sheets;
|
|
182
|
+
}
|
|
183
|
+
function columnLetter(index) {
|
|
184
|
+
let n = index + 1;
|
|
185
|
+
let letters = "";
|
|
186
|
+
while (n > 0) {
|
|
187
|
+
const rem = (n - 1) % 26;
|
|
188
|
+
letters = String.fromCharCode(65 + rem) + letters;
|
|
189
|
+
n = Math.floor((n - 1) / 26);
|
|
190
|
+
}
|
|
191
|
+
return letters;
|
|
192
|
+
}
|
|
193
|
+
function cellXml(value, ref) {
|
|
194
|
+
if (NUMERIC_RE.test(value)) {
|
|
195
|
+
return `<c r="${ref}"><v>${escapeXml(value)}</v></c>`;
|
|
196
|
+
}
|
|
197
|
+
return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(value)}</t></is></c>`;
|
|
198
|
+
}
|
|
199
|
+
function worksheetXml(grid) {
|
|
200
|
+
const rows = [];
|
|
201
|
+
for (let r = 0; r < grid.length; r++) {
|
|
202
|
+
const cells = grid[r];
|
|
203
|
+
const cellsXml = cells.map((value, c) => cellXml(value, `${columnLetter(c)}${r + 1}`)).join("");
|
|
204
|
+
rows.push(`<row r="${r + 1}">${cellsXml}</row>`);
|
|
205
|
+
}
|
|
206
|
+
return `${xmlDeclaration()}
|
|
207
|
+
<worksheet xmlns="${NS_SML}" xmlns:r="${NS_R}"><sheetData>${rows.join("")}</sheetData></worksheet>`;
|
|
208
|
+
}
|
|
209
|
+
function workbookXml(sheets) {
|
|
210
|
+
const sheetEls = sheets.map(
|
|
211
|
+
(sheet, i) => `<sheet name="${escapeXml(sheet.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`
|
|
212
|
+
).join("");
|
|
213
|
+
return `${xmlDeclaration()}
|
|
214
|
+
<workbook xmlns="${NS_SML}" xmlns:r="${NS_R}"><sheets>${sheetEls}</sheets></workbook>`;
|
|
215
|
+
}
|
|
216
|
+
function stylesXml() {
|
|
217
|
+
return `${xmlDeclaration()}
|
|
218
|
+
<styleSheet xmlns="${NS_SML}"><fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts><fills count="1"><fill><patternFill patternType="none"/></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs></styleSheet>`;
|
|
219
|
+
}
|
|
220
|
+
async function markdownDocToXlsx(doc, options = {}) {
|
|
221
|
+
const prefix = options.sheetNamePrefix ?? "Sheet";
|
|
222
|
+
let sheets = collectSheets(doc, prefix);
|
|
223
|
+
if (sheets.length === 0) {
|
|
224
|
+
sheets = [{ name: `${prefix}1`, grid: [] }];
|
|
225
|
+
}
|
|
226
|
+
const pkg = createPackage();
|
|
227
|
+
sheets.forEach((sheet, i) => {
|
|
228
|
+
const sheetPath = `xl/worksheets/sheet${i + 1}.xml`;
|
|
229
|
+
pkg.addPart(sheetPath, worksheetXml(sheet.grid), CONTENT_TYPE_XLSX_WORKSHEET);
|
|
230
|
+
pkg.addRelationship("xl/workbook.xml", {
|
|
231
|
+
id: `rId${i + 1}`,
|
|
232
|
+
type: REL_WORKSHEET,
|
|
233
|
+
target: `worksheets/sheet${i + 1}.xml`
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
pkg.addPart("xl/styles.xml", stylesXml(), CONTENT_TYPE_XLSX_STYLES);
|
|
237
|
+
pkg.addRelationship("xl/workbook.xml", {
|
|
238
|
+
id: `rId${sheets.length + 1}`,
|
|
239
|
+
type: REL_STYLES,
|
|
240
|
+
target: "styles.xml"
|
|
241
|
+
});
|
|
242
|
+
pkg.addPart("xl/workbook.xml", workbookXml(sheets), CONTENT_TYPE_XLSX_WORKBOOK);
|
|
243
|
+
pkg.addRelationship("", {
|
|
244
|
+
id: "rId1",
|
|
245
|
+
type: REL_OFFICE_DOCUMENT,
|
|
246
|
+
target: "xl/workbook.xml"
|
|
247
|
+
});
|
|
248
|
+
if (options.title || options.author) {
|
|
249
|
+
pkg.setCoreProperties({
|
|
250
|
+
title: options.title,
|
|
251
|
+
creator: options.author,
|
|
252
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
253
|
+
modified: (/* @__PURE__ */ new Date()).toISOString()
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return pkg.toArrayBuffer();
|
|
257
|
+
}
|
|
258
|
+
async function docToXlsx(doc, options) {
|
|
259
|
+
return markdownDocToXlsx(docToMarkdown(doc), options);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/xlsx/index.ts
|
|
263
|
+
async function xlsxToDoc(data, options) {
|
|
264
|
+
return markdownToDoc(await xlsxToMarkdownDoc(data, options));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export {
|
|
268
|
+
xlsxToMarkdownDoc,
|
|
269
|
+
markdownDocToXlsx,
|
|
270
|
+
docToXlsx,
|
|
271
|
+
xlsxToDoc
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=chunk-EHLGMSTE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/xlsx/index.ts","../src/xlsx/import.ts","../src/xlsx/export.ts"],"sourcesContent":["/**\n * @bendyline/squisq-formats XLSX Module\n *\n * Excel .xlsx support via SpreadsheetML and the shared ooxml/ infrastructure.\n * Both directions are implemented: import (`xlsxToMarkdownDoc` / `xlsxToDoc`)\n * turns each worksheet grid into a markdown table; export (`markdownDocToXlsx`\n * / `docToXlsx`) turns each markdown table into a worksheet. Export is\n * tables-only fidelity — non-table content is dropped (headings only survive\n * as sheet names). See export.ts for details.\n *\n * @example\n * ```ts\n * import { xlsxToMarkdownDoc, markdownDocToXlsx } from '@bendyline/squisq-formats/xlsx';\n * ```\n */\n\nimport { markdownToDoc } from '@bendyline/squisq/doc';\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { type XlsxImportOptions, xlsxToMarkdownDoc } from './import.js';\n\nexport type { XlsxImportOptions } from './import.js';\nexport { xlsxToMarkdownDoc } from './import.js';\n\nexport type { XlsxExportOptions } from './export.js';\nexport { markdownDocToXlsx, docToXlsx } from './export.js';\n\n/**\n * Convert a .xlsx file to a squisq Doc (via the markdown table model).\n */\nexport async function xlsxToDoc(\n data: ArrayBuffer | Blob,\n options?: XlsxImportOptions,\n): Promise<Doc> {\n return markdownToDoc(await xlsxToMarkdownDoc(data, options));\n}\n","/**\n * XLSX import — SpreadsheetML (.xlsx) → MarkdownDocument.\n *\n * Reuses the shared ooxml/ reader (zip + DOMParser). Reads the workbook's\n * sheet list, resolves each sheet part via relationships, pulls shared strings,\n * and turns each worksheet's cell grid into a markdown table (first row treated\n * as the header). By default every sheet is imported, each preceded by an H1 of\n * the sheet name; pass `options.sheet` (index or name) to import just one.\n */\n\nimport type {\n MarkdownBlockNode,\n MarkdownDocument,\n MarkdownTable,\n MarkdownTableCell,\n MarkdownTableRow,\n} from '@bendyline/squisq/markdown';\nimport { getPartXml, getPartRelationships, openPackage } from '../ooxml/reader.js';\nimport type { OoxmlPackage } from '../ooxml/types.js';\nimport { NS_R, NS_SML } from '../ooxml/namespaces.js';\n\nexport interface XlsxImportOptions {\n /** Which sheet to import (0-based index or sheet name). Default: all sheets. */\n sheet?: number | string;\n}\n\ninterface SheetRef {\n name: string;\n path: string;\n}\n\nfunction attrNS(el: Element, ns: string, local: string, fallback: string): string | null {\n return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);\n}\n\n/** Resolve a relationship target (relative to its part's dir) to a zip path. */\nfunction resolveTarget(baseDir: string, target: string): string {\n if (target.startsWith('/')) return target.replace(/^\\//, '');\n const stack = baseDir ? baseDir.split('/') : [];\n for (const seg of target.split('/')) {\n if (seg === '..') stack.pop();\n else if (seg !== '.') stack.push(seg);\n }\n return stack.join('/');\n}\n\nasync function readSheets(pkg: OoxmlPackage): Promise<SheetRef[]> {\n const wb = await getPartXml(pkg, 'xl/workbook.xml');\n if (!wb) return [];\n const rels = await getPartRelationships(pkg, 'xl/workbook.xml');\n const relById = new Map(rels.map((r) => [r.id, r.target]));\n const out: SheetRef[] = [];\n const sheetEls = wb.getElementsByTagNameNS(NS_SML, 'sheet');\n for (let i = 0; i < sheetEls.length; i++) {\n const el = sheetEls[i]!;\n const name = el.getAttribute('name') ?? `Sheet${i + 1}`;\n const rid = attrNS(el, NS_R, 'id', 'r:id');\n const target = rid ? relById.get(rid) : undefined;\n if (target) out.push({ name, path: resolveTarget('xl', target) });\n }\n return out;\n}\n\nasync function readSharedStrings(pkg: OoxmlPackage): Promise<string[]> {\n const doc = await getPartXml(pkg, 'xl/sharedStrings.xml');\n if (!doc) return [];\n const siEls = doc.getElementsByTagNameNS(NS_SML, 'si');\n const out: string[] = [];\n for (let i = 0; i < siEls.length; i++) out.push(siEls[i]!.textContent ?? '');\n return out;\n}\n\n/** Column letters of a cell ref (\"B7\" → 1, \"AA1\" → 26). */\nfunction colIndex(ref: string): number {\n const m = /^([A-Za-z]+)/.exec(ref);\n if (!m) return 0;\n let n = 0;\n for (const ch of m[1]!.toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);\n return n - 1;\n}\n\nfunction cellText(cell: Element, shared: string[]): string {\n const t = cell.getAttribute('t');\n if (t === 'inlineStr') {\n const is = cell.getElementsByTagNameNS(NS_SML, 't');\n return is.length ? (is[0]!.textContent ?? '') : '';\n }\n const vEls = cell.getElementsByTagNameNS(NS_SML, 'v');\n const v = vEls.length ? (vEls[0]!.textContent ?? '') : '';\n if (t === 's') return shared[Number.parseInt(v, 10)] ?? '';\n if (t === 'b') return v === '1' ? 'TRUE' : 'FALSE';\n return v;\n}\n\nasync function sheetToGrid(pkg: OoxmlPackage, path: string, shared: string[]): Promise<string[][]> {\n const doc = await getPartXml(pkg, path);\n if (!doc) return [];\n const rowEls = doc.getElementsByTagNameNS(NS_SML, 'row');\n const grid: string[][] = [];\n for (let r = 0; r < rowEls.length; r++) {\n const cells = rowEls[r]!.getElementsByTagNameNS(NS_SML, 'c');\n const rowArr: string[] = [];\n for (let c = 0; c < cells.length; c++) {\n const cell = cells[c]!;\n const ref = cell.getAttribute('r');\n const idx = ref ? colIndex(ref) : rowArr.length;\n while (rowArr.length < idx) rowArr.push('');\n rowArr[idx] = cellText(cell, shared);\n }\n grid.push(rowArr);\n }\n return grid;\n}\n\nfunction gridToTable(grid: string[][]): MarkdownTable {\n const maxCols = grid.reduce((m, r) => Math.max(m, r.length), 1);\n const rows: MarkdownTableRow[] = grid.map((cells, rowIdx) => {\n const children: MarkdownTableCell[] = [];\n for (let c = 0; c < maxCols; c++) {\n const value = cells[c] ?? '';\n children.push({\n type: 'tableCell',\n ...(rowIdx === 0 ? { isHeader: true } : {}),\n children: value ? [{ type: 'text', value }] : [],\n });\n }\n return { type: 'tableRow', children };\n });\n return { type: 'table', children: rows };\n}\n\nexport async function xlsxToMarkdownDoc(\n data: ArrayBuffer | Blob,\n options: XlsxImportOptions = {},\n): Promise<MarkdownDocument> {\n const pkg = await openPackage(data);\n const sheets = await readSheets(pkg);\n const shared = await readSharedStrings(pkg);\n\n let selected = sheets;\n if (options.sheet !== undefined) {\n const picked =\n typeof options.sheet === 'number'\n ? sheets[options.sheet]\n : sheets.find((s) => s.name === options.sheet);\n selected = picked ? [picked] : [];\n }\n\n const children: MarkdownBlockNode[] = [];\n const single = selected.length === 1 && options.sheet !== undefined;\n for (const sheet of selected) {\n const grid = await sheetToGrid(pkg, sheet.path, shared);\n if (!single) {\n children.push({ type: 'heading', depth: 1, children: [{ type: 'text', value: sheet.name }] });\n }\n if (grid.length > 0) children.push(gridToTable(grid));\n }\n return { type: 'document', children };\n}\n","/**\n * XLSX export — MarkdownDocument → SpreadsheetML (.xlsx).\n *\n * Tables-only fidelity (honestly documented): every `table` node in the\n * markdown AST becomes one worksheet; all other content (headings, prose,\n * lists, images, …) is dropped except that the nearest preceding heading is\n * used to name the sheet. This mirrors the import side (`xlsxToMarkdownDoc`),\n * which turns each worksheet grid back into a markdown table.\n *\n * Cells are emitted as inline strings (`t=\"inlineStr\"`) so no sharedStrings\n * part is needed, except values matching a plain-number pattern, which are\n * emitted as numeric cells. The package is assembled with the shared ooxml/\n * writer (auto-generates `[Content_Types].xml` + `_rels`), so only the\n * SpreadsheetML-specific parts (workbook, worksheets, styles) are written here.\n *\n * @example\n * ```ts\n * import { parseMarkdown } from '@bendyline/squisq/markdown';\n * import { markdownDocToXlsx } from '@bendyline/squisq-formats/xlsx';\n *\n * const md = parseMarkdown('# Metrics\\n\\n| A | B |\\n| - | - |\\n| 1 | 2 |');\n * const buffer = await markdownDocToXlsx(md);\n * ```\n */\n\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { docToMarkdown } from '@bendyline/squisq/doc';\nimport type {\n MarkdownDocument,\n MarkdownBlockNode,\n MarkdownTable,\n MarkdownTableRow,\n} from '@bendyline/squisq/markdown';\n\nimport { createPackage } from '../ooxml/writer.js';\nimport { xmlDeclaration, escapeXml } from '../ooxml/xmlUtils.js';\nimport { extractPlainText } from '../shared/text.js';\nimport {\n NS_SML,\n NS_R,\n REL_OFFICE_DOCUMENT,\n REL_STYLES,\n REL_WORKSHEET,\n CONTENT_TYPE_XLSX_WORKBOOK,\n CONTENT_TYPE_XLSX_WORKSHEET,\n CONTENT_TYPE_XLSX_STYLES,\n} from '../ooxml/namespaces.js';\n\n/**\n * Options for XLSX export.\n */\nexport interface XlsxExportOptions {\n /** Workbook title (written to core properties). */\n title?: string;\n /** Workbook author (written to core properties). */\n author?: string;\n /** Prefix used for auto-named sheets when no heading precedes a table. Default: \"Sheet\". */\n sheetNamePrefix?: string;\n}\n\ninterface SheetData {\n name: string;\n /** Grid of cell text: first row is the header. */\n grid: string[][];\n}\n\n/** A plain integer or decimal (optionally negative) — emitted as a numeric cell. */\nconst NUMERIC_RE = /^-?\\d+(\\.\\d+)?$/;\n\n/**\n * Sanitize a candidate name into a valid, unique Excel sheet name.\n * Strips characters Excel forbids (`[]:*?/\\`), caps at 31 chars, and\n * de-duplicates against already-used names by appending 2, 3, …\n */\nfunction sanitizeSheetName(raw: string, used: Set<string>, fallback: string): string {\n let name = raw.replace(/[[\\]:*?/\\\\]/g, '').trim();\n if (name.length === 0) name = fallback;\n name = name.slice(0, 31);\n\n if (!used.has(name)) {\n used.add(name);\n return name;\n }\n // Dedupe: \"Name\", \"Name2\", \"Name3\", … keeping within the 31-char cap.\n for (let n = 2; ; n++) {\n const suffix = String(n);\n const base = name.slice(0, 31 - suffix.length);\n const candidate = `${base}${suffix}`;\n if (!used.has(candidate)) {\n used.add(candidate);\n return candidate;\n }\n }\n}\n\n/** Convert a table node into a grid of plain-text cells. */\nfunction tableToGrid(table: MarkdownTable): string[][] {\n return table.children.map((row: MarkdownTableRow) =>\n row.children.map((cell) => extractPlainText(cell.children)),\n );\n}\n\n/** Walk the document, pairing each table with the nearest preceding heading. */\nfunction collectSheets(doc: MarkdownDocument, prefix: string): SheetData[] {\n const used = new Set<string>();\n const sheets: SheetData[] = [];\n let pendingHeading: string | null = null;\n\n const visit = (nodes: MarkdownBlockNode[]): void => {\n for (const node of nodes) {\n if (node.type === 'heading') {\n pendingHeading = extractPlainText(node.children);\n } else if (node.type === 'table') {\n const fallback = `${prefix}${sheets.length + 1}`;\n const name = sanitizeSheetName(pendingHeading ?? fallback, used, fallback);\n sheets.push({ name, grid: tableToGrid(node) });\n pendingHeading = null;\n }\n }\n };\n\n visit(doc.children);\n return sheets;\n}\n\n/** Column letters for a zero-based index (0 → \"A\", 26 → \"AA\"). */\nfunction columnLetter(index: number): string {\n let n = index + 1;\n let letters = '';\n while (n > 0) {\n const rem = (n - 1) % 26;\n letters = String.fromCharCode(65 + rem) + letters;\n n = Math.floor((n - 1) / 26);\n }\n return letters;\n}\n\nfunction cellXml(value: string, ref: string): string {\n if (NUMERIC_RE.test(value)) {\n return `<c r=\"${ref}\"><v>${escapeXml(value)}</v></c>`;\n }\n return `<c r=\"${ref}\" t=\"inlineStr\"><is><t xml:space=\"preserve\">${escapeXml(value)}</t></is></c>`;\n}\n\nfunction worksheetXml(grid: string[][]): string {\n const rows: string[] = [];\n for (let r = 0; r < grid.length; r++) {\n const cells = grid[r]!;\n const cellsXml = cells.map((value, c) => cellXml(value, `${columnLetter(c)}${r + 1}`)).join('');\n rows.push(`<row r=\"${r + 1}\">${cellsXml}</row>`);\n }\n return (\n `${xmlDeclaration()}\\n` +\n `<worksheet xmlns=\"${NS_SML}\" xmlns:r=\"${NS_R}\">` +\n `<sheetData>${rows.join('')}</sheetData>` +\n `</worksheet>`\n );\n}\n\nfunction workbookXml(sheets: SheetData[]): string {\n const sheetEls = sheets\n .map(\n (sheet, i) =>\n `<sheet name=\"${escapeXml(sheet.name)}\" sheetId=\"${i + 1}\" r:id=\"rId${i + 1}\"/>`,\n )\n .join('');\n return (\n `${xmlDeclaration()}\\n` +\n `<workbook xmlns=\"${NS_SML}\" xmlns:r=\"${NS_R}\">` +\n `<sheets>${sheetEls}</sheets>` +\n `</workbook>`\n );\n}\n\n/** Minimal but valid styles part (Excel expects the referenced collections to exist). */\nfunction stylesXml(): string {\n return (\n `${xmlDeclaration()}\\n` +\n `<styleSheet xmlns=\"${NS_SML}\">` +\n `<fonts count=\"1\"><font><sz val=\"11\"/><name val=\"Calibri\"/></font></fonts>` +\n `<fills count=\"1\"><fill><patternFill patternType=\"none\"/></fill></fills>` +\n `<borders count=\"1\"><border/></borders>` +\n `<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>` +\n `<cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/></cellXfs>` +\n `</styleSheet>`\n );\n}\n\n/**\n * Convert a MarkdownDocument to a .xlsx file (tables-only fidelity).\n *\n * Each markdown `table` becomes one worksheet; a document with no tables\n * yields a single empty sheet (a valid, openable file — never throws).\n */\nexport async function markdownDocToXlsx(\n doc: MarkdownDocument,\n options: XlsxExportOptions = {},\n): Promise<ArrayBuffer> {\n const prefix = options.sheetNamePrefix ?? 'Sheet';\n let sheets = collectSheets(doc, prefix);\n\n // Zero tables → one empty sheet so the file is still valid.\n if (sheets.length === 0) {\n sheets = [{ name: `${prefix}1`, grid: [] }];\n }\n\n const pkg = createPackage();\n\n // Worksheets + workbook→worksheet relationships.\n sheets.forEach((sheet, i) => {\n const sheetPath = `xl/worksheets/sheet${i + 1}.xml`;\n pkg.addPart(sheetPath, worksheetXml(sheet.grid), CONTENT_TYPE_XLSX_WORKSHEET);\n pkg.addRelationship('xl/workbook.xml', {\n id: `rId${i + 1}`,\n type: REL_WORKSHEET,\n target: `worksheets/sheet${i + 1}.xml`,\n });\n });\n\n // Styles part (referenced after the sheet relationships).\n pkg.addPart('xl/styles.xml', stylesXml(), CONTENT_TYPE_XLSX_STYLES);\n pkg.addRelationship('xl/workbook.xml', {\n id: `rId${sheets.length + 1}`,\n type: REL_STYLES,\n target: 'styles.xml',\n });\n\n // Workbook + root relationship.\n pkg.addPart('xl/workbook.xml', workbookXml(sheets), CONTENT_TYPE_XLSX_WORKBOOK);\n pkg.addRelationship('', {\n id: 'rId1',\n type: REL_OFFICE_DOCUMENT,\n target: 'xl/workbook.xml',\n });\n\n if (options.title || options.author) {\n pkg.setCoreProperties({\n title: options.title,\n creator: options.author,\n created: new Date().toISOString(),\n modified: new Date().toISOString(),\n });\n }\n\n return pkg.toArrayBuffer();\n}\n\n/**\n * Convert a squisq Doc to a .xlsx file (via the markdown table model).\n */\nexport async function docToXlsx(doc: Doc, options?: XlsxExportOptions): Promise<ArrayBuffer> {\n return markdownDocToXlsx(docToMarkdown(doc), options);\n}\n"],"mappings":"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","names":[]}
|