From 778a2323a7252193e5446c6b698d8f4cfba4a85d Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Thu, 5 Dec 2024 19:47:35 +0000 Subject: [PATCH 1/2] Format fixes by ruff --- Lib/fontParts/base/component.py | 27 ++++++++++++++------------- Lib/fontParts/base/font.py | 4 +++- Lib/fontParts/base/glyph.py | 4 +++- Lib/fontParts/base/layer.py | 4 +++- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Lib/fontParts/base/component.py b/Lib/fontParts/base/component.py index 763b0604..14f584bf 100644 --- a/Lib/fontParts/base/component.py +++ b/Lib/fontParts/base/component.py @@ -49,6 +49,7 @@ class BaseComponent( inserted as part of an outline. """ + copyAttributes: Tuple[str, str] = ("baseGlyph", "transformation") def _reprContents(self) -> List[str]: @@ -227,7 +228,7 @@ def _set_baseGlyph(self, value: str) -> None: :return: A :ref:`type-transformation` value representing the transformation matrix of the component. - """ + """, ) def _get_base_transformation(self) -> SextupleType[float]: @@ -288,7 +289,7 @@ def _set_transformation(self, value: SextupleCollectionType[IntFloatType]) -> No :return: A :ref:`type-coordinate.` representing the offset of the component. - """ + """, ) def _get_base_offset(self) -> PairType[IntFloatType]: @@ -348,7 +349,7 @@ def _set_offset(self, value: PairCollectionType[IntFloatType]) -> None: :return: A :class:`tuple` of two :class:`float` items representing the ``(x, y)`` scale of the component. - """ + """, ) def _get_base_scale(self) -> PairType[float]: @@ -416,7 +417,7 @@ def _set_scale(self, value: PairCollectionType[IntFloatType]) -> None: :raise FontPartsError: If attempting to set the index while the component does not belong to a glyph. - """ + """, ) def _get_base_index(self) -> Optional[int]: @@ -739,18 +740,18 @@ def _get_base_bounds(self) -> QuadrupleType[float]: def _get_bounds(self) -> QuadrupleType[float]: """Get the bounds of the component. - This is the environment implementation of the :attr:`BaseComponent.bounds` - property getter. + This is the environment implementation of the :attr:`BaseComponent.bounds` + property getter. - :return: A :class:`tuple` of four :class:`int` or :class:`float` values - in the form ``(x minimum, y minimum, x maximum, y maximum)`` - representing the bounds of the component, or :obj:`None` if the - component is empty. The value will be normalized - with :func:`normalizers.normalizeBoundingBox`. + :return: A :class:`tuple` of four :class:`int` or :class:`float` values + in the form ``(x minimum, y minimum, x maximum, y maximum)`` + representing the bounds of the component, or :obj:`None` if the + component is empty. The value will be normalized + with :func:`normalizers.normalizeBoundingBox`. - .. note:: + .. note:: - Subclasses may override this method. + Subclasses may override this method. """ pen = BoundsPen(self.layer) diff --git a/Lib/fontParts/base/font.py b/Lib/fontParts/base/font.py index 0899ef0a..75002d68 100644 --- a/Lib/fontParts/base/font.py +++ b/Lib/fontParts/base/font.py @@ -2058,7 +2058,9 @@ def _interpolate( compatibilityReporterClass = FontCompatibilityReporter - def isCompatible(self, other: BaseFont, cls=None) -> Tuple[bool, FontCompatibilityReporter]: + def isCompatible( + self, other: BaseFont, cls=None + ) -> Tuple[bool, FontCompatibilityReporter]: """Evaluate interpolation compatibility with another font. This method will return a :class:`bool` indicating if the font is diff --git a/Lib/fontParts/base/glyph.py b/Lib/fontParts/base/glyph.py index f5530694..dc6c9a81 100644 --- a/Lib/fontParts/base/glyph.py +++ b/Lib/fontParts/base/glyph.py @@ -2795,7 +2795,9 @@ def _checkPairs( reporter.warning = True reporterObject.append(compatibility) - def isCompatible(self, other: BaseGlyph, cls=None) -> Tuple[bool, GlyphCompatibilityReporter]: + def isCompatible( + self, other: BaseGlyph, cls=None + ) -> Tuple[bool, GlyphCompatibilityReporter]: """Evaluate interpolation compatibility with another glyph. :param other: The other :class:`BaseGlyph` instance to check diff --git a/Lib/fontParts/base/layer.py b/Lib/fontParts/base/layer.py index a4059e27..081e81eb 100644 --- a/Lib/fontParts/base/layer.py +++ b/Lib/fontParts/base/layer.py @@ -1106,7 +1106,9 @@ def _interpolate( compatibilityReporterClass = LayerCompatibilityReporter - def isCompatible(self, other: BaseLayer, cls=None) -> Tuple[bool, LayerCompatibilityReporter]: + def isCompatible( + self, other: BaseLayer, cls=None + ) -> Tuple[bool, LayerCompatibilityReporter]: """Evaluate interpolation compatibility with another layer. :param other: The other :class:`BaseLayer` instance to check From d008fa26c24e85a532192336b9fb618593c90000 Mon Sep 17 00:00:00 2001 From: Knut Nergaard Date: Thu, 5 Dec 2024 21:24:04 +0100 Subject: [PATCH 2/2] Annotate and document `component.py`. (#804) * Add type annotation and correct errors. * Delete unused imports. * Add/improve documentation and make other slight changes. --------- Co-authored-by: knutnergaard --- Lib/fontParts/base/component.py | 590 +++++++++++++++++++++++++------- 1 file changed, 468 insertions(+), 122 deletions(-) diff --git a/Lib/fontParts/base/component.py b/Lib/fontParts/base/component.py index 755e28ae..c65d4cec 100644 --- a/Lib/fontParts/base/component.py +++ b/Lib/fontParts/base/component.py @@ -1,11 +1,15 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Any, List, Optional, Tuple + from fontTools.misc import transform +from fontTools.pens.pointInsidePen import PointInsidePen +from fontTools.pens.boundsPen import BoundsPen from fontParts.base import normalizers from fontParts.base.errors import FontPartsError from fontParts.base.base import ( BaseObject, TransformationMixin, InterpolationMixin, - PointPositionMixin, SelectionMixin, IdentifierMixin, dynamicProperty, @@ -13,6 +17,21 @@ ) from fontParts.base.compatibility import ComponentCompatibilityReporter from fontParts.base.deprecated import DeprecatedComponent, RemovedComponent +from fontParts.base.annotations import ( + PairType, + PairCollectionType, + QuadrupleType, + SextupleType, + SextupleCollectionType, + IntFloatType, + PenType, + PointPenType, +) + +if TYPE_CHECKING: + from fontParts.base.glyph import BaseGlyph + from fontParts.base.layer import BaseLayer + from fontParts.base.font import BaseFont class BaseComponent( @@ -24,9 +43,16 @@ class BaseComponent( DeprecatedComponent, RemovedComponent, ): - copyAttributes = ("baseGlyph", "transformation") + """Represent the basis for a component object. + + This object provides a reference to another glyph, allowing it to be + inserted as part of an outline. + + """ + + copyAttributes: Tuple[str, str] = ("baseGlyph", "transformation") - def _reprContents(self): + def _reprContents(self) -> List[str]: contents = [ f"baseGlyph='{self.baseGlyph}'", f"offset='({self.offset[0]}, {self.offset[1]})'", @@ -44,14 +70,30 @@ def _reprContents(self): _glyph = None - glyph = dynamicProperty("glyph", "The component's parent glyph.") + glyph: dynamicProperty = dynamicProperty( + "glyph", + """Get or set the component's parent glyph object. + + The value must be a :class:`BaseGlyph` instance or :obj:`None`. + + :return: The :class:`BaseGlyph` instance containing the component + or :obj:`None`. + :raises AssertionError: If attempting to set the glyph when it + has already been set. + + Example:: + + >>> glyph = component.glyph + + """, + ) - def _get_glyph(self): + def _get_glyph(self) -> Optional[BaseGlyph]: if self._glyph is None: return None return self._glyph() - def _set_glyph(self, glyph): + def _set_glyph(self, glyph: Optional[BaseGlyph]) -> None: if self._glyph is not None: raise AssertionError("glyph for component already set") if glyph is not None: @@ -60,18 +102,46 @@ def _set_glyph(self, glyph): # Layer - layer = dynamicProperty("layer", "The component's parent layer.") + layer: dynamicProperty = dynamicProperty( + "layer", + """Get the component's parent layer object. + + This property is read-only. + + :return: The :class:`BaseLayer` instance containing the component + or :obj:`None`. - def _get_layer(self): + Example:: + + >>> layer = component.layer + + """, + ) + + def _get_layer(self) -> Optional[BaseLayer]: if self._glyph is None: return None return self.glyph.layer # Font - font = dynamicProperty("font", "The component's parent font.") + font: dynamicProperty = dynamicProperty( + "font", + """Get the component's parent font object. + + This property is read-only. + + :return: The :class:`BaseFont` instance containing the component + or :obj:`None`. + + Example:: + + >>> font = component.font - def _get_font(self): + """, + ) + + def _get_font(self) -> Optional[BaseFont]: if self._glyph is None: return None return self.glyph.font @@ -82,86 +152,186 @@ def _get_font(self): # baseGlyph - baseGlyph = dynamicProperty( - "base_baseGlyph", "The name of the glyph the component references." + baseGlyph: dynamicProperty = dynamicProperty( + "base_baseGlyph", + """Get or set the name of the glyph referenced by the component. + + The value must be a :class:`str`. + + :return: A :class:`str` representing the name of the base glyph, + or :obj:`None` if the component does not belong to a layer. + :raise ValueError: If value is None when the component is part of a layer. + + """, ) - def _get_base_baseGlyph(self): + def _get_base_baseGlyph(self) -> Optional[str]: value = self._get_baseGlyph() # if the component does not belong to a layer, # it is allowed to have None as its baseGlyph if value is None and self.layer is None: - pass - else: - value = normalizers.normalizeGlyphName(value) - return value + return value + if value is None: + raise ValueError(f"Value cannot be None when layer is '{self.layer}'.") + return normalizers.normalizeGlyphName(value) - def _set_base_baseGlyph(self, value): + def _set_base_baseGlyph(self, value: str) -> None: value = normalizers.normalizeGlyphName(value) self._set_baseGlyph(value) - def _get_baseGlyph(self): - """ - Subclasses must override this method. + def _get_baseGlyph(self) -> Optional[str]: + """Get the name of the glyph referenced by the native component. + + This is the environment implementation of the :attr:`BaseComponent.baseGlyph` + property getter. + + :return: A :class:`str` representing the name of the base glyph, + or :obj:`None` if the component does not belong to a layer. The value + will be normalized with :func:`normalizers.normalizeGlyphName`. + :raises NotImplementedError: If the method has not been overridden by a + subclass. + + .. important:: + + Subclasses must override this method. + """ self.raiseNotImplementedError() - def _set_baseGlyph(self, value): - """ - Subclasses must override this method. + def _set_baseGlyph(self, value: str) -> None: + """Set the name of the glyph referenced by the native component. + + This is the environment implementation of the :attr:`BaseComponent.baseGlyph` + property setter. + + :param value: The name of the glyph to set as a :class:`str`. The value + will have been normalized + with :func:`normalizers.normalizeGlyphName`. + :raises NotImplementedError: If the method has not been overridden by a + subclass. + + .. important:: + + Subclasses must override this method. + """ self.raiseNotImplementedError() # transformation - transformation = dynamicProperty( - "base_transformation", "The component's transformation matrix." + transformation: dynamicProperty = dynamicProperty( + "base_transformation", + """Get or set the component's transformation matrix. + + The value must be a :ref:`type-transformation`. + + :return: A :ref:`type-transformation` value representing the + transformation matrix of the component. + + """, ) - def _get_base_transformation(self): + def _get_base_transformation(self) -> SextupleType[float]: value = self._get_transformation() value = normalizers.normalizeTransformationMatrix(value) return value - def _set_base_transformation(self, value): + def _set_base_transformation( + self, value: SextupleCollectionType[IntFloatType] + ) -> None: value = normalizers.normalizeTransformationMatrix(value) self._set_transformation(value) - def _get_transformation(self): - """ - Subclasses must override this method. + def _get_transformation(self) -> SextupleType[float]: + """Get the native component's transformation matrix. + + This is the environment implementation of the + :attr:`BaseComponent.transformation` property getter. + + :return: A :ref:`type-transformation` value representing the + transformation matrix of the component. The value will be + normalized with :func:`normalizers.normalizeTransformationMatrix`. + :raises NotImplementedError: If the method has not been overridden by a + subclass. + + .. important:: + + Subclasses must override this method. + """ self.raiseNotImplementedError() - def _set_transformation(self, value): - """ - Subclasses must override this method. + def _set_transformation(self, value: SextupleCollectionType[IntFloatType]) -> None: + """Set the native component's transformation matrix. + + This is the environment implementation of the + :attr:`BaseComponent.transformation` property setter. + + :param value: The :ref:`type-transformation` to set. The value will have + been normalized with :func:`normalizers.normalizeTransformationMatrix`. + :raises NotImplementedError: If the method has not been overridden by a + subclass. + + .. important:: + + Subclasses must override this method. + """ self.raiseNotImplementedError() # offset - offset = dynamicProperty("base_offset", "The component's offset.") + offset: dynamicProperty = dynamicProperty( + "base_offset", + """Get or set the component's offset. - def _get_base_offset(self): + The value must be a :ref:`type-coordinate.` + + :return: A :ref:`type-coordinate.` representing the offset of the component. + + """, + ) + + def _get_base_offset(self) -> PairType[IntFloatType]: value = self._get_offset() value = normalizers.normalizeTransformationOffset(value) return value - def _set_base_offset(self, value): + def _set_base_offset(self, value: PairCollectionType[IntFloatType]) -> None: value = normalizers.normalizeTransformationOffset(value) self._set_offset(value) - def _get_offset(self): - """ - Subclasses may override this method. + def _get_offset(self) -> PairType[IntFloatType]: + """Get the native component's offset. + + This is the environment implementation of the :attr:`BaseComponent.offset` + property getter. + + :return: A :ref:`type-coordinate.` representing the offset of the component. + The value will be normalized + with :func:`normalizers.normalizeTransformationOffset`. + + .. note:: + + Subclasses may override this method. + """ sx, sxy, syx, sy, ox, oy = self.transformation return ox, oy - def _set_offset(self, value): - """ - Subclasses may override this method. + def _set_offset(self, value: PairCollectionType[IntFloatType]) -> None: + """Set the native component's offset. + + This is the environment implementation of the :attr:`BaseComponent.offset` + property setter. + + :param value: The offset to set as a :ref:`type-coordinate.`. The value will + have been normalized with :func:`normalizers.normalizeTransformationOffset`. + + .. note:: + + Subclasses may override this method. + """ sx, sxy, syx, sy, ox, oy = self.transformation ox, oy = value @@ -169,27 +339,61 @@ def _set_offset(self, value): # scale - scale = dynamicProperty("base_scale", "The component's scale.") + scale: dynamicProperty = dynamicProperty( + "base_scale", + """Get or set the component's scale. + + The value must be a :class:`list` or :class:`tuple` of two :class:`int` + or :class:`float` items representing the ``(x, y)`` scale of the component. - def _get_base_scale(self): + :return: A :class:`tuple` of two :class:`float` items representing the + ``(x, y)`` scale of the component. + + """, + ) + + def _get_base_scale(self) -> PairType[float]: value = self._get_scale() value = normalizers.normalizeComponentScale(value) return value - def _set_base_scale(self, value): + def _set_base_scale(self, value: PairCollectionType[IntFloatType]) -> None: value = normalizers.normalizeComponentScale(value) self._set_scale(value) - def _get_scale(self): - """ - Subclasses may override this method. + def _get_scale(self) -> PairType[float]: + """Get the native component's scale. + + This is the environment implementation of the :attr:`BaseComponent.scale` + property getter. + + :return: A :class:`tuple` of two :class:`float` items representing the + ``(x, y)`` scale of the component. The value will have been normalized + with :func:`normalizers.normalizeComponentScale`. + + .. note:: + + Subclasses may override this method. + """ sx, sxy, syx, sy, ox, oy = self.transformation return sx, sy - def _set_scale(self, value): - """ - Subclasses may override this method. + def _set_scale(self, value: PairCollectionType[IntFloatType]) -> None: + """Set the native component's scale. + + This is the environment implementation of the :attr:`BaseComponent.scale` + property setter. + + :param value: The scale to set as a :class:`list` or :class:`tuple` + of :class:`int` or :class:`float` items representing the ``(x, y)`` + scale of the component. The value will have been normalized + with :func:`normalizers.normalizeComponentScale`. + + .. note:: + + Subclasses may override this method. + """ sx, sxy, syx, sy, ox, oy = self.transformation sx, sy = value @@ -201,15 +405,22 @@ def _set_scale(self, value): # index - index = dynamicProperty( + index: dynamicProperty = dynamicProperty( "base_index", - ( - "The index of the component within the " - "ordered list of the parent glyph's components." - ), + """Get or set the index of the contour. + + The value must be an :class:`int` + + :return: An :class:`int` representing the index of the component within + the ordered list of parent glyph's components, or :obj:`None` if the + component does not belong to a glyph. + :raise FontPartsError: If attempting to set the index while the + component does not belong to a glyph. + + """, ) - def _get_base_index(self): + def _get_base_index(self) -> Optional[int]: glyph = self.glyph if glyph is None: return None @@ -217,11 +428,13 @@ def _get_base_index(self): value = normalizers.normalizeIndex(value) return value - def _set_base_index(self, value): + def _set_base_index(self, value: int) -> None: glyph = self.glyph if glyph is None: raise FontPartsError("The component does not belong to a glyph.") value = normalizers.normalizeIndex(value) + if value is None: + return componentCount = len(glyph.components) if value < 0: value = -(value % componentCount) @@ -229,16 +442,40 @@ def _set_base_index(self, value): value = componentCount self._set_index(value) - def _get_index(self): - """ - Subclasses may override this method. + def _get_index(self) -> Optional[int]: + """Get the index of the native contour. + + This is the environment implementation of the :attr:`BaseComponent.index` + property getter. + + :return: An :class:`int` representing the index of the component within + the ordered list of parent glyph's components, or :obj:`None` if the + component does not belong to a glyph. The value will be normalized + with :func:`normalizers.normalizeIndex`. + + .. note:: + + Subclasses may override this method. + """ glyph = self.glyph return glyph.components.index(self) - def _set_index(self, value): - """ - Subclasses must override this method. + def _set_index(self, value: int) -> None: + """Set the index of the native contour. + + This is the environment implementation of the :attr:`BaseComponent.index` + property setter. + + :param value: The index to set as an :class:`int`. The value will have been + normalized with :func:`normalizers.normalizeIndex`. + :raises NotImplementedError: If the method has not been overridden by a + subclass. + + .. important:: + + Subclasses must override this method. + """ self.raiseNotImplementedError() @@ -246,30 +483,56 @@ def _set_index(self, value): # Pens # ---- - def draw(self, pen): - """ - Draw the component with the given Pen. + def draw(self, pen: PenType) -> None: + """Draw the component with the given pen. + + :param pen: The :class:`fontTools.pens.basePen.AbstractPen` with which + to draw the componnet. + """ self._draw(pen) - def _draw(self, pen, **kwargs): - """ - Subclasses may override this method. + def _draw(self, pen: PenType, **kwargs: Any) -> None: + r"""Draw the native component with the given pen. + + This is the environment implementation of :meth:`BaseComponent.draw`. + + :param pen: The :class:`fontTools.pens.basePen.AbstractPen` with which + to draw the componnet. + :param \**kwargs: Additional keyword arguments. + + .. note:: + + Subclasses may override this method. + """ from fontTools.ufoLib.pointPen import PointToSegmentPen adapter = PointToSegmentPen(pen) self.drawPoints(adapter) - def drawPoints(self, pen): - """ - Draw the contour with the given PointPen. + def drawPoints(self, pen: PointPenType) -> None: + """Draw the component with the given point pen. + + :param pen: The :class:`fontTools.pens.pointPen.AbstractPointPen` with + which to draw the componnet. + """ self._drawPoints(pen) - def _drawPoints(self, pen, **kwargs): - """ - Subclasses may override this method. + def _drawPoints(self, pen: PointPenType, **kwargs: Any) -> None: + r"""Draw the native component with the given point pen. + + This is the environment implementation of :meth:`BaseComponent.draw`. + + :param pen: The :class:`fontTools.pens.pointPen.AbstractPointPen` with + which to draw the componnet. + :param \**kwargs: Additional keyword arguments. + + .. note:: + + Subclasses may override this method. + """ # The try: ... except TypeError: ... # handles backwards compatibility with @@ -289,9 +552,21 @@ def _drawPoints(self, pen, **kwargs): # Transformation # -------------- - def _transformBy(self, matrix, **kwargs): - """ - Subclasses may override this method. + def _transformBy( + self, matrix: SextupleCollectionType[IntFloatType], **kwargs: Any + ) -> None: + r"""Transform the component according to the given matrix. + + This is the environment implementation of :meth:`BaseComponent.transformBy`. + + :param matrix: The :ref:`type-transformation` to apply. The value will + be normalized with :func:`normalizers.normalizeTransformationMatrix`. + :param \**kwargs: Additional keyword arguments. + + .. note:: + + Subclasses may override this method. + """ t = transform.Transform(*matrix) transformation = t.transform(self.transformation) @@ -301,33 +576,48 @@ def _transformBy(self, matrix, **kwargs): # Normalization # ------------- - def round(self): - """ - Round offset coordinates. + def round(self) -> None: + """Round the compnent's offset coordinates. + + This applies to :attr:`offset` + """ self._round() - def _round(self): - """ - Subclasses may override this method. + def _round(self) -> None: + """Round the native compnent's offset coordinates. + + This is the environment implementation of :meth:`BaseComponent.round`. + + .. note:: + + Subclasses may override this method. + """ x, y = self.offset x = normalizers.normalizeVisualRounding(x) y = normalizers.normalizeVisualRounding(y) self.offset = (x, y) - def decompose(self): - """ - Decompose the component. - """ + def decompose(self) -> None: + """Decompose the component.""" glyph = self.glyph if glyph is None: raise FontPartsError("The component does not belong to a glyph.") self._decompose() - def _decompose(self): - """ - Subclasses must override this method. + def _decompose(self) -> None: + """Decompose the native component. + + This is the environment implementation of :meth:`BaseComponent.decompose`. + + :raises NotImplementedError: If the method has not been overridden by a + subclass. + + .. important:: + + Subclasses must override this method. + """ self.raiseNotImplementedError() @@ -337,9 +627,19 @@ def _decompose(self): compatibilityReporterClass = ComponentCompatibilityReporter - def isCompatible(self, other): - """ - Evaluate interpolation compatibility with **other**. :: + def isCompatible( + self, other: BaseComponent + ) -> Tuple[bool, ComponentCompatibilityReporter]: + """Evaluate interpolation compatibility with another component. + + :param other: The other :class:`BaseComponent` instance to check + compatibility with. + :return: A :class:`tuple` where the first element is a :class:`bool` + indicating compatibility, and the second element is + a :class:`fontParts.base.compatibility.ComponentCompatibilityReporter` + instance. + + Example:: >>> compatible, report = self.isCompatible(otherComponent) >>> compatible @@ -348,23 +648,29 @@ def isCompatible(self, other): [Warning] Component: "A" + "B" [Warning] Component: "A" has name A | "B" has name B - This will return a ``bool`` indicating if the component is - compatible for interpolation with **other** and a - :ref:`type-string` of compatibility notes. """ return super(BaseComponent, self).isCompatible(other, BaseComponent) - def _isCompatible(self, other, reporter): - """ - This is the environment implementation of - :meth:`BaseComponent.isCompatible`. + def _isCompatible( + self, other: BaseComponent, reporter: ComponentCompatibilityReporter + ) -> None: + """Evaluate interpolation compatibility with another native component. + + This is the environment implementation of :meth:`BaseComponent.isCompatible`. + + :param other: The other :class:`BaseComponent` instance to check + compatibility with. + :param reporter: An object used to report compatibility issues. + + .. note:: + + Subclasses may override this method. - Subclasses may override this method. """ component1 = self component2 = other # base glyphs - if component1.baseName != component2.baseName: + if component1.baseGlyph != component2.baseGlyph: reporter.baseDifference = True reporter.warning = True @@ -372,42 +678,82 @@ def _isCompatible(self, other, reporter): # Data Queries # ------------ - def pointInside(self, point): - """ - Determine if point is in the black or white of the component. + def pointInside(self, point: PairCollectionType[IntFloatType]) -> bool: + """Check if `point` lies inside the filled area of the component. + + :param point: The point to check as a :ref:`type-coordinate`. + :return: :obj:`True` if `point` is inside the filled area of the + glyph, :obj:`False` otherwise. + + Example:: + + >>> glyph.pointInside((40, 65)) + True - point must be an (x, y) tuple. """ point = normalizers.normalizeCoordinateTuple(point) return self._pointInside(point) - def _pointInside(self, point): - """ - Subclasses may override this method. - """ - from fontTools.pens.pointInsidePen import PointInsidePen + def _pointInside(self, point: PairCollectionType[IntFloatType]) -> bool: + """Check if `point` lies inside the filled area of the native component. + + This is the environment implementation of :meth:`BaseComponent.pointInside`. + + :param point: The point to check as a :ref:`type-coordinate`. The value will + have been normalized with :func:`normalizers.normalizeCoordinateTuple`. + :return: :class:`bool`. + .. note:: + + Subclasses may override this method. + + """ pen = PointInsidePen(glyphSet=self.layer, testPoint=point, evenOdd=False) self.draw(pen) return pen.getResult() - bounds = dynamicProperty( + bounds: dynamicProperty = dynamicProperty( "base_bounds", - ("The bounds of the component: " "(xMin, yMin, xMax, yMax) or None."), + """Get the bounds of the component. + + This property is read-only. + + :return: A :class:`tuple` of four :class:`int` or :class:`float` values + in the form ``(x minimum, y minimum, x maximum, y maximum)`` + representing the bounds of the component, or :obj:`None` if the + component is empty. + + Example:: + + >>> component.bounds + (10, 30, 765, 643) + + """, ) - def _get_base_bounds(self): + def _get_base_bounds(self) -> QuadrupleType[float]: value = self._get_bounds() if value is not None: value = normalizers.normalizeBoundingBox(value) return value - def _get_bounds(self): - """ - Subclasses may override this method. - """ - from fontTools.pens.boundsPen import BoundsPen + def _get_bounds(self) -> QuadrupleType[float]: + """Get the bounds of the component. + This is the environment implementation of the :attr:`BaseComponent.bounds` + property getter. + + :return: A :class:`tuple` of four :class:`int` or :class:`float` values + in the form ``(x minimum, y minimum, x maximum, y maximum)`` + representing the bounds of the component, or :obj:`None` if the + component is empty. The value will be normalized + with :func:`normalizers.normalizeBoundingBox`. + + .. note:: + + Subclasses may override this method. + + """ pen = BoundsPen(self.layer) self.draw(pen) return pen.bounds