UNPKG

pptx-automizer

Version:

A template based pptx generator

261 lines 9.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HtmlToMultiTextHelper = void 0; const xmldom_1 = require("@xmldom/xmldom"); const general_helper_1 = require("./general-helper"); class HtmlToMultiTextHelper { /** * Converts HTML string to MultiTextParagraph array * @param html HTML string to convert * @returns Array of MultiTextParagraph objects */ run(html) { const paragraphs = []; const parser = new xmldom_1.DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const currentBulletLevel = 0; // Get the body element using getElementsByTagName const bodyElement = doc.getElementsByTagName('body')[0]; // Process all top-level elements if (bodyElement) { Array.from(bodyElement.childNodes).forEach((node) => { if (node.nodeType === xmldom_1.Node.ELEMENT_NODE) { this.processNode(node, currentBulletLevel, paragraphs); } }); } else { (0, general_helper_1.log)('You need to provide a <body> tag for HtmlToMultiText', 0); } return paragraphs; } /** * Processes an HTML node and converts it to MultiTextParagraph objects */ processNode(node, level = 0, paragraphs, bulletLevel = { value: 0 }) { const tagName = node.nodeName.toLowerCase(); switch (tagName) { case 'p': this.processParagraph(node, paragraphs); break; case 'ul': case 'ol': this.processList(node, paragraphs, bulletLevel); break; case 'li': this.processListItem(node, level, paragraphs); break; default: // For other elements, process their children Array.from(node.childNodes).forEach((child) => { this.processNode(child, level, paragraphs, bulletLevel); }); } } /** * Processes a paragraph element */ processParagraph(node, paragraphs) { const textRuns = this.createTextRuns(node); // If no text runs were created, add an empty one if (textRuns.length === 0) { textRuns.push({ text: '' }); } // Create the paragraph paragraphs.push({ paragraph: { level: 0, bullet: false, alignment: 'l', }, textRuns, }); } /** * Processes a list (ul/ol) element */ processList(node, paragraphs, bulletLevel) { // Increase bullet level for nested lists bulletLevel.value++; // Process all list items Array.from(node.childNodes).forEach((child) => { this.processNode(child, bulletLevel.value, paragraphs, bulletLevel); }); // Decrease bullet level after processing list bulletLevel.value--; } /** * Processes a list item element */ processListItem(node, level, paragraphs) { const textRuns = []; // Process all child nodes to create text runs (except nested lists) Array.from(node.childNodes).forEach((child) => { // Skip nested lists, they'll be processed separately if (child.nodeType === xmldom_1.Node.ELEMENT_NODE && (child.nodeName.toLowerCase() === 'ul' || child.nodeName.toLowerCase() === 'ol')) { return; } const result = this.processTextNode(child); if (Array.isArray(result)) { textRuns.push(...result.filter((run) => run.text)); } else if (result.text) { textRuns.push(result); } }); // If no text runs were created, add an empty one if (textRuns.length === 0) { textRuns.push({ text: '' }); } // Create the paragraph for the list item paragraphs.push({ paragraph: { level, bullet: true, alignment: 'l', }, textRuns, }); // Process nested lists if any Array.from(node.childNodes).forEach((child) => { if (child.nodeName.toLowerCase() === 'ul' || child.nodeName.toLowerCase() === 'ol') { const bulletLevel = { value: level }; this.processNode(child, level + 1, paragraphs, bulletLevel); } }); } /** * Creates text runs from an element's child nodes */ createTextRuns(node) { const textRuns = []; // Process all child nodes to create text runs // Using Array.from to convert NodeList to array that has forEach Array.from(node.childNodes).forEach((child) => { const result = this.processTextNode(child); if (Array.isArray(result)) { textRuns.push(...result.filter((run) => run.text)); } else if (result.text) { textRuns.push(result); } }); return textRuns; } /** * Processes a text node and creates a TextRun or array of TextRuns */ processTextNode(node, style = {}) { // If this is a text node, return its content if (node.nodeType === xmldom_1.Node.TEXT_NODE) { const text = node.textContent || ''; if (text.trim() === '') { return { text: text, style }; } return { text, style }; } // If this is an element, handle specific styling if (node.nodeType === xmldom_1.Node.ELEMENT_NODE) { const element = node; const newStyle = this.applyElementStyles(element, Object.assign({}, style)); // For leaf nodes (no children), just return the text content with style if (element.childNodes.length === 0) { return { text: element.textContent || '', style: newStyle }; } // For nodes with children, recursively process children return this.processElementWithChildren(element, newStyle); } return { text: '' }; } /** * Applies styles based on the element type and attributes */ applyElementStyles(element, style) { const newStyle = Object.assign({}, style); const tagName = element.tagName.toLowerCase(); // Handle styling based on element type if (tagName === 'strong' || tagName === 'b') { newStyle.isBold = true; } else if (tagName === 'em' || tagName === 'i') { newStyle.isItalics = true; } else if (tagName === 'ins') { newStyle.isUnderlined = true; } else if (tagName === 'a') { this.processHyperlink(element, newStyle); } else if (tagName === 'span') { this.processSpanStyles(element, newStyle); } return newStyle; } /** * Processes anchor element for hyperlinks */ processHyperlink(element, style) { const href = element.getAttribute('href'); if (!href) return; if (!isNaN(parseInt(href))) { // Internal slide link: <a href="3">Link to slide 3</a> const slideNumber = parseInt(href); style.hyperlink = { target: slideNumber, isInternal: true, }; } else { // External link: <a href="https://example.com">Link</a> style.hyperlink = { target: href, isInternal: false, }; } } /** * Processes span element styles */ processSpanStyles(element, style) { const styleAttr = element.getAttribute('style'); if (!styleAttr) return; // Extract font size const fontSizeMatch = styleAttr.match(/font-size:\s*(\d+)px/i); if (fontSizeMatch && fontSizeMatch[1]) { style.size = parseInt(fontSizeMatch[1]) * 100; // Convert px to points (100ths of point) } // Extract color const colorMatch = styleAttr.match(/color:\s*([^;]+)/i); if (colorMatch && colorMatch[1]) { style.color = { type: 'srgbClr', value: colorMatch[1].trim(), }; } } /** * Processes an element with child nodes */ processElementWithChildren(element, style) { const runs = []; Array.from(element.childNodes).forEach((child) => { const childRun = this.processTextNode(child, style); if (Array.isArray(childRun)) { // If the result is an array of runs, add them all runs.push(...childRun.filter((run) => run.text)); } else if (childRun.text) { // If it's a single run, add it runs.push(childRun); } }); return runs; } } exports.HtmlToMultiTextHelper = HtmlToMultiTextHelper; //# sourceMappingURL=html-to-multitext-helper.js.map