Skip to content

Line breaks (<a:br>) render as "undefined" text instead of actual line breaks #14

@Gabe-IncentFit

Description

@Gabe-IncentFit

Bug Description

Line break elements (<a:br>) in PPTX files are currently rendering as the literal text "undefined" instead of being rendered as actual line breaks.

Environment

  • Package: pptx-preview v1.0.2
  • Browser: Chrome (also affects other browsers)
  • Usage: Via @vue-office/pptx v1.0.1

Reproduction Steps

  1. Create a PPTX file with text containing a line break element
  2. The PPTX XML contains structure like:
<a:p>
    <a:r>
        <a:t>First Line Text</a:t>
    </a:r>
    <a:br>
        <a:rPr lang="en" sz="1100" ...>
            <a:latin typeface="DM Sans"/>
            <a:ea typeface="DM Sans"/>
        </a:rPr>
    </a:br>
    <a:r>
        <a:t>Second Line Text</a:t>
    </a:r>
</a:p>
  1. Preview the PPTX using pptx-preview
  2. Observe the rendered output

Expected Behavior

The text should render as:

First Line Text
Second Line Text

With a proper line break between the two lines.

Actual Behavior

The text renders as:
First Line TextundefinedSecond Line Text
The literal text "undefined" appears where the line break should be.

Root Cause Analysis

After analyzing the compiled library code, I found the issue:

  1. Parsing is correct: The library correctly parses <a:br> elements from the PPTX XML and marks them with {isBr: true} in the row objects.
  2. Rendering is incorrect: In the paragraph rendering loop, the library calls the text span creation function (qQ) for all rows, including line break objects:
// Current buggy code (decompiled/simplified):
for(var h=0, w=c; h<w.length; h++){
    var b = w[h];  // b might be {isBr: true}
    paragraphElement.appendChild(qQ(b, ...))  // qQ expects b.text property
}
  1. The problem: The text span function qQ tries to access row.text, but line break objects don't have a text property, so it becomes undefined:
function qQ(r, e, t){
    var i = r.text;  // i = undefined when r.isBr === true
    s.innerHTML = i;  // Sets innerHTML to undefined → renders as "undefined"
    // ...
    return s;
}

Recommended Solution
Add a check for row.isBr before rendering, and create a
element instead of a text span:

// Fixed code (pseudocode):
for(var h=0, w=c; h<w.length; h++){
    var b = w[h];
    
    if(b.isBr){
        // Create line break element
        paragraphElement.appendChild(document.createElement('br'));
    } else {
        // Create text span
        paragraphElement.appendChild(qQ(b, ...));
    }
}

This is a one-line fix that should properly render line breaks as
elements instead of trying to render them as text spans with undefined content.

Example PPTX XML

Here's a complete example of a paragraph with a line break from a real PPTX file:

<a:p>
    <a:pPr marL="0" marR="0" lvl="0" indent="0" algn="l" rtl="0">
        <a:lnSpc><a:spcPct val="102000"/></a:lnSpc>
        <a:spcBef><a:spcPts val="0"/></a:spcBef>
        <a:spcAft><a:spcPts val="0"/></a:spcAft>
        <a:buNone/>
    </a:pPr>
    <a:r>
        <a:rPr lang="en" sz="1100" b="0" i="0" u="none" strike="noStrike" cap="none" dirty="0">
            <a:solidFill><a:srgbClr val="314DFF"/></a:solidFill>
            <a:latin typeface="DM Sans"/>
            <a:ea typeface="DM Sans"/>
            <a:cs typeface="DM Sans"/>
            <a:sym typeface="DM Sans"/>
        </a:rPr>
        <a:t>Fitness Rewards</a:t>
    </a:r>
    <a:br>
        <a:rPr lang="en" sz="1100" b="0" i="0" u="none" strike="noStrike" cap="none" dirty="0">
            <a:latin typeface="DM Sans"/>
            <a:ea typeface="DM Sans"/>
            <a:cs typeface="DM Sans"/>
        </a:rPr>
    </a:br>
    <a:r>
        <a:rPr lang="en" sz="900" b="0" i="0" u="none" strike="noStrike" cap="none" dirty="0">
            <a:solidFill><a:srgbClr val="373737"/></a:solidFill>
            <a:latin typeface="DM Sans"/>
            <a:ea typeface="DM Sans"/>
            <a:cs typeface="DM Sans"/>
            <a:sym typeface="DM Sans"/>
        </a:rPr>
        <a:t>Earn rewards for being physically active.</a:t>
    </a:r>
    <a:endParaRPr .../>
</a:p>

Screenshot

Current behavior (showing "undefined"):
Image

Impact

This bug affects any PPTX file that uses line breaks within paragraphs, which is a common formatting technique in presentations. The "undefined" text is visually jarring and unprofessional.

Workaround

For users encountering this issue, a temporary workaround is to post-process the rendered DOM:

// After rendering completes
const wrapper = document.querySelector('.pptx-preview-wrapper');
const spans = wrapper.querySelectorAll('span');
spans.forEach(span => {
    if (span.textContent === 'undefined' && !span.querySelector('*')) {
        const br = document.createElement('br');
        span.parentNode.replaceChild(br, span);
    }
});

Additional Information

  • This issue was discovered while using @vue-office/pptx which depends on pptx-preview
  • The bug is in the core pptx-preview library, not in the Vue wrapper
  • I'm willing to test any fixes if provided
  • If source code access is available, I'd be happy to contribute a PR with the fix

Thank you for maintaining this library! 🙏

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions