Files
Templated-PPT-Master/runtime/ppt-master/scripts/template_fill_pptx/transitions.py
T

151 lines
5.0 KiB
Python

"""apply: page-to-page transitions for cloned slides.
Template Fill preserves each source transition unless the CLI or a per-slide
plan entry requests a replacement. Effects and OOXML mutation come from the
shared ``pptx_transitions`` core so every PPTX path uses the same writer.
"""
from __future__ import annotations
from typing import Any
from xml.etree import ElementTree as ET
from pptx_transitions import (
AdvanceUpdate,
EnterUpdate,
apply_slide_motion,
normalize_transition_effect_request,
validate_seconds,
)
KEEP_TRANSITION = "keep"
# Preserve source transitions unless the CLI or a per-slide plan entry selects
# a replacement. The duration is consumed only when a visual effect is written.
DEFAULT_TRANSITION = KEEP_TRANSITION
DEFAULT_TRANSITION_DURATION = 0.5
TRANSITION_OBJECT_FIELDS = frozenset(
{
"effect",
"effect_options",
"duration",
"advance_after",
}
)
_UNSET = object()
def transition_unknown_fields(raw: dict[str, Any]) -> list[str]:
"""Return unsupported fields from a per-slide transition object."""
return sorted(set(raw) - TRANSITION_OBJECT_FIELDS)
def _set_slide_transition(
slide_root: ET.Element,
*,
effect: str | None,
duration: float,
effect_options: dict[str, object] | None = None,
advance_after: float | None = None,
) -> bool:
"""Apply a legacy template-fill transition through the shared core.
``None`` and ``keep`` preserve the source transition. ``none`` removes the
visual transition while retaining an explicitly requested auto-advance.
Legacy ``advance_after`` allowed both click and timed advance, so it maps to
``both`` rather than the stricter ``after`` mode. The return value reports
whether the resulting slide contains an automatic advance.
"""
if effect is None or effect == KEEP_TRANSITION:
enter = EnterUpdate(policy="preserve")
advance = AdvanceUpdate(
mode="preserve" if advance_after is None else "both",
after=advance_after,
)
elif effect == "none":
enter = EnterUpdate(policy="none", effect=None, duration=duration)
advance = AdvanceUpdate(
mode="click" if advance_after is None else "both",
after=advance_after,
)
else:
enter = EnterUpdate(
policy="replace",
effect=effect,
duration=duration,
effect_options=effect_options,
)
advance = AdvanceUpdate(
mode="click" if advance_after is None else "both",
after=advance_after,
)
try:
return apply_slide_motion(
slide_root,
enter=enter,
advance=advance,
)
except ValueError as exc:
raise RuntimeError(str(exc)) from exc
def _resolve_slide_transition(
item: dict[str, Any],
*,
default_effect: str | None,
default_duration: float,
) -> tuple[str | None, dict[str, object], float, float | None]:
"""Pick a slide's transition from its plan entry, falling back to CLI defaults."""
raw = item.get("transition", _UNSET)
if raw is _UNSET:
if default_effect in (None, "none", KEEP_TRANSITION):
return default_effect, {}, default_duration, None
effect, effect_options = normalize_transition_effect_request(
default_effect,
allow_none=False,
)
return effect, effect_options, default_duration, None
if isinstance(raw, dict):
unknown = transition_unknown_fields(raw)
if unknown:
raise RuntimeError(
"Transition has unknown field(s): " + ", ".join(unknown)
)
effect = raw.get("effect", default_effect)
raw_options = raw.get("effect_options")
if raw_options is not None and "effect" not in raw:
raise RuntimeError(
"Transition effect_options requires an explicit effect"
)
duration = raw.get("duration", default_duration)
advance_after = raw.get("advance_after")
else:
effect = None if raw is None else str(raw)
raw_options = None
duration = default_duration
advance_after = None
effect_options: dict[str, object] = {}
if effect is not None and effect not in ("none", KEEP_TRANSITION):
try:
effect, effect_options = normalize_transition_effect_request(
effect,
raw_options,
allow_none=False,
)
except ValueError as exc:
raise RuntimeError(str(exc)) from exc
elif raw_options not in (None, {}):
raise RuntimeError(
"Transition effect_options requires an explicit native effect"
)
try:
resolved_duration = validate_seconds(
duration,
"transition duration",
allow_zero=False,
)
except ValueError as exc:
raise RuntimeError(str(exc)) from exc
return effect, effect_options, resolved_duration, advance_after