# transform: Tools for working with transformation matrices

Affine 2D transformation matrix class.

The Transform class implements various transformation matrix operations, both on the matrix itself, as well as on 2D coordinates.

Transform instances are effectively immutable: all methods that operate on the transformation itself always return a new instance. This has as the interesting side effect that Transform instances are hashable, ie. they can be used as dictionary keys.

This module exports the following symbols:

Transform

  this is the main class

Identity

  Transform instance set to the identity transformation

Offset

  Convenience function that returns a translating transformation

Scale

  Convenience function that returns a scaling transformation

The DecomposedTransform class implements a transformation with separate translate, rotation, scale, skew, and transformation-center components.

Example:

      >>> t = Transform(2, 0, 0, 3, 0, 0)
      >>> t.transformPoint((100, 100))
      (200, 300)
      >>> t = Scale(2, 3)
      >>> t.transformPoint((100, 100))
      (200, 300)
      >>> t.transformPoint((0, 0))
      (0, 0)
      >>> t = Offset(2, 3)
      >>> t.transformPoint((100, 100))
      (102, 103)
      >>> t.transformPoint((0, 0))
      (2, 3)
      >>> t2 = t.scale(0.5)
      >>> t2.transformPoint((100, 100))
      (52.0, 53.0)
      >>> import math
      >>> t3 = t2.rotate(math.pi / 2)
      >>> t3.transformPoint((0, 0))
      (2.0, 3.0)
      >>> t3.transformPoint((100, 100))
      (-48.0, 53.0)
      >>> t = Identity.scale(0.5).translate(100, 200).skew(0.1, 0.2)
      >>> t.transformPoints([(0, 0), (1, 1), (100, 100)])
      [(50.0, 100.0), (50.550167336042726, 100.60135501775433), (105.01673360427253, 160.13550177543362)]
      >>>

class fontTools.misc.transform.Transform(xx: float = 1, xy: float = 0, yx: float = 0, yy: float = 1, dx: float = 0, dy: float = 0)

  Bases: `NamedTuple`

  2x2 transformation matrix plus offset, a.k.a. Affine transform. Transform instances are immutable: all transforming methods, eg. rotate(), return a new Transform instance.

  Example:

        >>> t = Transform()
        >>> t
        <Transform [1 0 0 1 0 0]>
        >>> t.scale(2)
        <Transform [2 0 0 2 0 0]>
        >>> t.scale(2.5, 5.5)
        <Transform [2.5 0 0 5.5 0 0]>
        >>>
        >>> t.scale(2, 3).transformPoint((100, 100))
        (200, 300)

  Transform’s constructor takes six arguments, all of which are optional, and can be used as keyword arguments:

      >>> Transform(12)
      <Transform [12 0 0 1 0 0]>
      >>> Transform(dx=12)
      <Transform [1 0 0 1 12 0]>
      >>> Transform(yx=12)
      <Transform [1 0 12 1 0 0]>

  Transform instances also behave like sequences of length 6:

      >>> len(Identity)
      6
      >>> list(Identity)
      [1, 0, 0, 1, 0, 0]
      >>> tuple(Identity)
      (1, 0, 0, 1, 0, 0)

  Transform instances are comparable:

      >>> t1 = Identity.scale(2, 3).translate(4, 6)
      >>> t2 = Identity.translate(8, 18).scale(2, 3)
      >>> t1 == t2
      1

  But beware of floating point rounding errors:

      >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
      >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
      >>> t1
      <Transform [0.2 0 0 0.3 0.08 0.18]>
      >>> t2
      <Transform [0.2 0 0 0.3 0.08 0.18]>
      >>> t1 == t2
      0

  Transform instances are hashable, meaning you can use them as keys in dictionaries:

      >>> d = {Scale(12, 13): None}
      >>> d
      {<Transform [12 0 0 13 0 0]>: None}

  But again, beware of floating point rounding errors:

      >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
      >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
      >>> t1
      <Transform [0.2 0 0 0.3 0.08 0.18]>
      >>> t2
      <Transform [0.2 0 0 0.3 0.08 0.18]>
      >>> d = {t1: None}
      >>> d
      {<Transform [0.2 0 0 0.3 0.08 0.18]>: None}
      >>> d[t2]
      Traceback (most recent call last):
        File "<stdin>", line 1, in ?
      KeyError: <Transform [0.2 0 0 0.3 0.08 0.18]>

  xx: float

    Alias for field number 0

  xy: float

    Alias for field number 1

  yx: float

    Alias for field number 2

  yy: float

    Alias for field number 3

  dx: float

    Alias for field number 4

  dy: float

    Alias for field number 5

  transformPoint(p)

    Transform a point.

    Example:

          >>> t = Transform()
          >>> t = t.scale(2.5, 5.5)
          >>> t.transformPoint((100, 100))
          (250.0, 550.0)

  transformPoints(points)

    Transform a list of points.

    Example:

          >>> t = Scale(2, 3)
          >>> t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)])
          [(0, 0), (0, 300), (200, 300), (200, 0)]
          >>>

  transformVector(v)

    Transform an (dx, dy) vector, treating translation as zero.

    Example:

          >>> t = Transform(2, 0, 0, 2, 10, 20)
          >>> t.transformVector((3, -4))
          (6, -8)
          >>>

  transformVectors(vectors)

    Transform a list of (dx, dy) vector, treating translation as zero.

    Example:

          >>> t = Transform(2, 0, 0, 2, 10, 20)
          >>> t.transformVectors([(3, -4), (5, -6)])
          [(6, -8), (10, -12)]
          >>>

  translate(x: float = 0, y: float = 0)

    Return a new transformation, translated (offset) by x, y.

    Example:

          >>> t = Transform()
          >>> t.translate(20, 30)
          <Transform [1 0 0 1 20 30]>
          >>>

  scale(x: float = 1, y: float | None = None)

    Return a new transformation, scaled by x, y. The ‘y’ argument may be None, which implies to use the x value for y as well.

    Example:

          >>> t = Transform()
          >>> t.scale(5)
          <Transform [5 0 0 5 0 0]>
          >>> t.scale(5, 6)
          <Transform [5 0 0 6 0 0]>
          >>>

  rotate(angle: float)

    Return a new transformation, rotated by ‘angle’ (radians).

    Example:

          >>> import math
          >>> t = Transform()
          >>> t.rotate(math.pi / 2)
          <Transform [0 1 -1 0 0 0]>
          >>>

  skew(x: float = 0, y: float = 0)

    Return a new transformation, skewed by x and y.

    Example:

          >>> import math
          >>> t = Transform()
          >>> t.skew(math.pi / 4)
          <Transform [1 0 1 1 0 0]>
          >>>

  transform(other)

    Return a new transformation, transformed by another transformation.

    Example:

          >>> t = Transform(2, 0, 0, 3, 1, 6)
          >>> t.transform((4, 3, 2, 1, 5, 6))
          <Transform [8 9 4 3 11 24]>
          >>>

  reverseTransform(other)

    Return a new transformation, which is the other transformation transformed by self. self.reverseTransform(other) is equivalent to other.transform(self).

    Example:

          >>> t = Transform(2, 0, 0, 3, 1, 6)
          >>> t.reverseTransform((4, 3, 2, 1, 5, 6))
          <Transform [8 6 6 3 21 15]>
          >>> Transform(4, 3, 2, 1, 5, 6).transform((2, 0, 0, 3, 1, 6))
          <Transform [8 6 6 3 21 15]>
          >>>

  inverse()

    Return the inverse transformation.

    Example:

          >>> t = Identity.translate(2, 3).scale(4, 5)
          >>> t.transformPoint((10, 20))
          (42, 103)
          >>> it = t.inverse()
          >>> it.transformPoint((42, 103))
          (10.0, 20.0)
          >>>

  toPS() → str

    Return a PostScript representation

    Example:

          >>> t = Identity.scale(2, 3).translate(4, 5)
          >>> t.toPS()
          '[2 0 0 3 8 15]'
          >>>

  toDecomposed() → DecomposedTransform

    Decompose into a DecomposedTransform.

fontTools.misc.transform.Offset(x: float = 0, y: float = 0) → Transform

  Return the identity transformation offset by x, y.

  Example:

        >>> Offset(2, 3)
        <Transform [1 0 0 1 2 3]>
        >>>

fontTools.misc.transform.Scale(x: float, y: float | None = None) → Transform

  Return the identity transformation scaled by x, y. The ‘y’ argument may be None, which implies to use the x value for y as well.

  Example:

        >>> Scale(2, 3)
        <Transform [2 0 0 3 0 0]>
        >>>

class fontTools.misc.transform.DecomposedTransform(translateX: float = 0, translateY: float = 0, rotation: float = 0, scaleX: float = 1, scaleY: float = 1, skewX: float = 0, skewY: float = 0, tCenterX: float = 0, tCenterY: float = 0)

  Bases: `object`

  The DecomposedTransform class implements a transformation with separate translate, rotation, scale, skew, and transformation-center components.

  translateX: float = 0
  translateY: float = 0
  rotation: float = 0
  scaleX: float = 1
  scaleY: float = 1
  skewX: float = 0
  skewY: float = 0
  tCenterX: float = 0
  tCenterY: float = 0
  classmethod fromTransform(transform)

    Return a DecomposedTransform() equivalent of this transformation. The returned solution always has skewY = 0, and angle in the (-180, 180].

    Example:

          >>> DecomposedTransform.fromTransform(Transform(3, 0, 0, 2, 0, 0))
          DecomposedTransform(translateX=0, translateY=0, rotation=0.0, scaleX=3.0, scaleY=2.0, skewX=0.0, skewY=0.0, tCenterX=0, tCenterY=0)
          >>> DecomposedTransform.fromTransform(Transform(0, 0, 0, 1, 0, 0))
          DecomposedTransform(translateX=0, translateY=0, rotation=0.0, scaleX=0.0, scaleY=1.0, skewX=0.0, skewY=0.0, tCenterX=0, tCenterY=0)
          >>> DecomposedTransform.fromTransform(Transform(0, 0, 1, 1, 0, 0))
          DecomposedTransform(translateX=0, translateY=0, rotation=-45.0, scaleX=0.0, scaleY=1.4142135623730951, skewX=0.0, skewY=0.0, tCenterX=0, tCenterY=0)

  toTransform() → Transform

    Return the Transform() equivalent of this transformation.

    Example:

          >>> DecomposedTransform(scaleX=2, scaleY=2).toTransform()
          <Transform [2 0 0 2 0 0]>
          >>>