aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/pptx/shapes/placeholder.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/pptx/shapes/placeholder.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pptx/shapes/placeholder.py407
1 files changed, 407 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pptx/shapes/placeholder.py b/.venv/lib/python3.12/site-packages/pptx/shapes/placeholder.py
new file mode 100644
index 00000000..c44837be
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pptx/shapes/placeholder.py
@@ -0,0 +1,407 @@
+"""Placeholder-related objects.
+
+Specific to shapes having a `p:ph` element. A placeholder has distinct behaviors
+depending on whether it appears on a slide, layout, or master. Hence there is a
+non-trivial class inheritance structure.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from pptx.enum.shapes import MSO_SHAPE_TYPE, PP_PLACEHOLDER
+from pptx.oxml.shapes.graphfrm import CT_GraphicalObjectFrame
+from pptx.oxml.shapes.picture import CT_Picture
+from pptx.shapes.autoshape import Shape
+from pptx.shapes.graphfrm import GraphicFrame
+from pptx.shapes.picture import Picture
+from pptx.util import Emu
+
+if TYPE_CHECKING:
+ from pptx.oxml.shapes.autoshape import CT_Shape
+
+
+class _InheritsDimensions(object):
+ """
+ Mixin class that provides inherited dimension behavior. Specifically,
+ left, top, width, and height report the value from the layout placeholder
+ where they would have otherwise reported |None|. This behavior is
+ distinctive to placeholders. :meth:`_base_placeholder` must be overridden
+ by all subclasses to provide lookup of the appropriate base placeholder
+ to inherit from.
+ """
+
+ @property
+ def height(self):
+ """
+ The effective height of this placeholder shape; its directly-applied
+ height if it has one, otherwise the height of its parent layout
+ placeholder.
+ """
+ return self._effective_value("height")
+
+ @height.setter
+ def height(self, value):
+ self._element.cy = value
+
+ @property
+ def left(self):
+ """
+ The effective left of this placeholder shape; its directly-applied
+ left if it has one, otherwise the left of its parent layout
+ placeholder.
+ """
+ return self._effective_value("left")
+
+ @left.setter
+ def left(self, value):
+ self._element.x = value
+
+ @property
+ def shape_type(self):
+ """
+ Member of :ref:`MsoShapeType` specifying the type of this shape.
+ Unconditionally ``MSO_SHAPE_TYPE.PLACEHOLDER`` in this case.
+ Read-only.
+ """
+ return MSO_SHAPE_TYPE.PLACEHOLDER
+
+ @property
+ def top(self):
+ """
+ The effective top of this placeholder shape; its directly-applied
+ top if it has one, otherwise the top of its parent layout
+ placeholder.
+ """
+ return self._effective_value("top")
+
+ @top.setter
+ def top(self, value):
+ self._element.y = value
+
+ @property
+ def width(self):
+ """
+ The effective width of this placeholder shape; its directly-applied
+ width if it has one, otherwise the width of its parent layout
+ placeholder.
+ """
+ return self._effective_value("width")
+
+ @width.setter
+ def width(self, value):
+ self._element.cx = value
+
+ @property
+ def _base_placeholder(self):
+ """
+ Return the layout or master placeholder shape this placeholder
+ inherits from. Not to be confused with an instance of
+ |BasePlaceholder| (necessarily).
+ """
+ raise NotImplementedError("Must be implemented by all subclasses.")
+
+ def _effective_value(self, attr_name):
+ """
+ The effective value of *attr_name* on this placeholder shape; its
+ directly-applied value if it has one, otherwise the value on the
+ layout placeholder it inherits from.
+ """
+ directly_applied_value = getattr(super(_InheritsDimensions, self), attr_name)
+ if directly_applied_value is not None:
+ return directly_applied_value
+ return self._inherited_value(attr_name)
+
+ def _inherited_value(self, attr_name):
+ """
+ Return the attribute value, e.g. 'width' of the base placeholder this
+ placeholder inherits from.
+ """
+ base_placeholder = self._base_placeholder
+ if base_placeholder is None:
+ return None
+ inherited_value = getattr(base_placeholder, attr_name)
+ return inherited_value
+
+
+class _BaseSlidePlaceholder(_InheritsDimensions, Shape):
+ """Base class for placeholders on slides.
+
+ Provides common behaviors such as inherited dimensions.
+ """
+
+ @property
+ def is_placeholder(self):
+ """
+ Boolean indicating whether this shape is a placeholder.
+ Unconditionally |True| in this case.
+ """
+ return True
+
+ @property
+ def shape_type(self):
+ """
+ Member of :ref:`MsoShapeType` specifying the type of this shape.
+ Unconditionally ``MSO_SHAPE_TYPE.PLACEHOLDER`` in this case.
+ Read-only.
+ """
+ return MSO_SHAPE_TYPE.PLACEHOLDER
+
+ @property
+ def _base_placeholder(self):
+ """
+ Return the layout placeholder this slide placeholder inherits from.
+ Not to be confused with an instance of |BasePlaceholder|
+ (necessarily).
+ """
+ layout, idx = self.part.slide_layout, self._element.ph_idx
+ return layout.placeholders.get(idx=idx)
+
+ def _replace_placeholder_with(self, element):
+ """
+ Substitute *element* for this placeholder element in the shapetree.
+ This placeholder's `._element` attribute is set to |None| and its
+ original element is free for garbage collection. Any attribute access
+ (including a method call) on this placeholder after this call raises
+ |AttributeError|.
+ """
+ element._nvXxPr.nvPr._insert_ph(self._element.ph)
+ self._element.addprevious(element)
+ self._element.getparent().remove(self._element)
+ self._element = None
+
+
+class BasePlaceholder(Shape):
+ """
+ NOTE: This class is deprecated and will be removed from a future release
+ along with the properties *idx*, *orient*, *ph_type*, and *sz*. The *idx*
+ property will be available via the .placeholder_format property. The
+ others will be accessed directly from the oxml layer as they are only
+ used for internal purposes.
+
+ Base class for placeholder subclasses that differentiate the varying
+ behaviors of placeholders on a master, layout, and slide.
+ """
+
+ @property
+ def idx(self):
+ """
+ Integer placeholder 'idx' attribute, e.g. 0
+ """
+ return self._sp.ph_idx
+
+ @property
+ def orient(self):
+ """
+ Placeholder orientation, e.g. ST_Direction.HORZ
+ """
+ return self._sp.ph_orient
+
+ @property
+ def ph_type(self):
+ """
+ Placeholder type, e.g. PP_PLACEHOLDER.CENTER_TITLE
+ """
+ return self._sp.ph_type
+
+ @property
+ def sz(self):
+ """
+ Placeholder 'sz' attribute, e.g. ST_PlaceholderSize.FULL
+ """
+ return self._sp.ph_sz
+
+
+class LayoutPlaceholder(_InheritsDimensions, Shape):
+ """Placeholder shape on a slide layout.
+
+ Provides differentiated behavior for slide layout placeholders, in particular, inheriting
+ shape properties from the master placeholder having the same type, when a matching one exists.
+ """
+
+ element: CT_Shape # pyright: ignore[reportIncompatibleMethodOverride]
+
+ @property
+ def _base_placeholder(self):
+ """
+ Return the master placeholder this layout placeholder inherits from.
+ """
+ base_ph_type = {
+ PP_PLACEHOLDER.BODY: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.CHART: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.BITMAP: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.CENTER_TITLE: PP_PLACEHOLDER.TITLE,
+ PP_PLACEHOLDER.ORG_CHART: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.DATE: PP_PLACEHOLDER.DATE,
+ PP_PLACEHOLDER.FOOTER: PP_PLACEHOLDER.FOOTER,
+ PP_PLACEHOLDER.MEDIA_CLIP: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.OBJECT: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.PICTURE: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.SLIDE_NUMBER: PP_PLACEHOLDER.SLIDE_NUMBER,
+ PP_PLACEHOLDER.SUBTITLE: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.TABLE: PP_PLACEHOLDER.BODY,
+ PP_PLACEHOLDER.TITLE: PP_PLACEHOLDER.TITLE,
+ }[self._element.ph_type]
+ slide_master = self.part.slide_master
+ return slide_master.placeholders.get(base_ph_type, None)
+
+
+class MasterPlaceholder(BasePlaceholder):
+ """Placeholder shape on a slide master."""
+
+ element: CT_Shape # pyright: ignore[reportIncompatibleMethodOverride]
+
+
+class NotesSlidePlaceholder(_InheritsDimensions, Shape):
+ """
+ Placeholder shape on a notes slide. Inherits shape properties from the
+ placeholder on the notes master that has the same type (e.g. 'body').
+ """
+
+ @property
+ def _base_placeholder(self):
+ """
+ Return the notes master placeholder this notes slide placeholder
+ inherits from, or |None| if no placeholder of the matching type is
+ present.
+ """
+ notes_master = self.part.notes_master
+ ph_type = self.element.ph_type
+ return notes_master.placeholders.get(ph_type=ph_type)
+
+
+class SlidePlaceholder(_BaseSlidePlaceholder):
+ """
+ Placeholder shape on a slide. Inherits shape properties from its
+ corresponding slide layout placeholder.
+ """
+
+
+class ChartPlaceholder(_BaseSlidePlaceholder):
+ """Placeholder shape that can only accept a chart."""
+
+ def insert_chart(self, chart_type, chart_data):
+ """
+ Return a |PlaceholderGraphicFrame| object containing a new chart of
+ *chart_type* depicting *chart_data* and having the same position and
+ size as this placeholder. *chart_type* is one of the
+ :ref:`XlChartType` enumeration values. *chart_data* is a |ChartData|
+ object populated with the categories and series values for the chart.
+ Note that the new |Chart| object is not returned directly. The chart
+ object may be accessed using the
+ :attr:`~.PlaceholderGraphicFrame.chart` property of the returned
+ |PlaceholderGraphicFrame| object.
+ """
+ rId = self.part.add_chart_part(chart_type, chart_data)
+ graphicFrame = self._new_chart_graphicFrame(
+ rId, self.left, self.top, self.width, self.height
+ )
+ self._replace_placeholder_with(graphicFrame)
+ return PlaceholderGraphicFrame(graphicFrame, self._parent)
+
+ def _new_chart_graphicFrame(self, rId, x, y, cx, cy):
+ """
+ Return a newly created `p:graphicFrame` element having the specified
+ position and size and containing the chart identified by *rId*.
+ """
+ id_, name = self.shape_id, self.name
+ return CT_GraphicalObjectFrame.new_chart_graphicFrame(id_, name, rId, x, y, cx, cy)
+
+
+class PicturePlaceholder(_BaseSlidePlaceholder):
+ """Placeholder shape that can only accept a picture."""
+
+ def insert_picture(self, image_file):
+ """Return a |PlaceholderPicture| object depicting the image in `image_file`.
+
+ `image_file` may be either a path (string) or a file-like object. The image is
+ cropped to fill the entire space of the placeholder. A |PlaceholderPicture|
+ object has all the properties and methods of a |Picture| shape except that the
+ value of its :attr:`~._BaseSlidePlaceholder.shape_type` property is
+ `MSO_SHAPE_TYPE.PLACEHOLDER` instead of `MSO_SHAPE_TYPE.PICTURE`.
+ """
+ pic = self._new_placeholder_pic(image_file)
+ self._replace_placeholder_with(pic)
+ return PlaceholderPicture(pic, self._parent)
+
+ def _new_placeholder_pic(self, image_file):
+ """
+ Return a new `p:pic` element depicting the image in *image_file*,
+ suitable for use as a placeholder. In particular this means not
+ having an `a:xfrm` element, allowing its extents to be inherited from
+ its layout placeholder.
+ """
+ rId, desc, image_size = self._get_or_add_image(image_file)
+ shape_id, name = self.shape_id, self.name
+ pic = CT_Picture.new_ph_pic(shape_id, name, desc, rId)
+ pic.crop_to_fit(image_size, (self.width, self.height))
+ return pic
+
+ def _get_or_add_image(self, image_file):
+ """
+ Return an (rId, description, image_size) 3-tuple identifying the
+ related image part containing *image_file* and describing the image.
+ """
+ image_part, rId = self.part.get_or_add_image_part(image_file)
+ desc, image_size = image_part.desc, image_part._px_size
+ return rId, desc, image_size
+
+
+class PlaceholderGraphicFrame(GraphicFrame):
+ """
+ Placeholder shape populated with a table, chart, or smart art.
+ """
+
+ @property
+ def is_placeholder(self):
+ """
+ Boolean indicating whether this shape is a placeholder.
+ Unconditionally |True| in this case.
+ """
+ return True
+
+
+class PlaceholderPicture(_InheritsDimensions, Picture):
+ """
+ Placeholder shape populated with a picture.
+ """
+
+ @property
+ def _base_placeholder(self):
+ """
+ Return the layout placeholder this picture placeholder inherits from.
+ """
+ layout, idx = self.part.slide_layout, self._element.ph_idx
+ return layout.placeholders.get(idx=idx)
+
+
+class TablePlaceholder(_BaseSlidePlaceholder):
+ """Placeholder shape that can only accept a table."""
+
+ def insert_table(self, rows, cols):
+ """Return |PlaceholderGraphicFrame| object containing a `rows` by `cols` table.
+
+ The position and width of the table are those of the placeholder and its height
+ is proportional to the number of rows. A |PlaceholderGraphicFrame| object has
+ all the properties and methods of a |GraphicFrame| shape except that the value
+ of its :attr:`~._BaseSlidePlaceholder.shape_type` property is unconditionally
+ `MSO_SHAPE_TYPE.PLACEHOLDER`. Note that the return value is not the new table
+ but rather *contains* the new table. The table can be accessed using the
+ :attr:`~.PlaceholderGraphicFrame.table` property of the returned
+ |PlaceholderGraphicFrame| object.
+ """
+ graphicFrame = self._new_placeholder_table(rows, cols)
+ self._replace_placeholder_with(graphicFrame)
+ return PlaceholderGraphicFrame(graphicFrame, self._parent)
+
+ def _new_placeholder_table(self, rows, cols):
+ """
+ Return a newly added `p:graphicFrame` element containing an empty
+ table with *rows* rows and *cols* columns, positioned at the location
+ of this placeholder and having its same width. The table's height is
+ determined by the number of rows.
+ """
+ shape_id, name, height = self.shape_id, self.name, Emu(rows * 370840)
+ return CT_GraphicalObjectFrame.new_table_graphicFrame(
+ shape_id, name, rows, cols, self.left, self.top, self.width, height
+ )