omniscript-converters
Version:
Format converters for OmniScript Format (OSF) - Convert to/from DOCX, PPTX, XLSX, PDF
351 lines • 13.5 kB
JavaScript
"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