about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/pptx/slide.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/pptx/slide.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pptx/slide.py498
1 files changed, 498 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pptx/slide.py b/.venv/lib/python3.12/site-packages/pptx/slide.py
new file mode 100644
index 00000000..3b1b65d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pptx/slide.py
@@ -0,0 +1,498 @@
+"""Slide-related objects, including masters, layouts, and notes."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Iterator, cast
+
+from pptx.dml.fill import FillFormat
+from pptx.enum.shapes import PP_PLACEHOLDER
+from pptx.shapes.shapetree import (
+    LayoutPlaceholders,
+    LayoutShapes,
+    MasterPlaceholders,
+    MasterShapes,
+    NotesSlidePlaceholders,
+    NotesSlideShapes,
+    SlidePlaceholders,
+    SlideShapes,
+)
+from pptx.shared import ElementProxy, ParentedElementProxy, PartElementProxy
+from pptx.util import lazyproperty
+
+if TYPE_CHECKING:
+    from pptx.oxml.presentation import CT_SlideIdList, CT_SlideMasterIdList
+    from pptx.oxml.slide import (
+        CT_CommonSlideData,
+        CT_NotesSlide,
+        CT_Slide,
+        CT_SlideLayoutIdList,
+        CT_SlideMaster,
+    )
+    from pptx.parts.presentation import PresentationPart
+    from pptx.parts.slide import SlideLayoutPart, SlideMasterPart, SlidePart
+    from pptx.presentation import Presentation
+    from pptx.shapes.placeholder import LayoutPlaceholder, MasterPlaceholder
+    from pptx.shapes.shapetree import NotesSlidePlaceholder
+    from pptx.text.text import TextFrame
+
+
+class _BaseSlide(PartElementProxy):
+    """Base class for slide objects, including masters, layouts and notes."""
+
+    _element: CT_Slide
+
+    @lazyproperty
+    def background(self) -> _Background:
+        """|_Background| object providing slide background properties.
+
+        This property returns a |_Background| object whether or not the
+        slide, master, or layout has an explicitly defined background.
+
+        The same |_Background| object is returned on every call for the same
+        slide object.
+        """
+        return _Background(self._element.cSld)
+
+    @property
+    def name(self) -> str:
+        """String representing the internal name of this slide.
+
+        Returns an empty string (`''`) if no name is assigned. Assigning an empty string or |None|
+        to this property causes any name to be removed.
+        """
+        return self._element.cSld.name
+
+    @name.setter
+    def name(self, value: str | None):
+        new_value = "" if value is None else value
+        self._element.cSld.name = new_value
+
+
+class _BaseMaster(_BaseSlide):
+    """Base class for master objects such as |SlideMaster| and |NotesMaster|.
+
+    Provides access to placeholders and regular shapes.
+    """
+
+    @lazyproperty
+    def placeholders(self) -> MasterPlaceholders:
+        """|MasterPlaceholders| collection of placeholder shapes in this master.
+
+        Sequence sorted in `idx` order.
+        """
+        return MasterPlaceholders(self._element.spTree, self)
+
+    @lazyproperty
+    def shapes(self):
+        """
+        Instance of |MasterShapes| containing sequence of shape objects
+        appearing on this slide.
+        """
+        return MasterShapes(self._element.spTree, self)
+
+
+class NotesMaster(_BaseMaster):
+    """Proxy for the notes master XML document.
+
+    Provides access to shapes, the most commonly used of which are placeholders.
+    """
+
+
+class NotesSlide(_BaseSlide):
+    """Notes slide object.
+
+    Provides access to slide notes placeholder and other shapes on the notes handout
+    page.
+    """
+
+    element: CT_NotesSlide  # pyright: ignore[reportIncompatibleMethodOverride]
+
+    def clone_master_placeholders(self, notes_master: NotesMaster) -> None:
+        """Selectively add placeholder shape elements from `notes_master`.
+
+        Selected placeholder shape elements from `notes_master` are added to the shapes
+        collection of this notes slide. Z-order of placeholders is preserved. Certain
+        placeholders (header, date, footer) are not cloned.
+        """
+
+        def iter_cloneable_placeholders() -> Iterator[MasterPlaceholder]:
+            """Generate a reference to each cloneable placeholder in `notes_master`.
+
+            These are the placeholders that should be cloned to a notes slide when the a new notes
+            slide is created.
+            """
+            cloneable = (
+                PP_PLACEHOLDER.SLIDE_IMAGE,
+                PP_PLACEHOLDER.BODY,
+                PP_PLACEHOLDER.SLIDE_NUMBER,
+            )
+            for placeholder in notes_master.placeholders:
+                if placeholder.element.ph_type in cloneable:
+                    yield placeholder
+
+        shapes = self.shapes
+        for placeholder in iter_cloneable_placeholders():
+            shapes.clone_placeholder(cast("LayoutPlaceholder", placeholder))
+
+    @property
+    def notes_placeholder(self) -> NotesSlidePlaceholder | None:
+        """the notes placeholder on this notes slide, the shape that contains the actual notes text.
+
+        Return |None| if no notes placeholder is present; while this is probably uncommon, it can
+        happen if the notes master does not have a body placeholder, or if the notes placeholder
+        has been deleted from the notes slide.
+        """
+        for placeholder in self.placeholders:
+            if placeholder.placeholder_format.type == PP_PLACEHOLDER.BODY:
+                return placeholder
+        return None
+
+    @property
+    def notes_text_frame(self) -> TextFrame | None:
+        """The text frame of the notes placeholder on this notes slide.
+
+        |None| if there is no notes placeholder. This is a shortcut to accommodate the common case
+        of simply adding "notes" text to the notes "page".
+        """
+        notes_placeholder = self.notes_placeholder
+        if notes_placeholder is None:
+            return None
+        return notes_placeholder.text_frame
+
+    @lazyproperty
+    def placeholders(self) -> NotesSlidePlaceholders:
+        """Instance of |NotesSlidePlaceholders| for this notes-slide.
+
+        Contains the sequence of placeholder shapes in this notes slide.
+        """
+        return NotesSlidePlaceholders(self.element.spTree, self)
+
+    @lazyproperty
+    def shapes(self) -> NotesSlideShapes:
+        """Sequence of shape objects appearing on this notes slide."""
+        return NotesSlideShapes(self._element.spTree, self)
+
+
+class Slide(_BaseSlide):
+    """Slide object. Provides access to shapes and slide-level properties."""
+
+    part: SlidePart  # pyright: ignore[reportIncompatibleMethodOverride]
+
+    @property
+    def follow_master_background(self):
+        """|True| if this slide inherits the slide master background.
+
+        Assigning |False| causes background inheritance from the master to be
+        interrupted; if there is no custom background for this slide,
+        a default background is added. If a custom background already exists
+        for this slide, assigning |False| has no effect.
+
+        Assigning |True| causes any custom background for this slide to be
+        deleted and inheritance from the master restored.
+        """
+        return self._element.bg is None
+
+    @property
+    def has_notes_slide(self) -> bool:
+        """`True` if this slide has a notes slide, `False` otherwise.
+
+        A notes slide is created by :attr:`.notes_slide` when one doesn't exist; use this property
+        to test for a notes slide without the possible side effect of creating one.
+        """
+        return self.part.has_notes_slide
+
+    @property
+    def notes_slide(self) -> NotesSlide:
+        """The |NotesSlide| instance for this slide.
+
+        If the slide does not have a notes slide, one is created. The same single instance is
+        returned on each call.
+        """
+        return self.part.notes_slide
+
+    @lazyproperty
+    def placeholders(self) -> SlidePlaceholders:
+        """Sequence of placeholder shapes in this slide."""
+        return SlidePlaceholders(self._element.spTree, self)
+
+    @lazyproperty
+    def shapes(self) -> SlideShapes:
+        """Sequence of shape objects appearing on this slide."""
+        return SlideShapes(self._element.spTree, self)
+
+    @property
+    def slide_id(self) -> int:
+        """Integer value that uniquely identifies this slide within this presentation.
+
+        The slide id does not change if the position of this slide in the slide sequence is changed
+        by adding, rearranging, or deleting slides.
+        """
+        return self.part.slide_id
+
+    @property
+    def slide_layout(self) -> SlideLayout:
+        """|SlideLayout| object this slide inherits appearance from."""
+        return self.part.slide_layout
+
+
+class Slides(ParentedElementProxy):
+    """Sequence of slides belonging to an instance of |Presentation|.
+
+    Has list semantics for access to individual slides. Supports indexed access, len(), and
+    iteration.
+    """
+
+    part: PresentationPart  # pyright: ignore[reportIncompatibleMethodOverride]
+
+    def __init__(self, sldIdLst: CT_SlideIdList, prs: Presentation):
+        super(Slides, self).__init__(sldIdLst, prs)
+        self._sldIdLst = sldIdLst
+
+    def __getitem__(self, idx: int) -> Slide:
+        """Provide indexed access, (e.g. 'slides[0]')."""
+        try:
+            sldId = self._sldIdLst.sldId_lst[idx]
+        except IndexError:
+            raise IndexError("slide index out of range")
+        return self.part.related_slide(sldId.rId)
+
+    def __iter__(self) -> Iterator[Slide]:
+        """Support iteration, e.g. `for slide in slides:`."""
+        for sldId in self._sldIdLst.sldId_lst:
+            yield self.part.related_slide(sldId.rId)
+
+    def __len__(self) -> int:
+        """Support len() built-in function, e.g. `len(slides) == 4`."""
+        return len(self._sldIdLst)
+
+    def add_slide(self, slide_layout: SlideLayout) -> Slide:
+        """Return a newly added slide that inherits layout from `slide_layout`."""
+        rId, slide = self.part.add_slide(slide_layout)
+        slide.shapes.clone_layout_placeholders(slide_layout)
+        self._sldIdLst.add_sldId(rId)
+        return slide
+
+    def get(self, slide_id: int, default: Slide | None = None) -> Slide | None:
+        """Return the slide identified by int `slide_id` in this presentation.
+
+        Returns `default` if not found.
+        """
+        slide = self.part.get_slide(slide_id)
+        if slide is None:
+            return default
+        return slide
+
+    def index(self, slide: Slide) -> int:
+        """Map `slide` to its zero-based position in this slide sequence.
+
+        Raises |ValueError| on *slide* not present.
+        """
+        for idx, this_slide in enumerate(self):
+            if this_slide == slide:
+                return idx
+        raise ValueError("%s is not in slide collection" % slide)
+
+
+class SlideLayout(_BaseSlide):
+    """Slide layout object.
+
+    Provides access to placeholders, regular shapes, and slide layout-level properties.
+    """
+
+    part: SlideLayoutPart  # pyright: ignore[reportIncompatibleMethodOverride]
+
+    def iter_cloneable_placeholders(self) -> Iterator[LayoutPlaceholder]:
+        """Generate layout-placeholders on this slide-layout that should be cloned to a new slide.
+
+        Used when creating a new slide from this slide-layout.
+        """
+        latent_ph_types = (
+            PP_PLACEHOLDER.DATE,
+            PP_PLACEHOLDER.FOOTER,
+            PP_PLACEHOLDER.SLIDE_NUMBER,
+        )
+        for ph in self.placeholders:
+            if ph.element.ph_type not in latent_ph_types:
+                yield ph
+
+    @lazyproperty
+    def placeholders(self) -> LayoutPlaceholders:
+        """Sequence of placeholder shapes in this slide layout.
+
+        Placeholders appear in `idx` order.
+        """
+        return LayoutPlaceholders(self._element.spTree, self)
+
+    @lazyproperty
+    def shapes(self) -> LayoutShapes:
+        """Sequence of shapes appearing on this slide layout."""
+        return LayoutShapes(self._element.spTree, self)
+
+    @property
+    def slide_master(self) -> SlideMaster:
+        """Slide master from which this slide-layout inherits properties."""
+        return self.part.slide_master
+
+    @property
+    def used_by_slides(self):
+        """Tuple of slide objects based on this slide layout."""
+        # ---getting Slides collection requires going around the horn a bit---
+        slides = self.part.package.presentation_part.presentation.slides
+        return tuple(s for s in slides if s.slide_layout == self)
+
+
+class SlideLayouts(ParentedElementProxy):
+    """Sequence of slide layouts belonging to a slide-master.
+
+    Supports indexed access, len(), iteration, index() and remove().
+    """
+
+    part: SlideMasterPart  # pyright: ignore[reportIncompatibleMethodOverride]
+
+    def __init__(self, sldLayoutIdLst: CT_SlideLayoutIdList, parent: SlideMaster):
+        super(SlideLayouts, self).__init__(sldLayoutIdLst, parent)
+        self._sldLayoutIdLst = sldLayoutIdLst
+
+    def __getitem__(self, idx: int) -> SlideLayout:
+        """Provides indexed access, e.g. `slide_layouts[2]`."""
+        try:
+            sldLayoutId = self._sldLayoutIdLst.sldLayoutId_lst[idx]
+        except IndexError:
+            raise IndexError("slide layout index out of range")
+        return self.part.related_slide_layout(sldLayoutId.rId)
+
+    def __iter__(self) -> Iterator[SlideLayout]:
+        """Generate each |SlideLayout| in the collection, in sequence."""
+        for sldLayoutId in self._sldLayoutIdLst.sldLayoutId_lst:
+            yield self.part.related_slide_layout(sldLayoutId.rId)
+
+    def __len__(self) -> int:
+        """Support len() built-in function, e.g. `len(slides) == 4`."""
+        return len(self._sldLayoutIdLst)
+
+    def get_by_name(self, name: str, default: SlideLayout | None = None) -> SlideLayout | None:
+        """Return SlideLayout object having `name`, or `default` if not found."""
+        for slide_layout in self:
+            if slide_layout.name == name:
+                return slide_layout
+        return default
+
+    def index(self, slide_layout: SlideLayout) -> int:
+        """Return zero-based index of `slide_layout` in this collection.
+
+        Raises `ValueError` if `slide_layout` is not present in this collection.
+        """
+        for idx, this_layout in enumerate(self):
+            if slide_layout == this_layout:
+                return idx
+        raise ValueError("layout not in this SlideLayouts collection")
+
+    def remove(self, slide_layout: SlideLayout) -> None:
+        """Remove `slide_layout` from the collection.
+
+        Raises ValueError when `slide_layout` is in use; a slide layout which is the basis for one
+        or more slides cannot be removed.
+        """
+        # ---raise if layout is in use---
+        if slide_layout.used_by_slides:
+            raise ValueError("cannot remove slide-layout in use by one or more slides")
+
+        # ---target layout is identified by its index in this collection---
+        target_idx = self.index(slide_layout)
+
+        # --remove layout from p:sldLayoutIds of its master
+        # --this stops layout from showing up, but doesn't remove it from package
+        target_sldLayoutId = self._sldLayoutIdLst.sldLayoutId_lst[target_idx]
+        self._sldLayoutIdLst.remove(target_sldLayoutId)
+
+        # --drop relationship from master to layout
+        # --this removes layout from package, along with everything (only) it refers to,
+        # --including images (not used elsewhere) and hyperlinks
+        slide_layout.slide_master.part.drop_rel(target_sldLayoutId.rId)
+
+
+class SlideMaster(_BaseMaster):
+    """Slide master object.
+
+    Provides access to slide layouts. Access to placeholders, regular shapes, and slide master-level
+    properties is inherited from |_BaseMaster|.
+    """
+
+    _element: CT_SlideMaster  # pyright: ignore[reportIncompatibleVariableOverride]
+
+    @lazyproperty
+    def slide_layouts(self) -> SlideLayouts:
+        """|SlideLayouts| object providing access to this slide-master's layouts."""
+        return SlideLayouts(self._element.get_or_add_sldLayoutIdLst(), self)
+
+
+class SlideMasters(ParentedElementProxy):
+    """Sequence of |SlideMaster| objects belonging to a presentation.
+
+    Has list access semantics, supporting indexed access, len(), and iteration.
+    """
+
+    part: PresentationPart  # pyright: ignore[reportIncompatibleMethodOverride]
+
+    def __init__(self, sldMasterIdLst: CT_SlideMasterIdList, parent: Presentation):
+        super(SlideMasters, self).__init__(sldMasterIdLst, parent)
+        self._sldMasterIdLst = sldMasterIdLst
+
+    def __getitem__(self, idx: int) -> SlideMaster:
+        """Provides indexed access, e.g. `slide_masters[2]`."""
+        try:
+            sldMasterId = self._sldMasterIdLst.sldMasterId_lst[idx]
+        except IndexError:
+            raise IndexError("slide master index out of range")
+        return self.part.related_slide_master(sldMasterId.rId)
+
+    def __iter__(self):
+        """Generate each |SlideMaster| instance in the collection, in sequence."""
+        for smi in self._sldMasterIdLst.sldMasterId_lst:
+            yield self.part.related_slide_master(smi.rId)
+
+    def __len__(self):
+        """Support len() built-in function, e.g. `len(slide_masters) == 4`."""
+        return len(self._sldMasterIdLst)
+
+
+class _Background(ElementProxy):
+    """Provides access to slide background properties.
+
+    Note that the presence of this object does not by itself imply an
+    explicitly-defined background; a slide with an inherited background still
+    has a |_Background| object.
+    """
+
+    def __init__(self, cSld: CT_CommonSlideData):
+        super(_Background, self).__init__(cSld)
+        self._cSld = cSld
+
+    @lazyproperty
+    def fill(self):
+        """|FillFormat| instance for this background.
+
+        This |FillFormat| object is used to interrogate or specify the fill
+        of the slide background.
+
+        Note that accessing this property is potentially destructive. A slide
+        background can also be specified by a background style reference and
+        accessing this property will remove that reference, if present, and
+        replace it with NoFill. This is frequently the case for a slide
+        master background.
+
+        This is also the case when there is no explicitly defined background
+        (background is inherited); merely accessing this property will cause
+        the background to be set to NoFill and the inheritance link will be
+        interrupted. This is frequently the case for a slide background.
+
+        Of course, if you are accessing this property in order to set the
+        fill, then these changes are of no consequence, but the existing
+        background cannot be reliably interrogated using this property unless
+        you have already established it is an explicit fill.
+
+        If the background is already a fill, then accessing this property
+        makes no changes to the current background.
+        """
+        bgPr = self._cSld.get_or_add_bgPr()
+        return FillFormat.from_fill_parent(bgPr)