[VarComposite] Add VarTransform and use

This commit is contained in:
Behdad Esfahbod 2023-02-02 14:41:57 -07:00
parent 68774ac0e6
commit 59418656a4
4 changed files with 54 additions and 39 deletions

View File

@ -49,6 +49,7 @@ Scale
>>>
"""
import math
from typing import NamedTuple
@ -398,6 +399,30 @@ def Scale(x, y=None):
return Transform(x, 0, 0, y, 0, 0)
class VarTransform:
translateX: float = 0
translateY: float = 0
rotation: float = 0 # in degrees, counter-clockwise
scaleX: float = 1
scaleY: float = 1
skewX: float = 0 # in degrees, counter-clockwise
skewY: float = 0 # in degrees, counter-clockwise
tCenterX: float = 0
tCenterY: float = 0
def toTransform(self):
t = Transform()
t = t.translate(
self.translateX + self.tCenterX, self.translateY + self.tCenterY
)
t = t.rotate(math.radians(self.rotation))
t = t.scale(self.scaleX, self.scaleY)
t = t.skew(-math.radians(self.skewX), math.radians(self.skewY))
t = t.translate(-self.tCenterX, -self.tCenterY)
return t
if __name__ == "__main__":
import sys
import doctest

View File

@ -127,9 +127,8 @@ def visit(visitor, obj, attr, glyphs):
if g.isVarComposite():
for component in g.components:
for attr in ("translateX", "translateY", "tCenterX", "tCenterY"):
v = getattr(component, attr, None)
if v is not None:
setattr(component, attr, visitor.scale(v))
v = getattr(component.transform, attr)
setattr(component.transform, attr, visitor.scale(v))
continue
if hasattr(g, "coordinates"):

View File

@ -4,6 +4,7 @@ from collections import namedtuple
from fontTools.misc import sstruct
from fontTools import ttLib
from fontTools import version
from fontTools.misc.transform import VarTransform
from fontTools.misc.textTools import tostr, safeEval, pad
from fontTools.misc.arrayTools import calcIntBounds, pointInRect
from fontTools.misc.bezierTools import calcQuadraticBounds
@ -1749,7 +1750,8 @@ VAR_COMPONENT_TRANSFORM_MAPPING = {
class GlyphVarComponent(object):
def __init__(self):
pass
self.location = {}
self.transform = VarTransform()
def decompile(self, data, glyfTable):
flags = struct.unpack(">H", data[:2])[0]
@ -1803,13 +1805,13 @@ class GlyphVarComponent(object):
for attr_name, mapping_values in VAR_COMPONENT_TRANSFORM_MAPPING.items():
data, value = read_transform_component(data, mapping_values)
setattr(self, attr_name, value)
setattr(self.transform, attr_name, value)
if flags & VarComponentFlags.UNIFORM_SCALE:
if flags & VarComponentFlags.HAVE_SCALE_X and not (
flags & VarComponentFlags.HAVE_SCALE_Y
):
self.scaleY = self.scaleX
self.transform.scaleY = self.transform.scaleX
flags |= VarComponentFlags.HAVE_SCALE_Y
flags ^= VarComponentFlags.UNIFORM_SCALE
@ -1822,7 +1824,7 @@ class GlyphVarComponent(object):
flags = 0
# Calculate optimal transform component flags
for attr_name, mapping in VAR_COMPONENT_TRANSFORM_MAPPING.items():
value = getattr(self, attr_name, mapping.defaultValue)
value = getattr(self.transform, attr_name)
if fl2fi(value / mapping.scale, mapping.fractionalBits) != fl2fi(
mapping.defaultValue / mapping.scale, mapping.fractionalBits
):
@ -1886,7 +1888,7 @@ class GlyphVarComponent(object):
attrs = attrs + [("flags", hex(self.flags))]
for attr_name, mapping in VAR_COMPONENT_TRANSFORM_MAPPING.items():
v = getattr(self, attr_name, mapping.defaultValue)
v = getattr(self.transform, attr_name)
if v != mapping.defaultValue:
attrs.append((attr_name, fl2str(v, mapping.fractionalBits)))
@ -1911,14 +1913,11 @@ class GlyphVarComponent(object):
self.flags = safeEval(attrs["flags"])
for attr_name, mapping in VAR_COMPONENT_TRANSFORM_MAPPING.items():
v = (
str2fl(safeEval(attrs[attr_name]), mapping.fractionalBits)
if attr_name in attrs
else mapping.defaultValue
)
setattr(self, attr_name, v)
if attr_name not in attrs:
continue
v = str2fl(safeEval(attrs[attr_name]), mapping.fractionalBits)
setattr(self.transform, attr_name, v)
self.location = {}
for c in content:
if not isinstance(c, tuple):
continue
@ -1974,28 +1973,30 @@ class GlyphVarComponent(object):
VarComponentFlags.HAVE_TRANSLATE_X | VarComponentFlags.HAVE_TRANSLATE_Y
):
controls.append("translate")
coords.append((self.translateX, self.translateY))
coords.append((self.transform.translateX, self.transform.translateY))
if self.flags & VarComponentFlags.HAVE_ROTATION:
controls.append("rotation")
coords.append((fl2fi(self.rotation / 180, 12), 0))
coords.append((fl2fi(self.transform.rotation / 180, 12), 0))
if self.flags & (
VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y
):
controls.append("scale")
coords.append((fl2fi(self.scaleX, 10), fl2fi(self.scaleY, 10)))
coords.append(
(fl2fi(self.transform.scaleX, 10), fl2fi(self.transform.scaleY, 10))
)
if self.flags & (VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y):
controls.append("skew")
coords.append(
(
fl2fi(self.skewX / 180, 12),
fl2fi(self.skewY / 180, 12),
fl2fi(self.transform.skewX / 180, 12),
fl2fi(self.transform.skewY / 180, 12),
)
)
if self.flags & (
VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y
):
controls.append("tCenter")
coords.append((self.tCenterX, self.tCenterY))
coords.append((self.transform.tCenterX, self.transform.tCenterY))
return coords, controls
@ -2012,18 +2013,20 @@ class GlyphVarComponent(object):
if self.flags & (
VarComponentFlags.HAVE_TRANSLATE_X | VarComponentFlags.HAVE_TRANSLATE_Y
):
self.translateX, self.translateY = coords[i]
self.transform.translateX, self.transform.translateY = coords[i]
i += 1
if self.flags & VarComponentFlags.HAVE_ROTATION:
self.rotation = fi2fl(coords[i][0], 12) * 180
self.transform.rotation = fi2fl(coords[i][0], 12) * 180
i += 1
if self.flags & (
VarComponentFlags.HAVE_SCALE_X | VarComponentFlags.HAVE_SCALE_Y
):
self.scaleX, self.scaleY = fi2fl(coords[i][0], 10), fi2fl(coords[i][1], 10)
self.transform.scaleX, self.transform.scaleY = fi2fl(
coords[i][0], 10
), fi2fl(coords[i][1], 10)
i += 1
if self.flags & (VarComponentFlags.HAVE_SKEW_X | VarComponentFlags.HAVE_SKEW_Y):
self.skewX, self.skewY = (
self.transform.skewX, self.transform.skewY = (
fi2fl(coords[i][0], 12) * 180,
fi2fl(coords[i][1], 12) * 180,
)
@ -2031,7 +2034,7 @@ class GlyphVarComponent(object):
if self.flags & (
VarComponentFlags.HAVE_TCENTER_X | VarComponentFlags.HAVE_TCENTER_Y
):
self.tCenterX, self.tCenterY = coords[i]
self.transform.tCenterX, self.transform.tCenterY = coords[i]
i += 1
return coords[i:]

View File

@ -1,6 +1,5 @@
"""GlyphSets returned by a TTFont."""
import math
from abc import ABC, abstractmethod
from collections.abc import Mapping
from contextlib import contextmanager
@ -181,18 +180,7 @@ class _TTGlyphGlyf(_TTGlyph):
for comp in glyph.components:
# Create a transform filled in from the component and defaults
ct = SimpleNamespace()
for key, values in VAR_COMPONENT_TRANSFORM_MAPPING.items():
setattr(ct, key, getattr(comp, key, values.defaultValue))
t = Transform()
t = t.translate(ct.translateX + ct.tCenterX, ct.translateY + ct.tCenterY)
t = t.rotate(math.radians(ct.rotation))
t = t.scale(ct.scaleX, ct.scaleY)
t = t.skew(-math.radians(ct.skewX), math.radians(ct.skewY))
t = t.translate(-ct.tCenterX, -ct.tCenterY)
t = comp.transform.toTransform()
if isPointPen:
tPen = TransformPointPen(pen, t)
else: