from fontTools.misc import transform
from fontParts.base.base import (
BaseObject,
TransformationMixin,
PointPositionMixin,
SelectionMixin,
dynamicProperty,
reference
)
from fontParts.base import normalizers
from fontParts.base.color import Color
from fontParts.base.deprecated import DeprecatedImage, RemovedImage
[docs]class BaseImage(
BaseObject,
TransformationMixin,
PointPositionMixin,
SelectionMixin,
DeprecatedImage,
RemovedImage
):
copyAttributes = (
"transformation",
"color",
"data"
)
def _reprContents(self):
contents = [
"offset='({x}, {y})'".format(x=self.offset[0], y=self.offset[1]),
]
if self.color:
contents.append("color=%r" % str(self.color))
if self.glyph is not None:
contents.append("in glyph")
contents += self.glyph._reprContents()
return contents
def __bool__(self):
if self.data is None:
return False
elif len(self.data) == 0:
return False
else:
return True
__nonzero__ = __bool__
# -------
# Parents
# -------
# Glyph
_glyph = None
glyph = dynamicProperty("glyph", "The image's parent :class:`BaseGlyph`.")
def _get_glyph(self):
if self._glyph is None:
return None
return self._glyph()
def _set_glyph(self, glyph):
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("layer", "The image's parent :class:`BaseLayer`.")
def _get_layer(self):
if self._glyph is None:
return None
return self.glyph.layer
# Font
font = dynamicProperty("font", "The image's parent :class:`BaseFont`.")
def _get_font(self):
if self._glyph is None:
return None
return self.glyph.font
# ----------
# Attributes
# ----------
# Transformation
transformation = dynamicProperty(
"base_transformation",
"""
The image's :ref:`type-transformation`.
This defines the image's position, scale,
and rotation. ::
>>> image.transformation
(1, 0, 0, 1, 0, 0)
>>> image.transformation = (2, 0, 0, 2, 100, -50)
"""
)
def _get_base_transformation(self):
value = self._get_transformation()
value = normalizers.normalizeTransformationMatrix(value)
return value
def _set_base_transformation(self, value):
value = normalizers.normalizeTransformationMatrix(value)
self._set_transformation(value)
offset = dynamicProperty(
"base_offset",
"""
The image's offset. This is a shortcut to the offset
values in :attr:`transformation`. This must be an
iterable containing two :ref:`type-int-float` values
defining the x and y values to offset the image by. ::
>>> image.offset
(0, 0)
>>> image.offset = (100, -50)
"""
)
def _get_base_offset(self):
value = self._get_offset()
value = normalizers.normalizeTransformationOffset(value)
return value
def _set_base_offset(self, value):
value = normalizers.normalizeTransformationOffset(value)
self._set_offset(value)
[docs] def _get_offset(self):
"""
Subclasses may override this method.
"""
sx, sxy, syx, sy, ox, oy = self.transformation
return (ox, oy)
[docs] def _set_offset(self, value):
"""
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(
"base_scale",
"""
The image's scale. This is a shortcut to the scale
values in :attr:`transformation`. This must be an
iterable containing two :ref:`type-int-float` values
defining the x and y values to scale the image by. ::
>>> image.scale
(1, 1)
>>> image.scale = (2, 2)
"""
)
def _get_base_scale(self):
value = self._get_scale()
value = normalizers.normalizeTransformationScale(value)
return value
def _set_base_scale(self, value):
value = normalizers.normalizeTransformationScale(value)
self._set_scale(value)
[docs] def _get_scale(self):
"""
Subclasses may override this method.
"""
sx, sxy, syx, sy, ox, oy = self.transformation
return (sx, sy)
[docs] def _set_scale(self, value):
"""
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(
"base_color",
"""
The image's color. This will be a
:ref:`type-color` or ``None``. ::
>>> image.color
None
>>> image.color = (1, 0, 0, 0.5)
"""
)
def _get_base_color(self):
value = self._get_color()
if value is not None:
value = normalizers.normalizeColor(value)
value = Color(value)
return value
def _set_base_color(self, value):
if value is not None:
value = normalizers.normalizeColor(value)
self._set_color(value)
[docs] def _get_color(self):
"""
Return the color value as a color tuple or None.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs] def _set_color(self, value):
"""
value will be a color tuple or None.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# Data
data = dynamicProperty(
"data",
"""
The image's raw byte data. The possible
formats are defined by each environment.
"""
)
def _get_base_data(self):
return self._get_data()
def _set_base_data(self, value):
self._set_data(value)
[docs] def _get_data(self):
"""
This must return raw byte data.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
[docs] def _set_data(self, value):
"""
value will be raw byte data.
Subclasses must override this method.
"""
self.raiseNotImplementedError()
# --------------
# Transformation
# --------------
# -------------
# Normalization
# -------------
[docs] def round(self):
"""
Round offset coordinates.
"""
self._round()
[docs] def _round(self):
"""
Subclasses may override this method.
"""
x, y = self.offset
x = normalizers.normalizeVisualRounding(x)
y = normalizers.normalizeVisualRounding(y)
self.offset = (x, y)