(element, baseOptions = {}, runs = [], baseTextTransform = (x) => x)
| 417 | |
| 418 | // Parse inline formatting tags (<b>, <i>, <u>, <strong>, <em>, <span>) into text runs |
| 419 | const parseInlineFormatting = (element, baseOptions = {}, runs = [], baseTextTransform = (x) => x) => { |
| 420 | let prevNodeIsText = false; |
| 421 | |
| 422 | element.childNodes.forEach((node) => { |
| 423 | let textTransform = baseTextTransform; |
| 424 | |
| 425 | const isText = node.nodeType === Node.TEXT_NODE || node.tagName === 'BR'; |
| 426 | if (isText) { |
| 427 | const text = node.tagName === 'BR' ? '\n' : textTransform(node.textContent.replace(/\s+/g, ' ')); |
| 428 | const prevRun = runs[runs.length - 1]; |
| 429 | if (prevNodeIsText && prevRun) { |
| 430 | prevRun.text += text; |
| 431 | } else { |
| 432 | runs.push({ text, options: { ...baseOptions } }); |
| 433 | } |
| 434 | |
| 435 | } else if (node.nodeType === Node.ELEMENT_NODE && node.textContent.trim()) { |
| 436 | const options = { ...baseOptions }; |
| 437 | const computed = window.getComputedStyle(node); |
| 438 | |
| 439 | // Handle inline elements with computed styles |
| 440 | if (node.tagName === 'SPAN' || node.tagName === 'B' || node.tagName === 'STRONG' || node.tagName === 'I' || node.tagName === 'EM' || node.tagName === 'U') { |
| 441 | const isBold = computed.fontWeight === 'bold' || parseInt(computed.fontWeight) >= 600; |
| 442 | if (isBold && !shouldSkipBold(computed.fontFamily)) options.bold = true; |
| 443 | if (computed.fontStyle === 'italic') options.italic = true; |
| 444 | if (computed.textDecoration && computed.textDecoration.includes('underline')) options.underline = true; |
| 445 | if (computed.color && computed.color !== 'rgb(0, 0, 0)') { |
| 446 | options.color = rgbToHex(computed.color); |
| 447 | const transparency = extractAlpha(computed.color); |
| 448 | if (transparency !== null) options.transparency = transparency; |
| 449 | } |
| 450 | if (computed.fontSize) options.fontSize = pxToPoints(computed.fontSize); |
| 451 | |
| 452 | // Apply text-transform on the span element itself |
| 453 | if (computed.textTransform && computed.textTransform !== 'none') { |
| 454 | const transformStr = computed.textTransform; |
| 455 | textTransform = (text) => applyTextTransform(text, transformStr); |
| 456 | } |
| 457 | |
| 458 | // Validate: Check for margins on inline elements |
| 459 | if (computed.marginLeft && parseFloat(computed.marginLeft) > 0) { |
| 460 | errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-left which is not supported in PowerPoint. Remove margin from inline elements.`); |
| 461 | } |
| 462 | if (computed.marginRight && parseFloat(computed.marginRight) > 0) { |
| 463 | errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-right which is not supported in PowerPoint. Remove margin from inline elements.`); |
| 464 | } |
| 465 | if (computed.marginTop && parseFloat(computed.marginTop) > 0) { |
| 466 | errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-top which is not supported in PowerPoint. Remove margin from inline elements.`); |
| 467 | } |
| 468 | if (computed.marginBottom && parseFloat(computed.marginBottom) > 0) { |
| 469 | errors.push(`Inline element <${node.tagName.toLowerCase()}> has margin-bottom which is not supported in PowerPoint. Remove margin from inline elements.`); |
| 470 | } |
| 471 | |
| 472 | // Recursively process the child node. This will flatten nested spans into multiple runs. |
| 473 | parseInlineFormatting(node, options, runs, textTransform); |
| 474 | } |
| 475 | } |
| 476 |
no test coverage detected