fix(pptx): preserve masters/layouts/theme/fonts/bg + EMF fallback on save#45
Merged
Merged
Conversation
…save Three stacked regressions caused client decks to lose chunks of content after a single text edit: * pptxgenjs regenerates its own slideMasters/, slideLayouts/, theme/ and never emits fonts/. preserveUnknowns now copies the source's chrome (incl. notesMasters, handoutMasters, tags) into the generated zip, splices source's sldMasterIdLst / notesMasterIdLst / embeddedFontLst into presentation.xml, rewrites presentation.xml.rels + each slide's rels to point at the originals, updates [Content_Types].xml, and copies referenced media (renamed on collision with pptxgenjs's media). Bails when source/output aspect ratios differ so 4:3 sources don't get their chrome stretched onto a 16:9 canvas. * pptxgenjs's slide.background only emits flat-hex solidFill, collapsing theme refs / gradients / image-fills. A new per-slide pass copies the source slide's <p:bg> verbatim (with r:id rewrite for image fills), or strips the flat-hex stand-in when the source inherits from the layout / master. * EMF/WMF decode failures used to return null from parsePic; combined with upstream catches this could wipe every element on the same slide (Dickinson slides 2/3/9). The fallback now returns an UnknownElement so the source <p:pic> is re-injected verbatim and PowerPoint renders the metafile natively. Validated end-to-end on Dickinson_Sample_Slides.pptx (9/9 slides retain content, slide 2's schemeClr tx1 theme bg survives) and eon-deck.pptx (28 layouts, 5 embedded fonts, 3 themes preserved).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three stacked regressions caused client decks to lose chunks of visual content after a single text edit. Verified end-to-end against
Dickinson_Sample_Slides.pptxandeon-deck.pptx.1. Slide masters, layouts, theme, embedded fonts dropped on save
pptxgenjs regenerates its own
ppt/slideMasters/,ppt/slideLayouts/,ppt/theme/, and never emitsppt/fonts/. Anything that lived on the master / layout — backgrounds, brand bars, gradients, page numbers, footers, theme palettes, embedded brand fonts — disappeared.preserveUnknownsnow copies the source's chrome (alsonotesMasters,handoutMasters,tags) into the generated zip, splices the source's<p:sldMasterIdLst>/<p:notesMasterIdLst>/<p:embeddedFontLst>intopresentation.xml, rewritespresentation.xml.relsand each slide's rels to point at the original layouts, updates[Content_Types].xml, and copies referenced media (renamed on collision with pptxgenjs's own media). Bails when source/output slide-size aspect ratios differ so 4:3 sources don't get their chrome stretched onto a 16:9 canvas.2. Slide-level
<p:bg>collapsed to flat hexpptxgenjs's
slide.backgroundonly emits a<a:solidFill>with a hex color, so theme refs / gradients / image-fill backgrounds were collapsing (e.g.<a:schemeClr val=\"tx1\"/>became<a:srgbClr val=\"151515\"/>).A new per-slide pass copies the source slide's
<p:bg>element verbatim, rewritingr:embed/r:linkfor image-fill backgrounds (with media copy) using the same machinery asinjectIntoSlide. When the source slide has no explicit bg, the pptxgenjs flat-hex stand-in is stripped so layout / master inheritance can do its job.3. EMF/WMF decode failures wiped the entire slide
parsePicreturnednullon metafile decode failure. Combined with upstream catches, a single un-decodable<p:pic>could wipe every other element on the same slide (Dickinson sample slides 2, 3, 9 — title + subtitle + logo all gone after one text edit on a different slide). The fallback now returns anUnknownElementso the source<p:pic>is re-injected verbatim and the EMF reference survives for PowerPoint to render natively.Validation
Run on the two real attached decks (
.context/attachments/):Dickinson_Sample_Slides.pptx(4:3)spTree; 5/9 broken<a:schemeClr val=\"tx1\"/>theme bg surviveseon-deck.pptx(16:9)Changeset
Minor bump on
@textcortex/slidewise— see.changeset/preserve-chrome-and-emf-fallback.md.Test plan
pnpm typecheckcleanpnpm --filter @textcortex/slidewise test— 37/37 passing (2 new fixture-driven tests inchrome-preservation.test.ts)/tmp/*.saved.pptxif you want to eyeball in PowerPoint before mergeKnown not-yet-fixed
<p:sldSz>from the source and inverse-fit editor coords to source-canvas space — bigger refactor that touches the importer'sfit+pxToInches. Tracking as follow-up.