UNPKG

omniscript-converters

Version:

Format converters for OmniScript Format (OSF) - Convert to/from DOCX, PPTX, XLSX, PDF

351 lines 13.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DOCXConverter = void 0; const docx_1 = require("docx"); class DOCXConverter { getSupportedFormats() { return ['docx']; } async convert(document, options = {}) { const docElements = this.generateDocumentElements(document, options); const doc = new docx_1.Document({ creator: 'OmniScript OSF', title: this.getDocumentTitle(document), description: 'Generated from OSF document', sections: [{ properties: {}, children: docElements }] }); const buffer = await docx_1.Packer.toBuffer(doc); return { buffer, mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', extension: 'docx' }; } generateDocumentElements(document, options) { const elements = []; for (const block of document.blocks) { switch (block.type) { case 'meta': if (options.includeMetadata !== false) { elements.push(...this.renderMetaBlock(block)); } break; case 'doc': elements.push(...this.renderDocBlock(block)); break; case 'slide': elements.push(...this.renderSlideBlock(block)); break; case 'sheet': elements.push(...this.renderSheetBlock(block)); break; } } return elements; } renderMetaBlock(meta) { const elements = []; if (meta.props.title) { elements.push(new docx_1.Paragraph({ children: [ new docx_1.TextRun({ text: String(meta.props.title), bold: true, size: 32, }) ], heading: docx_1.HeadingLevel.TITLE, alignment: docx_1.AlignmentType.CENTER, spacing: { after: 400 } })); } const metaInfo = []; if (meta.props.author) metaInfo.push(`Author: ${meta.props.author}`); if (meta.props.date) metaInfo.push(`Date: ${meta.props.date}`); if (meta.props.theme) metaInfo.push(`Theme: ${meta.props.theme}`); if (metaInfo.length > 0) { elements.push(new docx_1.Paragraph({ children: [ new docx_1.TextRun({ text: metaInfo.join(' | '), italics: true, color: '666666' }) ], alignment: docx_1.AlignmentType.CENTER, spacing: { after: 600 } })); } return elements; } renderDocBlock(doc) { const content = doc.content || ''; const elements = []; // Split content into lines and process markdown-like syntax const lines = content.split('\n'); let currentParagraph = []; for (const line of lines) { const trimmed = line.trim(); if (trimmed.startsWith('# ')) { // Heading 1 if (currentParagraph.length > 0) { elements.push(this.createParagraph(currentParagraph.join(' '))); currentParagraph = []; } elements.push(new docx_1.Paragraph({ children: [new docx_1.TextRun({ text: trimmed.substring(2), bold: true, size: 28 })], heading: docx_1.HeadingLevel.HEADING_1, spacing: { before: 240, after: 120 } })); } else if (trimmed.startsWith('## ')) { // Heading 2 if (currentParagraph.length > 0) { elements.push(this.createParagraph(currentParagraph.join(' '))); currentParagraph = []; } elements.push(new docx_1.Paragraph({ children: [new docx_1.TextRun({ text: trimmed.substring(3), bold: true, size: 24 })], heading: docx_1.HeadingLevel.HEADING_2, spacing: { before: 200, after: 100 } })); } else if (trimmed.startsWith('### ')) { // Heading 3 if (currentParagraph.length > 0) { elements.push(this.createParagraph(currentParagraph.join(' '))); currentParagraph = []; } elements.push(new docx_1.Paragraph({ children: [new docx_1.TextRun({ text: trimmed.substring(4), bold: true, size: 20 })], heading: docx_1.HeadingLevel.HEADING_3, spacing: { before: 160, after: 80 } })); } else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) { // List item if (currentParagraph.length > 0) { elements.push(this.createParagraph(currentParagraph.join(' '))); currentParagraph = []; } const listText = trimmed.substring(2); elements.push(new docx_1.Paragraph({ children: [ new docx_1.TextRun({ text: '• ' }), ...this.parseInlineFormatting(listText) ], spacing: { after: 80 } })); } else if (trimmed === '') { // Empty line - end current paragraph if (currentParagraph.length > 0) { elements.push(this.createParagraph(currentParagraph.join(' '))); currentParagraph = []; } } else { // Regular text line currentParagraph.push(trimmed); } } // Handle any remaining paragraph content if (currentParagraph.length > 0) { elements.push(this.createParagraph(currentParagraph.join(' '))); } return elements; } renderSlideBlock(slide) { const elements = []; // Add slide title if (slide.title) { elements.push(new docx_1.Paragraph({ children: [ new docx_1.TextRun({ text: slide.title, bold: true, size: 24, color: '2E74B5' }) ], heading: docx_1.HeadingLevel.HEADING_2, spacing: { before: 400, after: 200 } })); } // Add slide content if (slide.content) { for (const contentBlock of slide.content) { if (contentBlock.type === 'unordered_list') { for (const item of contentBlock.items) { const itemText = item.content.map(this.extractText).join(''); elements.push(new docx_1.Paragraph({ children: [ new docx_1.TextRun({ text: '• ' }), ...this.parseInlineFormatting(itemText) ], spacing: { after: 100 } })); } } else if (contentBlock.type === 'paragraph') { const paragraphText = contentBlock.content.map(this.extractText).join(''); elements.push(this.createParagraph(paragraphText)); } } } return elements; } renderSheetBlock(sheet) { const elements = []; // Add sheet title if (sheet.name) { elements.push(new docx_1.Paragraph({ children: [ new docx_1.TextRun({ text: sheet.name, bold: true, size: 20, color: '2E74B5' }) ], heading: docx_1.HeadingLevel.HEADING_3, spacing: { before: 300, after: 150 } })); } if (sheet.data) { // Calculate table dimensions const coords = Object.keys(sheet.data).map(k => k.split(',').map(Number)); const maxRow = Math.max(...coords.map(c => c[0])); const maxCol = Math.max(...coords.map(c => c[1])); const tableRows = []; // Add header row if columns are defined if (sheet.cols) { const cols = Array.isArray(sheet.cols) ? sheet.cols : String(sheet.cols).replace(/[[\]]/g, '').split(',').map(s => s.trim()); const headerCells = cols.map(col => new docx_1.TableCell({ children: [ new docx_1.Paragraph({ children: [new docx_1.TextRun({ text: col, bold: true })] }) ], shading: { fill: 'E8E8E8' } })); tableRows.push(new docx_1.TableRow({ children: headerCells })); } // Add data rows for (let r = 1; r <= maxRow; r++) { const cells = []; for (let c = 1; c <= maxCol; c++) { const key = `${r},${c}`; const value = sheet.data[key] || ''; cells.push(new docx_1.TableCell({ children: [ new docx_1.Paragraph({ children: [new docx_1.TextRun({ text: String(value) })] }) ] })); } tableRows.push(new docx_1.TableRow({ children: cells })); } if (tableRows.length > 0) { const table = new docx_1.Table({ rows: tableRows, width: { size: 100, type: docx_1.WidthType.PERCENTAGE } }); elements.push(table); } } return elements; } createParagraph(text) { return new docx_1.Paragraph({ children: this.parseInlineFormatting(text), spacing: { after: 120 } }); } parseInlineFormatting(text) { const runs = []; let currentIndex = 0; // Simple regex patterns for basic formatting const patterns = [ { regex: /\*\*(.+?)\*\*/g, format: { bold: true } }, { regex: /\*(.+?)\*/g, format: { italics: true } }, { regex: /`(.+?)`/g, format: { font: 'Courier New' } } ]; const matches = []; // Find all formatting matches for (const pattern of patterns) { let match; while ((match = pattern.regex.exec(text)) !== null) { matches.push({ index: match.index, length: match[0].length, text: match[1], format: pattern.format }); } } // Sort matches by index matches.sort((a, b) => a.index - b.index); // Build text runs for (const match of matches) { // Add any plain text before this match if (match.index > currentIndex) { const plainText = text.substring(currentIndex, match.index); if (plainText) { runs.push(new docx_1.TextRun({ text: plainText })); } } // Add formatted text runs.push(new docx_1.TextRun({ text: match.text, ...match.format })); currentIndex = match.index + match.length; } // Add any remaining plain text if (currentIndex < text.length) { const remainingText = text.substring(currentIndex); if (remainingText) { runs.push(new docx_1.TextRun({ text: remainingText })); } } // If no formatting was found, return the entire text as a single run if (runs.length === 0) { runs.push(new docx_1.TextRun({ text })); } return runs; } extractText(run) { if (typeof run === 'string') return run; if (run.type === 'link') return run.text; if (run.type === 'image') return run.alt || ''; if (run.text) return run.text; return ''; } getDocumentTitle(document) { for (const block of document.blocks) { if (block.type === 'meta') { const meta = block; if (meta.props.title) { return String(meta.props.title); } } } return 'OSF Document'; } } exports.DOCXConverter = DOCXConverter; //# sourceMappingURL=docx.js.map