-
Notifications
You must be signed in to change notification settings - Fork 9
Description
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-previewv1.0.2 - Browser: Chrome (also affects other browsers)
- Usage: Via
@vue-office/pptxv1.0.1
Reproduction Steps
- Create a PPTX file with text containing a line break element
- 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>- Preview the PPTX using pptx-preview
- 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:
- Parsing is correct: The library correctly parses <a:br> elements from the PPTX XML and marks them with {isBr: true} in the row objects.
- 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
}
- 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"):

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! 🙏