diff --git a/bin/html2pptx.js b/bin/html2pptx.js
index 7a38b3a..6660185 100644
--- a/bin/html2pptx.js
+++ b/bin/html2pptx.js
@@ -177,6 +177,30 @@ async function main() {
style.visibility !== 'hidden';
});
+ // Recursively extract inline text runs with computed styles
+ function extractRuns(node) {
+ var runs = [];
+ for (var ri = 0; ri < node.childNodes.length; ri++) {
+ var child = node.childNodes[ri];
+ if (child.nodeType === 3) {
+ var t = (child.textContent || '').trim();
+ if (t) runs.push({ text: t });
+ } else if (child.nodeType === 1) {
+ var cs = window.getComputedStyle(child);
+ var childRuns = extractRuns(child);
+ for (var cj = 0; cj < childRuns.length; cj++) {
+ childRuns[cj].bold = childRuns[cj].bold || parseInt(cs.fontWeight) >= 600;
+ childRuns[cj].italic = childRuns[cj].italic || cs.fontStyle === 'italic';
+ childRuns[cj].color = childRuns[cj].color || cs.color;
+ childRuns[cj].fontSize = childRuns[cj].fontSize || parseFloat(cs.fontSize);
+ childRuns[cj].fontFamily = childRuns[cj].fontFamily || cs.fontFamily;
+ }
+ runs = runs.concat(childRuns);
+ }
+ }
+ return runs.length > 0 ? runs : [{ text: node.textContent || '' }];
+ }
+
// Tags whose full text content should be a single text box
var TEXT_BLOCK_TAGS = new Set([
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
@@ -207,7 +231,9 @@ async function main() {
}
if (skip) continue;
- var text = (el.textContent || '').trim();
+ var runs = extractRuns(el);
+ if (!runs || runs.length === 0) continue;
+ var text = runs.map(function (r) { return r.text || ''; }).join(' ').trim();
if (!text) continue;
var rect = el.getBoundingClientRect();
@@ -215,7 +241,13 @@ async function main() {
var style = window.getComputedStyle(el);
+ // Truncate overly long runs
+ runs.forEach(function (r) {
+ if (r.text && r.text.length > 2000) r.text = r.text.substring(0, 2000);
+ });
+
elements.push({
+ runs: runs,
text: text.substring(0, 2000),
x: rect.left - slideRect.left,
y: rect.top - slideRect.top,
@@ -285,25 +317,74 @@ async function main() {
var w = Math.max(el.width * scaleX, 0.3);
var h = Math.max(el.height * scaleY, 0.2);
- // Font size: CSS px → pt (1pt = 1/72in, CSS 1px = 1/96in, so px × 72/96 = pt)
+ // Bounds constraints: prevent textbox from overflowing slide
+ var MAX_W = 10 - x;
+ var MAX_H = 5.625 - y;
+ w = Math.min(w, MAX_W);
+ h = Math.min(h, MAX_H);
+
+ // Base font size: CSS px → pt (1pt = 1/72in, CSS 1px = 1/96in)
var fontSize = el.fontSize * 72 / 96;
fontSize = Math.max(fontSize, 8);
- slide.addText(t, {
- x: x,
- y: y,
- w: w,
- h: h,
- fontSize: fontSize,
- fontFace: (el.fontFamily || 'Arial').split(',')[0].trim().replace(/['"]/g, ''),
- color: parseCSSColor(el.color, '#000000'),
- bold: parseFontWeight(el.fontWeight),
- italic: el.fontStyle === 'italic' || el.fontStyle === 'oblique',
- align: normalizeAlign(el.textAlign),
- valign: 'top',
- wrap: true,
- margin: 0,
- });
+ // Check if rich text is needed (runs with differing styles)
+ var hasRuns = el.runs && el.runs.length > 0;
+ var isSimpleRun = hasRuns && el.runs.length === 1 &&
+ el.runs[0].bold === undefined &&
+ el.runs[0].italic === undefined &&
+ el.runs[0].color === undefined &&
+ el.runs[0].fontSize === undefined &&
+ el.runs[0].fontFamily === undefined;
+
+ if (hasRuns && !isSimpleRun && el.runs.length > 1) {
+ // Rich text: render each run with its own style
+ var pptxRuns = el.runs.map(function (run) {
+ if (!run.text || !run.text.trim()) return null;
+ var baseSize = el.fontSize * 72 / 96;
+ return {
+ text: run.text,
+ options: {
+ fontSize: run.fontSize ? Math.max(run.fontSize * 72 / 96, 8) : baseSize,
+ bold: run.bold === true || (run.bold === undefined && parseFontWeight(el.fontWeight)),
+ italic: run.italic === true || (run.italic === undefined && (el.fontStyle === 'italic' || el.fontStyle === 'oblique')),
+ color: run.color ? parseCSSColor(run.color, '#000000') : parseCSSColor(el.color, '#000000'),
+ fontFace: run.fontFamily
+ ? run.fontFamily.split(',')[0].trim().replace(/['"]/g, '')
+ : (el.fontFamily || 'Arial').split(',')[0].trim().replace(/['"]/g, ''),
+ },
+ };
+ }).filter(Boolean);
+
+ if (pptxRuns.length > 0) {
+ slide.addText(pptxRuns, {
+ x: x,
+ y: y,
+ w: w,
+ h: h,
+ align: normalizeAlign(el.textAlign),
+ valign: 'top',
+ wrap: true,
+ margin: 0,
+ });
+ }
+ } else {
+ // Plain text: original single-style logic
+ slide.addText(t, {
+ x: x,
+ y: y,
+ w: w,
+ h: h,
+ fontSize: fontSize,
+ fontFace: (el.fontFamily || 'Arial').split(',')[0].trim().replace(/['"]/g, ''),
+ color: parseCSSColor(el.color, '#000000'),
+ bold: parseFontWeight(el.fontWeight),
+ italic: el.fontStyle === 'italic' || el.fontStyle === 'oblique',
+ align: normalizeAlign(el.textAlign),
+ valign: 'top',
+ wrap: true,
+ margin: 0,
+ });
+ }
} catch (err) {
// Skip individual element failure
}