PPTX Transition Core
The shared transition core owns page-transition vocabulary, slide-advance semantics, OOXML mutation, MCE preservation, package timing enablement, and read-back validation for every PPTX route.
1. Ownership
| Concern | Owner |
|---|---|
| Page transition registry | scripts/pptx_transitions.py |
| In-slide object animation | scripts/pptx_animations.py |
| Generated PPTX adapter | svg_to_pptx/pptx_package/builder.py |
| Public workflow | references/animations.md |
Hard rule: adapters resolve route policy, then call the shared core. They must not build, replace, or patch a transition with route-local XML or regex.
2. Domain Model
| Layer | Meaning | OOXML |
|---|---|---|
| Enter | How the current slide appears from the preceding slide | Native effect, Effect Options, and duration |
| Sound | Optional cue played with the current slide's transition | p:sndAc/p:stSnd/p:snd with an embedded WAV relationship |
| Advance | How the current slide leaves for the next slide | advClick and advTm |
Enter policy:
| Policy | Behavior |
|---|---|
| preserve | Keep the source visual transition, including unknown extensions |
| replace | Write the requested supported effect |
| none | Write no visual effect |
Advance mode:
| Mode | Behavior |
|---|---|
| preserve | Keep source advClick and advTm |
| click | Click advance only |
| after | Timed advance only |
| both | Click or timed advance, whichever occurs first |
| narration | Timed advance from narration lead-in, audio duration, and page-tail padding; click disabled |
Hard rule: enter=none may coexist with a sound and/or timed advance. The
valid result is a non-visual p:transition with p:sndAc, advance attributes,
or both, and no visual-effect child.
3. Compatibility Contract
The native registry covers the complete current PowerPoint transition gallery: 12 Subtle effects, 29 Exciting effects, and 7 Dynamic Content effects. New selection, sidecars, plans, conversion traces, help, and writers use only these 48 native keys.
Eight established low-level names remain valid at input boundaries. They
normalize to one native effect plus native effect_options; they are not a
second transition registry:
| Compatibility input | Native request |
|---|---|
strips |
wipe with direction: right |
circle |
shape with shape: circle |
diamond |
shape with shape: diamond |
plus |
shape with shape: plus |
newsflash |
flash |
pull |
uncover |
wedge |
clock with style: wedge |
wheel |
clock with style: clockwise |
Standard PresentationML effects use a direct p:transition carrier:
| Effect | Required primary child and attributes |
|---|---|
| fade | p:fade |
| push | p:push dir=r |
| wipe | p:wipe dir=r |
| split | p:split |
| cut | p:cut |
| random_bars | p:randomBar dir=vert |
| shape | p:circle |
| uncover | p:pull dir=r |
| cover | p:cover dir=r |
| dissolve | p:dissolve |
| checkerboard | p:checker |
| blinds | p:blinds dir=vert |
| clock | p:wheel spokes=1 |
| random | p:random |
| box | p:zoom |
| comb | p:comb |
Office 2010 effects use a p14 Choice with a p:fade Fallback:
| Effect | Required primary child and attributes |
|---|---|
| reveal | p14:reveal dir=r |
| flash | p14:flash |
| ripple | p14:ripple |
| honeycomb | p14:honeycomb |
| glitter | p14:glitter |
| vortex | p14:vortex dir=r |
| shred | p14:shred dir=out |
| switch | p14:switch dir=r |
| flip | p14:flip dir=r |
| gallery | p14:gallery dir=r |
| cube | p14:prism dir=r |
| doors | p14:doors dir=vert |
| zoom | p14:warp dir=in |
| pan | p14:pan dir=r |
| ferris_wheel | p14:ferris dir=r |
| conveyor | p14:conveyor dir=r |
| rotate | p14:prism dir=r isContent=1 |
| window | p14:window |
| orbit | p14:prism dir=r isContent=1 isInverted=1 |
| fly_through | p14:flythrough |
Office 2012 effects use a p15 Choice with a p:fade Fallback:
| Effect | Required primary child and attributes |
|---|---|
| fall_over | p15:prstTrans prst=fallOver invX=1 |
| drape | p15:prstTrans prst=drape invX=1 |
| curtains | p15:prstTrans prst=curtains |
| wind | p15:prstTrans prst=wind |
| prestige | p15:prstTrans prst=prestige |
| fracture | p15:prstTrans prst=fracture |
| crush | p15:prstTrans prst=crush |
| peel_off | p15:prstTrans prst=peelOff invX=1 |
| page_curl | p15:prstTrans prst=pageCurlSingle invX=1 |
| airplane | p15:prstTrans prst=airplane |
| origami | p15:prstTrans prst=origami |
morph uses p159:morph option=byObject in an Office 2015 Choice with a
p:fade Fallback. none is the explicit no-visual-effect input and therefore
is not a registry entry.
3.1 Native Effect Options
Use effect_options only with an explicit native effect. Omitted options use
the PowerPoint-authored default reported by --describe-transition:
| Effect | Supported options |
|---|---|
morph |
morph_by: object, word, character |
fade |
style: smoothly, through_black |
push, wipe, vortex, cube, pan, rotate, orbit |
direction: left, right, up, down |
split |
orientation: horizontal, vertical; direction: out, in |
reveal |
direction: right, left; through_black: boolean |
cut |
through_black: boolean |
random_bars, blinds, doors |
orientation: vertical, horizontal |
checkerboard |
direction: across, down |
comb, window |
orientation: horizontal, vertical |
shape |
shape: circle, diamond, plus |
uncover, cover |
direction: left, right, up, down, up_left, up_right, down_left, down_right |
fall_over, drape, wind, peel_off, airplane, origami |
direction: right, left |
page_curl |
direction: right, left; pages: single, double |
clock |
style: clockwise, counterclockwise, wedge |
ripple |
origin: center, up_left, up_right, down_left, down_right |
glitter |
shape: diamond, hexagon; direction: right, left, up, down |
shred |
pattern: strips, rectangle; direction: out, in |
switch, flip, gallery, ferris_wheel, conveyor |
direction: right, left |
box, zoom |
direction: out, in |
fly_through |
direction: in, out; bounce: boolean |
| All other native effects | No Effect Options |
Example:
{
"transition": {
"effect": "page_curl",
"effect_options": {
"direction": "left",
"pages": "double"
},
"duration": 0.6,
"sound": "sounds/bigsoundbank/<file>.wav"
}
}transition.sound is optional and accepts a .wav path resolved by the
generated-project adapter. A bundled-library choice is synced on demand only
after the visual transition plan is complete, then referenced by its
project-relative sounds/<namespace>/<file>.wav path. The adapter packages the
file and passes the shared core an embedded relationship id/name; the core
never reads templates/sounds/ or resolves library ids.
Inspect the exact contract, including compatibility desugaring:
python3 skills/ppt-master/scripts/pptx_animations.py --describe-transition page_curl
python3 skills/ppt-master/scripts/pptx_animations.py --describe-transition diamondRead-back reports the canonical native effect, its complete effective options, the raw OOXML child, and raw attributes. This makes option loss a validation failure rather than a silent downgrade.
Hard rule — no downgrade:
- Never rename or remove an established effect.
- Never omit its established direction or split attributes.
- Reject an unknown requested effect; never substitute fade.
- Preserve an unknown source effect when the route selects preserve.
- An extension counts as successful only when the primary Choice contains the requested effect. A fallback alone is not success.
3.2 Deterministic Morph Identity
The generated route may add an explicit slides.<destination>.morph block to
bind direct-root SVG groups across adjacent slides. The sidecar stable key is
lowered to the same top-level p:cNvPr@name="!!<key>" on both final
Slide-local objects. This does not create an Animation Pane row and does not
change either object's numeric shape id.
The full plan is resolved before any SVG conversion so a source group named by the following slide remains a stable top-level target. Names are written only after flat/structured/preserve processing has finished; structured slide-shape roster expectations are then refreshed. Package read-back requires:
- the declared source to be the immediately preceding public slide;
- exactly one
!!<key>object on each side; - the same OOXML object container type on both sides;
- Morph by object on the destination; and
- no structural target, same-slide name collision, group/key conflict, or
undeclared shared
!!name on a Morph edge.
Morph without an explicit pair block retains PowerPoint's automatic matching behavior. Explicit pairing is generated-route authoring; source-preserving round-trip export keeps existing object names and transition XML.
4. Route Mapping
| Route | Default enter | Default advance | Compatibility note |
|---|---|---|---|
| Generated PPTX CLI | fade, 0.4s; no sound | click | auto-advance maps to both; an optional sidecar sound is project-local |
| Recorded narration | Preserve resolved enter | narration | none remains visually none |
Edit Native PPTX (svg_to_pptx.py --roundtrip) |
preserve source | preserve source | Compare enter, advance, and animation against the import baseline separately. -t or explicit slide transition replaces enter; omission inherits source even with importer defaults.transition: none. Effect-only edits retain advClick / advTm; explicit advance or --use-narration-timings replaces advance |
The public create_pptx_with_native_svg Python API retains its legacy 0.5s
default; the generated-deck CLI explicitly passes 0.4s.
5. OOXML Rules
Slide child order:
p:cSld
p:clrMapOvr
p:transition or transition mc:AlternateContent
p:timing
p:extLstOne slide may contain at most one logical transition carrier:
- one direct p:transition; or
- one root-level mc:AlternateContent whose Choice/Fallback branches contain p:transition.
Mutation rules:
| Operation | Direct transition | AlternateContent |
|---|---|---|
| preserve | Leave unchanged | Leave wrapper and branches unchanged |
| advance-only | Patch direct attributes | Patch Choice and Fallback identically |
| replace | Replace the direct carrier | Remove the whole wrapper, then write one carrier |
| none | Remove visual effect; retain a non-visual carrier when sound or timing is needed | Remove the whole wrapper; write a non-visual carrier when sound or timing is needed |
Sound placement: for a direct transition, append p:sndAc inside
p:transition after the visual-effect child, when present. For
mc:AlternateContent, write the same sound action into both Choice and
Fallback transition carriers so older Office consumers do not lose the cue.
The slide relationship targets one packaged WAV part; replacing or removing a
requested generated sound must not leave a dangling relationship.
MCE prefix rule: Requires and Ignorable values contain textual prefix names. Serialization must retain bindings for those exact names. Renaming an effect prefix without updating these attributes corrupts compatibility.
Package timing rule: when a route writes advTm, set ppt/presProps.xml p:presentationPr/p:showPr useTimings=1. Do not write showPr into ppt/presentation.xml.
Kiosk show: svg_to_pptx.py --kiosk writes p:showPr loop="1" with a
p:kiosk child (PowerPoint's "Browsed at a kiosk"): the show loops until
Escape and ignores click and keyboard advance, so only advTm timings and
hyperlinks move between slides. It leaves useTimings to the timing rule, so
a hyperlink-driven touch guide can be a kiosk show without timed advance.
6. Validation and Read-Back
Reject:
- unknown effect names;
- options without an explicit native effect;
- unknown option fields or values for the selected effect;
- non-finite values, including NaN and Infinity;
- duration less than or equal to zero;
- a missing/non-file transition sound, a non-WAV transition sound, or an unresolved/dangling sound relationship;
- negative advance or narration padding;
- booleans passed as numeric API values;
- multiple logical transition carriers;
- unresolved MCE Requires or Ignorable prefixes.
- invalid forced-Morph adjacency, identity uniqueness, object type, or destination effect.
Read-back must report the canonical native effect and complete effective options, while keeping the primary Choice child separate from the fallback. It must also report raw effect attributes, carrier type, duration, click mode, and automatic advance time, plus the transition sound relationship/name when present. Package validation must run after writing, not only before mutation.
Use inline smoke commands and gitignored projects/smoke* artifacts. Do not add a tests directory or test_*.py files.