diff --git a/bin/html2pptx.js b/bin/html2pptx.js index d2ea7b0..7d78f42 100644 --- a/bin/html2pptx.js +++ b/bin/html2pptx.js @@ -154,9 +154,17 @@ async function main() { // ------ 3. DOM extraction (in-browser) ------ const slidesData = await page.evaluate(() => { // ----- Slide detection ----- - let slides = document.querySelectorAll( - 'section[data-slide], section.slide, section' - ); + var slides; + // DashiAI format: .ppt-deck > .ppt-slide + var deck = document.querySelector('.ppt-deck'); + if (deck) { + slides = deck.querySelectorAll('.ppt-slide'); + } + if (!slides || slides.length <= 1) { + slides = document.querySelectorAll( + 'section[data-slide], section.slide, section' + ); + } if (slides.length <= 1) { slides = document.querySelectorAll('[data-slide]'); } @@ -165,40 +173,42 @@ async function main() { return r.width > 0 && r.height > 0; }); - // Tags that are purely structural containers — skip when they have no direct text - var CONTAINER_TAGS = new Set([ - 'div', 'section', 'ul', 'ol', 'nav', 'header', 'footer', - 'main', 'aside', 'article', 'form', 'fieldset', 'table', - 'thead', 'tbody', 'tfoot', 'tr', 'figure', + // Tags whose full text content should be a single text box + var TEXT_BLOCK_TAGS = new Set([ + 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'li', 'td', 'th', 'blockquote', 'figcaption', 'dt', 'dd', 'caption', ]); - function hasDirectText(el) { - var nodes = el.childNodes; - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].nodeType === 3 && nodes[i].textContent.trim()) return true; - } - return false; - } - return slides.map(function (slide) { var slideRect = slide.getBoundingClientRect(); - var allEls = slide.querySelectorAll('*'); var elements = []; - for (var i = 0; i < allEls.length; i++) { - var el = allEls[i]; + // Collect block-level text containers; each becomes one text box + var textBlocks = slide.querySelectorAll( + 'p, h1, h2, h3, h4, h5, h6, li, td, th, blockquote, ' + + 'figcaption, dt, dd, caption' + ); + + for (var i = 0; i < textBlocks.length; i++) { + var el = textBlocks[i]; + // Skip if this block is nested inside another text block (e.g. li inside another li) + var parent = el.parentElement; + var skip = false; + while (parent && parent !== slide) { + if (TEXT_BLOCK_TAGS.has(parent.tagName.toLowerCase())) { + skip = true; + break; + } + parent = parent.parentElement; + } + if (skip) continue; + var text = (el.textContent || '').trim(); if (!text) continue; var rect = el.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) continue; - var tag = el.tagName.toLowerCase(); - var directText = hasDirectText(el); - - // Skip structural containers that have no direct text (e.g. wrapper divs) - if (CONTAINER_TAGS.has(tag) && !directText) continue; - var style = window.getComputedStyle(el); elements.push({ @@ -271,9 +281,8 @@ async function main() { var w = Math.max(el.width * scaleX, 0.3); var h = Math.max(el.height * scaleY, 0.2); - // Font size: proportional to slide height - // CSS px → inches at 96dpi → scaled to PPTX slide height → pt (/72 inches per pt) - var fontSize = el.fontSize * scaleY * 72; + // Font size: CSS px → pt (1pt = 1/72in, CSS 1px = 1/96in, so px × 72/96 = pt) + var fontSize = el.fontSize * 72 / 96; fontSize = Math.max(fontSize, 8); slide.addText(t, {