diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/pptx/oxml/slide.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/pptx/oxml/slide.py | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pptx/oxml/slide.py b/.venv/lib/python3.12/site-packages/pptx/oxml/slide.py new file mode 100644 index 00000000..37a9780f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pptx/oxml/slide.py @@ -0,0 +1,347 @@ +"""Slide-related custom element classes, including those for masters.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, cast + +from pptx.oxml import parse_from_template, parse_xml +from pptx.oxml.dml.fill import CT_GradientFillProperties +from pptx.oxml.ns import nsdecls +from pptx.oxml.simpletypes import XsdString +from pptx.oxml.xmlchemy import ( + BaseOxmlElement, + Choice, + OneAndOnlyOne, + OptionalAttribute, + RequiredAttribute, + ZeroOrMore, + ZeroOrOne, + ZeroOrOneChoice, +) + +if TYPE_CHECKING: + from pptx.oxml.shapes.groupshape import CT_GroupShape + + +class _BaseSlideElement(BaseOxmlElement): + """Base class for the six slide types, providing common methods.""" + + cSld: CT_CommonSlideData + + @property + def spTree(self) -> CT_GroupShape: + """Return required `p:cSld/p:spTree` grandchild.""" + return self.cSld.spTree + + +class CT_Background(BaseOxmlElement): + """`p:bg` element.""" + + _insert_bgPr: Callable[[CT_BackgroundProperties], None] + + # ---these two are actually a choice, not a sequence, but simpler for + # ---present purposes this way. + _tag_seq = ("p:bgPr", "p:bgRef") + bgPr: CT_BackgroundProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] + "p:bgPr", successors=() + ) + bgRef = ZeroOrOne("p:bgRef", successors=()) + del _tag_seq + + def add_noFill_bgPr(self): + """Return a new `p:bgPr` element with noFill properties.""" + xml = "<p:bgPr %s>\n" " <a:noFill/>\n" " <a:effectLst/>\n" "</p:bgPr>" % nsdecls("a", "p") + bgPr = cast(CT_BackgroundProperties, parse_xml(xml)) + self._insert_bgPr(bgPr) + return bgPr + + +class CT_BackgroundProperties(BaseOxmlElement): + """`p:bgPr` element.""" + + _tag_seq = ( + "a:noFill", + "a:solidFill", + "a:gradFill", + "a:blipFill", + "a:pattFill", + "a:grpFill", + "a:effectLst", + "a:effectDag", + "a:extLst", + ) + eg_fillProperties = ZeroOrOneChoice( + ( + Choice("a:noFill"), + Choice("a:solidFill"), + Choice("a:gradFill"), + Choice("a:blipFill"), + Choice("a:pattFill"), + Choice("a:grpFill"), + ), + successors=_tag_seq[6:], + ) + del _tag_seq + + def _new_gradFill(self): + """Override default to add default gradient subtree.""" + return CT_GradientFillProperties.new_gradFill() + + +class CT_CommonSlideData(BaseOxmlElement): + """`p:cSld` element.""" + + _remove_bg: Callable[[], None] + get_or_add_bg: Callable[[], CT_Background] + + _tag_seq = ("p:bg", "p:spTree", "p:custDataLst", "p:controls", "p:extLst") + bg: CT_Background | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] + "p:bg", successors=_tag_seq[1:] + ) + spTree: CT_GroupShape = OneAndOnlyOne("p:spTree") # pyright: ignore[reportAssignmentType] + del _tag_seq + name: str = OptionalAttribute( # pyright: ignore[reportAssignmentType] + "name", XsdString, default="" + ) + + def get_or_add_bgPr(self) -> CT_BackgroundProperties: + """Return `p:bg/p:bgPr` grandchild. + + If no such grandchild is present, any existing `p:bg` child is first removed and a new + default `p:bg` with noFill settings is added. + """ + bg = self.bg + if bg is None or bg.bgPr is None: + bg = self._change_to_noFill_bg() + return cast(CT_BackgroundProperties, bg.bgPr) + + def _change_to_noFill_bg(self) -> CT_Background: + """Establish a `p:bg` child with no-fill settings. + + Any existing `p:bg` child is first removed. + """ + self._remove_bg() + bg = self.get_or_add_bg() + bg.add_noFill_bgPr() + return bg + + +class CT_NotesMaster(_BaseSlideElement): + """`p:notesMaster` element, root of a notes master part.""" + + _tag_seq = ("p:cSld", "p:clrMap", "p:hf", "p:notesStyle", "p:extLst") + cSld: CT_CommonSlideData = OneAndOnlyOne("p:cSld") # pyright: ignore[reportAssignmentType] + del _tag_seq + + @classmethod + def new_default(cls) -> CT_NotesMaster: + """Return a new `p:notesMaster` element based on the built-in default template.""" + return cast(CT_NotesMaster, parse_from_template("notesMaster")) + + +class CT_NotesSlide(_BaseSlideElement): + """`p:notes` element, root of a notes slide part.""" + + _tag_seq = ("p:cSld", "p:clrMapOvr", "p:extLst") + cSld: CT_CommonSlideData = OneAndOnlyOne("p:cSld") # pyright: ignore[reportAssignmentType] + del _tag_seq + + @classmethod + def new(cls) -> CT_NotesSlide: + """Return a new ``<p:notes>`` element based on the default template. + + Note that the template does not include placeholders, which must be subsequently cloned + from the notes master. + """ + return cast(CT_NotesSlide, parse_from_template("notes")) + + +class CT_Slide(_BaseSlideElement): + """`p:sld` element, root element of a slide part (XML document).""" + + _tag_seq = ("p:cSld", "p:clrMapOvr", "p:transition", "p:timing", "p:extLst") + cSld: CT_CommonSlideData = OneAndOnlyOne("p:cSld") # pyright: ignore[reportAssignmentType] + clrMapOvr = ZeroOrOne("p:clrMapOvr", successors=_tag_seq[2:]) + timing = ZeroOrOne("p:timing", successors=_tag_seq[4:]) + del _tag_seq + + @classmethod + def new(cls) -> CT_Slide: + """Return new `p:sld` element configured as base slide shape.""" + return cast(CT_Slide, parse_xml(cls._sld_xml())) + + @property + def bg(self): + """Return `p:bg` grandchild or None if not present.""" + return self.cSld.bg + + def get_or_add_childTnLst(self): + """Return parent element for a new `p:video` child element. + + The `p:video` element causes play controls to appear under a video + shape (pic shape containing video). There can be more than one video + shape on a slide, which causes the precondition to vary. It needs to + handle the case when there is no `p:sld/p:timing` element and when + that element already exists. If the case isn't simple, it just nukes + what's there and adds a fresh one. This could theoretically remove + desired existing timing information, but there isn't any evidence + available to me one way or the other, so I've taken the simple + approach. + """ + childTnLst = self._childTnLst + if childTnLst is None: + childTnLst = self._add_childTnLst() + return childTnLst + + def _add_childTnLst(self): + """Add `./p:timing/p:tnLst/p:par/p:cTn/p:childTnLst` descendant. + + Any existing `p:timing` child element is ruthlessly removed and + replaced. + """ + self.remove(self.get_or_add_timing()) + timing = parse_xml(self._childTnLst_timing_xml()) + self._insert_timing(timing) + return timing.xpath("./p:tnLst/p:par/p:cTn/p:childTnLst")[0] + + @property + def _childTnLst(self): + """Return `./p:timing/p:tnLst/p:par/p:cTn/p:childTnLst` descendant. + + Return None if that element is not present. + """ + childTnLsts = self.xpath("./p:timing/p:tnLst/p:par/p:cTn/p:childTnLst") + if not childTnLsts: + return None + return childTnLsts[0] + + @staticmethod + def _childTnLst_timing_xml(): + return ( + "<p:timing %s>\n" + " <p:tnLst>\n" + " <p:par>\n" + ' <p:cTn id="1" dur="indefinite" restart="never" nodeType="' + 'tmRoot">\n' + " <p:childTnLst/>\n" + " </p:cTn>\n" + " </p:par>\n" + " </p:tnLst>\n" + "</p:timing>" % nsdecls("p") + ) + + @staticmethod + def _sld_xml(): + return ( + "<p:sld %s>\n" + " <p:cSld>\n" + " <p:spTree>\n" + " <p:nvGrpSpPr>\n" + ' <p:cNvPr id="1" name=""/>\n' + " <p:cNvGrpSpPr/>\n" + " <p:nvPr/>\n" + " </p:nvGrpSpPr>\n" + " <p:grpSpPr/>\n" + " </p:spTree>\n" + " </p:cSld>\n" + " <p:clrMapOvr>\n" + " <a:masterClrMapping/>\n" + " </p:clrMapOvr>\n" + "</p:sld>" % nsdecls("a", "p", "r") + ) + + +class CT_SlideLayout(_BaseSlideElement): + """`p:sldLayout` element, root of a slide layout part.""" + + _tag_seq = ("p:cSld", "p:clrMapOvr", "p:transition", "p:timing", "p:hf", "p:extLst") + cSld: CT_CommonSlideData = OneAndOnlyOne("p:cSld") # pyright: ignore[reportAssignmentType] + del _tag_seq + + +class CT_SlideLayoutIdList(BaseOxmlElement): + """`p:sldLayoutIdLst` element, child of `p:sldMaster`. + + Contains references to the slide layouts that inherit from the slide master. + """ + + sldLayoutId_lst: list[CT_SlideLayoutIdListEntry] + + sldLayoutId = ZeroOrMore("p:sldLayoutId") + + +class CT_SlideLayoutIdListEntry(BaseOxmlElement): + """`p:sldLayoutId` element, child of `p:sldLayoutIdLst`. + + Contains a reference to a slide layout. + """ + + rId: str = RequiredAttribute("r:id", XsdString) # pyright: ignore[reportAssignmentType] + + +class CT_SlideMaster(_BaseSlideElement): + """`p:sldMaster` element, root of a slide master part.""" + + get_or_add_sldLayoutIdLst: Callable[[], CT_SlideLayoutIdList] + + _tag_seq = ( + "p:cSld", + "p:clrMap", + "p:sldLayoutIdLst", + "p:transition", + "p:timing", + "p:hf", + "p:txStyles", + "p:extLst", + ) + cSld: CT_CommonSlideData = OneAndOnlyOne("p:cSld") # pyright: ignore[reportAssignmentType] + sldLayoutIdLst: CT_SlideLayoutIdList = ZeroOrOne( # pyright: ignore[reportAssignmentType] + "p:sldLayoutIdLst", successors=_tag_seq[3:] + ) + del _tag_seq + + +class CT_SlideTiming(BaseOxmlElement): + """`p:timing` element, specifying animations and timed behaviors.""" + + _tag_seq = ("p:tnLst", "p:bldLst", "p:extLst") + tnLst = ZeroOrOne("p:tnLst", successors=_tag_seq[1:]) + del _tag_seq + + +class CT_TimeNodeList(BaseOxmlElement): + """`p:tnLst` or `p:childTnList` element.""" + + def add_video(self, shape_id): + """Add a new `p:video` child element for movie having *shape_id*.""" + video_xml = ( + "<p:video %s>\n" + ' <p:cMediaNode vol="80000">\n' + ' <p:cTn id="%d" fill="hold" display="0">\n' + " <p:stCondLst>\n" + ' <p:cond delay="indefinite"/>\n' + " </p:stCondLst>\n" + " </p:cTn>\n" + " <p:tgtEl>\n" + ' <p:spTgt spid="%d"/>\n' + " </p:tgtEl>\n" + " </p:cMediaNode>\n" + "</p:video>\n" % (nsdecls("p"), self._next_cTn_id, shape_id) + ) + video = parse_xml(video_xml) + self.append(video) + + @property + def _next_cTn_id(self): + """Return the next available unique ID (int) for p:cTn element.""" + cTn_id_strs = self.xpath("/p:sld/p:timing//p:cTn/@id") + ids = [int(id_str) for id_str in cTn_id_strs] + return max(ids) + 1 + + +class CT_TLMediaNodeVideo(BaseOxmlElement): + """`p:video` element, specifying video media details.""" + + _tag_seq = ("p:cMediaNode",) + cMediaNode = OneAndOnlyOne("p:cMediaNode") + del _tag_seq |