Source code for fontParts.base.image

from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union
from collections.abc import Callable

from fontTools.misc import transform
from fontParts.base.base import (
    BaseObject,
    TransformationMixin,
    SelectionMixin,
    dynamicProperty,
    reference,
)
from fontParts.base import normalizers
from fontParts.base.color import Color
from fontParts.base.deprecated import DeprecatedImage, RemovedImage
from fontParts.base.annotations import (
    ScaleFactorLike,
    ScaleFactorPair,
    ScaleFactor,
    AffineTransformationLike,
    AffineTransformation,
    Coordinate,
    CoordinateLike,
    RGBA,
    RGBALike,
    IntFloatType,
)

if TYPE_CHECKING:
    from fontParts.base.glyph import BaseGlyph
    from fontParts.base.layer import BaseLayer
    from fontParts.base.font import BaseFont


[docs] class BaseImage( BaseObject, TransformationMixin, SelectionMixin, DeprecatedImage, RemovedImage ): """Represent the basis for an image object.""" copyAttributes = ("transformation", "color", "data") def _reprContents(self): contents = [f"offset='({self.offset[0]}, {self.offset[1]})'"] if self.color: contents.append(f"color={self.color!r}") if self.glyph is not None: contents.append("in glyph") contents += self.glyph._reprContents() return contents def __bool__(self) -> bool: if self.data is None: return False elif len(self.data) == 0: return False else: return True __nonzero__ = __bool__ # ------- # Parents # ------- # Glyph _glyph: Callable[[], BaseGlyph] | None = None glyph = dynamicProperty( "glyph", """Get or set the image's parent glyph object. The value must be a :class:`BaseGlyph` instance or :obj:`None`. :return: The :class:`BaseGlyph` instance containing the image or :obj:`None`. :raises AssertionError: If attempting to set the glyph when it has already been set. Example:: >>> glyph = image.glyph """, ) def _get_glyph(self) -> BaseGlyph | None: if self._glyph is None: return None return self._glyph() def _set_glyph(self, glyph: BaseGlyph | Callable[[], BaseGlyph] | None) -> None: if self._glyph is not None: raise AssertionError("glyph for image already set") if glyph is not None: glyph = reference(glyph) self._glyph = glyph # Layer layer: dynamicProperty = dynamicProperty( "layer", """Get the image's parent layer object. This property is read-only. :return: The :class:`BaseLayer` instance containing the image or :obj:`None`. Example:: >>> layer = image.layer """, ) def _get_layer(self) -> BaseLayer | None: if self._glyph is None: return None return self.glyph.layer # Font font: dynamicProperty = dynamicProperty( "font", """Get the image's parent font object. This property is read-only. :return: The :class:`BaseFont` instance containing the image or :obj:`None`. Example:: >>> font = image.font """, ) def _get_font(self) -> BaseFont | None: if self._glyph is None: return None return self.glyph.font # ---------- # Attributes # ---------- # Transformation transformation: dynamicProperty = dynamicProperty( "base_transformation", """Get or set the image's transformation matrix. The value must be a :ref:`type-transformation`. :return: A :ref:`type-transformation` value representing the transformation matrix of the image. Example:: >>> image.transformation (1, 0, 0, 1, 0, 0) >>> image.transformation = (2, 0, 0, 2, 100, -50) """, ) def _get_base_transformation(self) -> AffineTransformation: value = self._get_transformation() value = normalizers.normalizeTransformationMatrix(value) return value def _set_base_transformation(self, value: AffineTransformationLike) -> None: value = normalizers.normalizeTransformationMatrix(value) self._set_transformation(value)
[docs] def _get_transformation(self) -> AffineTransformation: """Get the native image's transformation matrix. This is the environment implementation of the :attr:`BaseImage.transformation` property getter. :return: A :ref:`type-transformation` value representing the transformation matrix of the image. 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()
[docs] def _set_transformation(self, value: AffineTransformationLike) -> None: """Set the native image's transformation matrix. This is the environment implementation of the :attr:`BaseImage.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: dynamicProperty = dynamicProperty( "base_offset", """Get or set the image's offset. The value must be a :ref:`type-coordinate`. :return: A :ref:`type-coordinate` representing the offset of the image. Example:: >>> image.offset (0, 0) >>> image.offset = (100, -50) """, ) def _get_base_offset(self) -> Coordinate: value = self._get_offset() value = normalizers.normalizeTransformationOffset(value) return value def _set_base_offset(self, value: CoordinateLike) -> None: value = normalizers.normalizeTransformationOffset(value) self._set_offset(value)
[docs] def _get_offset(self) -> Coordinate: """Get the native image's offset. This is the environment implementation of the :attr:`BaseImage.offset` property getter. :return: A :ref:`type-coordinate` representing the offset of the image. 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)
[docs] def _set_offset(self, value: CoordinateLike) -> None: """Set the native image's offset. This is the environment implementation of the :attr:`BaseImage.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 self.transformation = (sx, sxy, syx, sy, ox, oy)
scale: dynamicProperty = dynamicProperty( "base_scale", """Get or set the image'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 image. :return: A :class:`tuple` of two :class:`float` items representing the ``(x, y)`` scale of the image. Example:: >>> image.scale (1, 1) >>> image.scale = (2, 2) """, ) def _get_base_scale(self) -> ScaleFactor: value = self._get_scale() value = normalizers.normalizeTransformationScale(value) return value def _set_base_scale(self, value: ScaleFactorLike) -> None: value = normalizers.normalizeTransformationScale(value) self._set_scale(value)
[docs] def _get_scale(self) -> ScaleFactor: """Get the native image's scale. This is the environment implementation of the :attr:`BaseImage.scale` property getter. :return: A :class:`tuple` of two :class:`float` items representing the ``(x, y)`` scale of the image. The value will have been normalized with :func:`normalizers.normalizeTransformationScale`. .. note:: Subclasses may override this method. """ sx, sxy, syx, sy, ox, oy = self.transformation return (sx, sy)
[docs] def _set_scale(self, value: ScaleFactorPair) -> None: """Set the native image's scale. This is the environment implementation of the :attr:`BaseImage.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 image. The value will have been normalized with :func:`normalizers.normalizeTransformationScale`. .. note:: Subclasses may override this method. """ sx, sxy, syx, sy, ox, oy = self.transformation sx, sy = value self.transformation = (sx, sxy, syx, sy, ox, oy)
# Color color: dynamicProperty = dynamicProperty( "base_color", """Get or set the image's color. The value must be a :ref:`type-color` or :obj:`None`. :return: A :class:`Color` instance representing the color of the image, or :obj:`None`. Example:: >>> image.color None >>> image.color = (1, 0, 0, 0.5) """, ) def _get_base_color(self) -> Color | None: value = self._get_color() if value is not None: value = Color(value) return value def _set_base_color(self, value: RGBALike | None) -> None: if value is not None: value = normalizers.normalizeColor(value) self._set_color(value)
[docs] def _get_color(self) -> RGBA | None: """Get the native image's color. This is the environment implementation of the :attr:`BaseImage.color` property getter. :return: A :ref:`type-color` representing the color of the image, or :obj:`None`. The value will be normalized with :func:`normalizers.normalizeColor`. :raises NotImplementedError: If the method has not been overridden by a subclass. .. important:: Subclasses must override this method. """ self.raiseNotImplementedError()
[docs] def _set_color(self, value: RGBALike | None) -> None: """Set the native image's color. This is the environment implementation of the :attr:`BaseImage.color` property setter. :param value: The :ref:`type-color` to set for the image or :obj:`None`. The value will have been normalized with :func:`normalizers.normalizeColor`. :raises NotImplementedError: If the method has not been overridden by a subclass. .. important:: Subclasses must override this method. """ self.raiseNotImplementedError()
# Data data: dynamicProperty = dynamicProperty( "data", """Get or set the image's raw byte data. The possible formats are defined by each environment. The value must be a :class:`bytes` object. :return: A :class:`bytes` object representing the raw byte data of the image or :obj:`None`. """, ) def _get_base_data(self) -> bytes | None: return self._get_data() def _set_base_data(self, value: bytes) -> None: self._set_data(value)
[docs] def _get_data(self) -> bytes | None: """Get the native image's raw byte data. This is the environment implementation of the :attr:`BaseImage.data` property getter. :return: A :class:`bytes` object representing the raw byte data of the image or :obj:`None`. :raises NotImplementedError: If the method has not been overridden by a subclass. .. important:: Subclasses must override this method. """ self.raiseNotImplementedError()
[docs] def _set_data(self, value: bytes) -> None: """Set the native image's raw byte data. This is the environment implementation of the :attr:`BaseImage.data` property setter. :param value: The :class:`bytes` object to set for the image. :raises NotImplementedError: If the method has not been overridden by a subclass. .. important:: Subclasses must override this method. """ self.raiseNotImplementedError()
# -------------- # Transformation # --------------
[docs] def _transformBy(self, matrix: AffineTransformationLike, **kwargs: Any) -> None: r"""Transform the native image. This is the environment implementation of :meth:`BaseImage.transformBy`. :param matrix: The transformation to apply as a :ref:`type-transformation`. :param \**kwargs: Additional keyword arguments. .. note:: Subclasses may override this method. """ t = transform.Transform(*matrix) transformation = t.transform(self.transformation) self.transformation = tuple(transformation)
# ------------- # Normalization # -------------
[docs] def round(self) -> None: """Round the image's offset coordinates. Example:: >>> image.round() """ self._round()
[docs] def _round(self) -> None: """Round the native image's offset coordinates. This is the environment implementation of :meth:`BaseImage.round`. .. note:: Subclasses may override this method. """ x, y = self.offset x = normalizers.normalizeVisualRounding(x) y = normalizers.normalizeVisualRounding(y) self.offset = (x, y)