about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py355
1 files changed, 355 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py b/.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py
new file mode 100644
index 00000000..c7f8cd93
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pptx/shapes/autoshape.py
@@ -0,0 +1,355 @@
+"""Autoshape-related objects such as Shape and Adjustment."""
+
+from __future__ import annotations
+
+from numbers import Number
+from typing import TYPE_CHECKING, Iterable
+from xml.sax import saxutils
+
+from pptx.dml.fill import FillFormat
+from pptx.dml.line import LineFormat
+from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE, MSO_SHAPE_TYPE
+from pptx.shapes.base import BaseShape
+from pptx.spec import autoshape_types
+from pptx.text.text import TextFrame
+from pptx.util import lazyproperty
+
+if TYPE_CHECKING:
+    from pptx.oxml.shapes.autoshape import CT_GeomGuide, CT_PresetGeometry2D, CT_Shape
+    from pptx.spec import AdjustmentValue
+    from pptx.types import ProvidesPart
+
+
+class Adjustment:
+    """An adjustment value for an autoshape.
+
+    An adjustment value corresponds to the position of an adjustment handle on an auto shape.
+    Adjustment handles are the small yellow diamond-shaped handles that appear on certain auto
+    shapes and allow the outline of the shape to be adjusted. For example, a rounded rectangle has
+    an adjustment handle that allows the radius of its corner rounding to be adjusted.
+
+    Values are |float| and generally range from 0.0 to 1.0, although the value can be negative or
+    greater than 1.0 in certain circumstances.
+    """
+
+    def __init__(self, name: str, def_val: int, actual: int | None = None):
+        super(Adjustment, self).__init__()
+        self.name = name
+        self.def_val = def_val
+        self.actual = actual
+
+    @property
+    def effective_value(self) -> float:
+        """Read/write |float| representing normalized adjustment value for this adjustment.
+
+        Actual values are a large-ish integer expressed in shape coordinates, nominally between 0
+        and 100,000. The effective value is normalized to a corresponding value nominally between
+        0.0 and 1.0. Intuitively this represents the proportion of the width or height of the shape
+        at which the adjustment value is located from its starting point. For simple shapes such as
+        a rounded rectangle, this intuitive correspondence holds. For more complicated shapes and
+        at more extreme shape proportions (e.g. width is much greater than height), the value can
+        become negative or greater than 1.0.
+        """
+        raw_value = self.actual if self.actual is not None else self.def_val
+        return self._normalize(raw_value)
+
+    @effective_value.setter
+    def effective_value(self, value: float):
+        if not isinstance(value, Number):
+            raise ValueError(f"adjustment value must be numeric, got {repr(value)}")
+        self.actual = self._denormalize(value)
+
+    @staticmethod
+    def _denormalize(value: float) -> int:
+        """Return integer corresponding to normalized `raw_value` on unit basis of 100,000.
+
+        See Adjustment.normalize for additional details.
+        """
+        return int(value * 100000.0)
+
+    @staticmethod
+    def _normalize(raw_value: int) -> float:
+        """Return normalized value for `raw_value`.
+
+        A normalized value is a |float| between 0.0 and 1.0 for nominal raw values between 0 and
+        100,000. Raw values less than 0 and greater than 100,000 are valid and return values
+        calculated on the same unit basis of 100,000.
+        """
+        return raw_value / 100000.0
+
+    @property
+    def val(self) -> int:
+        """Denormalized effective value.
+
+        Expressed in shape coordinates, this is suitable for using in the XML.
+        """
+        return self.actual if self.actual is not None else self.def_val
+
+
+class AdjustmentCollection:
+    """Sequence of |Adjustment| instances for an auto shape.
+
+    Each represents an available adjustment for a shape of its type. Supports `len()` and indexed
+    access, e.g. `shape.adjustments[1] = 0.15`.
+    """
+
+    def __init__(self, prstGeom: CT_PresetGeometry2D):
+        super(AdjustmentCollection, self).__init__()
+        self._adjustments_ = self._initialized_adjustments(prstGeom)
+        self._prstGeom = prstGeom
+
+    def __getitem__(self, idx: int) -> float:
+        """Provides indexed access, (e.g. 'adjustments[9]')."""
+        return self._adjustments_[idx].effective_value
+
+    def __setitem__(self, idx: int, value: float):
+        """Provides item assignment via an indexed expression, e.g. `adjustments[9] = 999.9`.
+
+        Causes all adjustment values in collection to be written to the XML.
+        """
+        self._adjustments_[idx].effective_value = value
+        self._rewrite_guides()
+
+    def _initialized_adjustments(self, prstGeom: CT_PresetGeometry2D | None) -> list[Adjustment]:
+        """Return an initialized list of adjustment values based on the contents of `prstGeom`."""
+        if prstGeom is None:
+            return []
+        davs = AutoShapeType.default_adjustment_values(prstGeom.prst)
+        adjustments = [Adjustment(name, def_val) for name, def_val in davs]
+        self._update_adjustments_with_actuals(adjustments, prstGeom.gd_lst)
+        return adjustments
+
+    def _rewrite_guides(self):
+        """Write `a:gd` elements to the XML, one for each adjustment value.
+
+        Any existing guide elements are overwritten.
+        """
+        guides = [(adj.name, adj.val) for adj in self._adjustments_]
+        self._prstGeom.rewrite_guides(guides)
+
+    @staticmethod
+    def _update_adjustments_with_actuals(
+        adjustments: Iterable[Adjustment], guides: Iterable[CT_GeomGuide]
+    ):
+        """Update |Adjustment| instances in `adjustments` with actual values held in `guides`.
+
+        `guides` is a list of `a:gd` elements. Guides with a name that does not match an adjustment
+        object are skipped.
+        """
+        adjustments_by_name = dict((adj.name, adj) for adj in adjustments)
+        for gd in guides:
+            name = gd.name
+            actual = int(gd.fmla[4:])
+            try:
+                adjustment = adjustments_by_name[name]
+            except KeyError:
+                continue
+            adjustment.actual = actual
+        return
+
+    @property
+    def _adjustments(self) -> tuple[Adjustment, ...]:
+        """Sequence of |Adjustment| objects contained in collection."""
+        return tuple(self._adjustments_)
+
+    def __len__(self):
+        """Implement built-in function len()"""
+        return len(self._adjustments_)
+
+
+class AutoShapeType:
+    """Provides access to metadata for an auto-shape of type identified by `autoshape_type_id`.
+
+    Instances are cached, so no more than one instance for a particular auto shape type is in
+    memory.
+
+    Instances provide the following attributes:
+
+    .. attribute:: autoshape_type_id
+
+       Integer uniquely identifying this auto shape type. Corresponds to a
+       value in `pptx.constants.MSO` like `MSO_SHAPE.ROUNDED_RECTANGLE`.
+
+    .. attribute:: basename
+
+       Base part of shape name for auto shapes of this type, e.g. `Rounded
+       Rectangle` becomes `Rounded Rectangle 99` when the distinguishing
+       integer is added to the shape name.
+
+    .. attribute:: prst
+
+       String identifier for this auto shape type used in the `a:prstGeom`
+       element.
+
+    """
+
+    _instances: dict[MSO_AUTO_SHAPE_TYPE, AutoShapeType] = {}
+
+    def __new__(cls, autoshape_type_id: MSO_AUTO_SHAPE_TYPE) -> AutoShapeType:
+        """Only create new instance on first call for content_type.
+
+        After that, use cached instance.
+        """
+        # -- if there's not a matching instance in the cache, create one --
+        if autoshape_type_id not in cls._instances:
+            inst = super(AutoShapeType, cls).__new__(cls)
+            cls._instances[autoshape_type_id] = inst
+        # -- return the instance; note that __init__() gets called either way --
+        return cls._instances[autoshape_type_id]
+
+    def __init__(self, autoshape_type_id: MSO_AUTO_SHAPE_TYPE):
+        """Initialize attributes from constant values in `pptx.spec`."""
+        # -- skip loading if this instance is from the cache --
+        if hasattr(self, "_loaded"):
+            return
+        # -- raise on bad autoshape_type_id --
+        if autoshape_type_id not in autoshape_types:
+            raise KeyError(
+                "no autoshape type with id '%s' in pptx.spec.autoshape_types" % autoshape_type_id
+            )
+        # -- otherwise initialize new instance --
+        autoshape_type = autoshape_types[autoshape_type_id]
+        self._autoshape_type_id = autoshape_type_id
+        self._basename = autoshape_type["basename"]
+        self._loaded = True
+
+    @property
+    def autoshape_type_id(self) -> MSO_AUTO_SHAPE_TYPE:
+        """MSO_AUTO_SHAPE_TYPE enumeration member identifying this auto shape type."""
+        return self._autoshape_type_id
+
+    @property
+    def basename(self) -> str:
+        """Base of shape name for this auto shape type.
+
+        A shape name is like "Rounded Rectangle 7" and appears as an XML attribute for example at
+        `p:sp/p:nvSpPr/p:cNvPr{name}`. This basename value is the name less the distinguishing
+        integer. This value is escaped because at least one autoshape-type name includes double
+        quotes ('"No" Symbol').
+        """
+        return saxutils.escape(self._basename, {'"': """})
+
+    @classmethod
+    def default_adjustment_values(cls, prst: MSO_AUTO_SHAPE_TYPE) -> tuple[AdjustmentValue, ...]:
+        """Sequence of (name, value) pair adjustment value defaults for `prst` autoshape-type."""
+        return autoshape_types[prst]["avLst"]
+
+    @classmethod
+    def id_from_prst(cls, prst: str) -> MSO_AUTO_SHAPE_TYPE:
+        """Select auto shape type with matching `prst`.
+
+        e.g. `MSO_SHAPE.RECTANGLE` corresponding to preset geometry keyword `"rect"`.
+        """
+        return MSO_AUTO_SHAPE_TYPE.from_xml(prst)
+
+    @property
+    def prst(self):
+        """
+        Preset geometry identifier string for this auto shape. Used in the
+        `prst` attribute of `a:prstGeom` element to specify the geometry
+        to be used in rendering the shape, for example `'roundRect'`.
+        """
+        return MSO_AUTO_SHAPE_TYPE.to_xml(self._autoshape_type_id)
+
+
+class Shape(BaseShape):
+    """A shape that can appear on a slide.
+
+    Corresponds to the `p:sp` element that can appear in any of the slide-type parts
+    (slide, slideLayout, slideMaster, notesPage, notesMaster, handoutMaster).
+    """
+
+    def __init__(self, sp: CT_Shape, parent: ProvidesPart):
+        super(Shape, self).__init__(sp, parent)
+        self._sp = sp
+
+    @lazyproperty
+    def adjustments(self) -> AdjustmentCollection:
+        """Read-only reference to |AdjustmentCollection| instance for this shape."""
+        return AdjustmentCollection(self._sp.prstGeom)
+
+    @property
+    def auto_shape_type(self):
+        """Enumeration value identifying the type of this auto shape.
+
+        Like `MSO_SHAPE.ROUNDED_RECTANGLE`. Raises |ValueError| if this shape is not an auto shape.
+        """
+        if not self._sp.is_autoshape:
+            raise ValueError("shape is not an auto shape")
+        return self._sp.prst
+
+    @lazyproperty
+    def fill(self):
+        """|FillFormat| instance for this shape.
+
+        Provides access to fill properties such as fill color.
+        """
+        return FillFormat.from_fill_parent(self._sp.spPr)
+
+    def get_or_add_ln(self):
+        """Return the `a:ln` element containing the line format properties XML for this shape."""
+        return self._sp.get_or_add_ln()
+
+    @property
+    def has_text_frame(self) -> bool:
+        """|True| if this shape can contain text. Always |True| for an AutoShape."""
+        return True
+
+    @lazyproperty
+    def line(self):
+        """|LineFormat| instance for this shape.
+
+        Provides access to line properties such as line color.
+        """
+        return LineFormat(self)
+
+    @property
+    def ln(self):
+        """The `a:ln` element containing the line format properties such as line color and width.
+
+        |None| if no `a:ln` element is present.
+        """
+        return self._sp.ln
+
+    @property
+    def shape_type(self) -> MSO_SHAPE_TYPE:
+        """Unique integer identifying the type of this shape, like `MSO_SHAPE_TYPE.TEXT_BOX`."""
+        if self.is_placeholder:
+            return MSO_SHAPE_TYPE.PLACEHOLDER
+        if self._sp.has_custom_geometry:
+            return MSO_SHAPE_TYPE.FREEFORM
+        if self._sp.is_autoshape:
+            return MSO_SHAPE_TYPE.AUTO_SHAPE
+        if self._sp.is_textbox:
+            return MSO_SHAPE_TYPE.TEXT_BOX
+        raise NotImplementedError("Shape instance of unrecognized shape type")
+
+    @property
+    def text(self) -> str:
+        """Read/write. Text in shape as a single string.
+
+        The returned string will contain a newline character (`"\\n"`) separating each paragraph
+        and a vertical-tab (`"\\v"`) character for each line break (soft carriage return) in the
+        shape's text.
+
+        Assignment to `text` replaces any text previously contained in the shape, along with any
+        paragraph or font formatting applied to it. A newline character (`"\\n"`) in the assigned
+        text causes a new paragraph to be started. A vertical-tab (`"\\v"`) character in the
+        assigned text causes a line-break (soft carriage-return) to be inserted. (The vertical-tab
+        character appears in clipboard text copied from PowerPoint as its str encoding of
+        line-breaks.)
+        """
+        return self.text_frame.text
+
+    @text.setter
+    def text(self, text: str):
+        self.text_frame.text = text
+
+    @property
+    def text_frame(self):
+        """|TextFrame| instance for this shape.
+
+        Contains the text of the shape and provides access to text formatting properties.
+        """
+        txBody = self._sp.get_or_add_txBody()
+        return TextFrame(txBody, self)