Skip to content

fix(pptx): carry verbatim custGeom OOXML in deck JSON (cross-process serialize)#69

Merged
karthikmudunuri merged 1 commit into
mainfrom
karthikmudunuri/fix-custgeom-verbatim
May 31, 2026
Merged

fix(pptx): carry verbatim custGeom OOXML in deck JSON (cross-process serialize)#69
karthikmudunuri merged 1 commit into
mainfrom
karthikmudunuri/fix-custgeom-verbatim

Conversation

@karthikmudunuri
Copy link
Copy Markdown
Member

Path B from the eon-deck custGeom investigation — the architecturally-correct fix for the cross-process failure mode (parse client-side → store deck JSON → serialize server-side).

Root cause (confirmed in code)

High-fidelity replay of imported elements depends on two module-global registries — sourceBufferCache (pptxToDeck.ts:366) and elementSourceRegistry (line 394) — populated only by parsePptx and never serialized into the deck JSON. Per-element verbatim replay gates on getElementSource(el.id) (deckToPptx.ts). So when serialize runs in a different process from the import, the registry is empty → every element is re-synthesised from its deck fields.

Synthesis can't represent OOXML even-odd / winding (custGeom has no winding flag), so complex vectors blank. I verified the bicycle's synthesized custGeom is structurally perfect (6 closed subpaths, coords fill the box, no NaN) — which is exactly why the fix isn't "patch the synth" but "don't synth at all when we have the source."

Fix

The importer stamps the verbatim <p:sp> of self-contained custGeom shapes onto the element as pristineOoxml = { xml, snapshot }, which rides in the deck JSON. On serialize, an unedited such shape (snapshot still matches) is replayed verbatim — exact source winding/geometry — instead of re-synthesised; cNvPr/@id is rewritten to avoid spTree collisions. Edited shapes fall back to synthesis.

Same persist-in-JSON pattern already used for embedded fonts (deck.fonts), scoped to vector shapes so JSON bloat is negligible (a few KB/deck — the collaborator measured ~13 KB across this 25-slide deck).

Self-contained only: shapes whose source XML references r:embed/r:id/r:link (images) or a:schemeClr (theme colours) are not stamped — the fragment would be invalid without the source archive/theme. Those still synth.

Coexists cleanly with same-process replay: in-process, the shape is caught earlier by isPristineImportedElement (registry hit) and replayed through the source archive; pristineOoxml is the cross-process fallback only.

Tests (+3, 61 pass)

  • Importer stamps pristineOoxml (with custGeom + snapshot) on a round-tripped self-contained custGeom shape.
  • Serialize replays the verbatim <p:sp> (source fill + marker present) and rewrites the colliding cNvPr id.
  • Edited shape (snapshot diverges) falls back to synthesis.

tsc -b clean, build:lib succeeds. Branch is off post-#68 main.

Scope note

This is the durable class-killer for cross-process custGeom (and the pattern extends to radial/other preserved elements later). The fill="darken" + path→EMU synth fixes from #66 are already merged; this is complementary — it makes unedited vectors byte-faithful regardless of process.

@karthikmudunuri karthikmudunuri merged commit 65eeac2 into main May 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant