Skip to content

feat(pptx/render): re-land #96 — applyEdits layout-instantiation + headless render API + font transparency#98

Merged
karthikmudunuri merged 2 commits into
mainfrom
karthikmudunuri/render-layout-fonts
Jun 12, 2026
Merged

feat(pptx/render): re-land #96 — applyEdits layout-instantiation + headless render API + font transparency#98
karthikmudunuri merged 2 commits into
mainfrom
karthikmudunuri/render-layout-fonts

Conversation

@karthikmudunuri

Copy link
Copy Markdown
Member

Why this PR exists

PR #96 was accidentally merged into the #95 branch (karthikmudunuri/work-on-plan) instead of main. #95 reached main (as e56ddd2), but #96's changes — layout-instantiation in applyEdits, the headless render API, and font transparency — were stranded on the now-dead branch and never landed.

This PR re-lands exactly those changes on top of current main (cherry-picked a38dd75 + 76f1989, clean — no conflicts; both changesets restored). It supersedes the mis-merged #96.

What's included

P0 — layout-instantiation inside applyEdits. source: { layoutId, fills? } instantiates a fresh slide from the template's own layouts losslessly (binds to ppt/slideLayouts/<id>.xml, inherits chrome; every other part byte-identical). Every placeholder (text and non-text) is addressable via layoutSlotElementId(layoutId, key). Unresolvable layout → onWarning + skip.

P1 — renderDeckToImages (headless visual-QA render). Browser-free SVG renderer drawing what the editor draws — native charts (ECharts SSR), diagrams, text/shapes/images — not raster fallbacks. renderDeckToSvg / renderDeckToImages / renderSlideToImage / renderPptxToImages; rasterisation via an injected rasterizeSvg hook (default tries @resvg/resvg-js, no hard dep). ECharts is lazy-loaded so the editor bundle stays ~1.1 MB.

P2 — font transparency. parsePptx stamps deck.fontUsage: { family, embedded }[] — every family the text uses, flagged embedded vs only-referenced (separate field from the load-bearing deck.fonts).

Verification

  • pnpm typecheck
  • pnpm test ✅ — 184 passed, 9 skipped (fixture-gated)
  • pnpm --filter @textcortex/slidewise build:lib ✅ — no warnings, echarts in its own lazy chunk

Tests: apply-edits-layout.test.ts, render-deck.test.ts, font-usage.test.ts. Two changesets included.

…ariety)

Support source:{layoutId,fills} in a PlannedSlide — instantiate a fresh slide
from one of the template's OWN layouts inside the lossless byte-patch path. The
layout is already a part of source, so the new slide binds to its
ppt/slideLayouts/<id>.xml (inheriting theme/master/background chrome) while every
other part stays byte-identical.

- Each layout placeholder becomes an addressable, positioned element keyed by
  the deterministic id layoutSlotElementId(layoutId, key) (exported). Text/obj
  slots fill from fills + edit via setText; picture slots get a transparent
  placeholder blip so setImage can repoint them; chart/table/other expose
  geometry for addChart/addDiagram.
- Placeholder geometry is read EMU-native from the layout, falling back to the
  matching master slot — no canvas-px round-trip.
- Unresolvable layoutId → onWarning + skip (never ship a wrong slide).

Threads an instantiated-block map through the edit ops so edits resolve against
the freshly-built slide XML. Tests: mixed clone+instantiate (3 slides, correct
layout rel, chrome inherited, cloned parts byte-identical, zero dangling rels),
master-geometry inheritance, unresolved-layout skip.
…parency

Headless render-to-image for a server-side visual-QA loop. Browser-free (no
Playwright/Chromium/DOM): composes a deterministic SVG per slide that draws what
the editor draws — native charts (buildChartOption + ECharts SSR), diagrams
(layoutDiagram), text/shapes/images/backgrounds in z-order — not the OOXML
raster fallbacks.

- renderDeckToSvg / renderDeckToImages / renderSlideToImage / renderPptxToImages.
- Rasterisation is an injected hook (opts.rasterizeSvg, e.g. @resvg/resvg-js);
  default tries a dynamic resvg import and throws a clear error otherwise — no
  hard native dep. ECharts is loaded on demand so it never bloats the editor
  bundle (main chunk stays ~1.1MB).
- opts: slides (1-based subset), dpi, format, maxWidth.

Font transparency: parsePptx stamps deck.fontUsage {family,embedded}[] — every
family the text uses, flagged embedded (a real ppt/fonts/* part in
<p:embeddedFontLst>) vs only referenced, so the host can warn on missing brand
fonts. Read-only diagnostic, distinct from deck.fonts.

Tests: render-deck (chart colours, diagram, image, subset, injected rasteriser,
maxWidth, browser-free) + font-usage (embed vs reference).
@karthikmudunuri karthikmudunuri merged commit 652f804 into main Jun 12, 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