Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transformations: always transform components' x/y offsets #380

Merged
merged 3 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 11 additions & 21 deletions Lib/ufo2ft/filters/transformations.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,34 @@
from __future__ import print_function, division, absolute_import, unicode_literals

import math
from collections import namedtuple
import logging

from ufo2ft.fontInfoData import getAttrWithFallback
from ufo2ft.filters import BaseFilter

from fontTools.misc.py23 import round
from fontTools.misc.fixedTools import otRound
from fontTools.misc.transform import Transform, Identity
from fontTools.pens.recordingPen import RecordingPen
from fontTools.pens.transformPen import TransformPen as _TransformPen
from fontTools.pens.recordingPen import RecordingPointPen
from fontTools.pens.transformPen import TransformPointPen as _TransformPointPen
from enum import IntEnum


log = logging.getLogger(__name__)


class TransformPen(_TransformPen):
def __init__(self, outPen, transformation, modified=None):
super(TransformPen, self).__init__(outPen, transformation)
class TransformPointPen(_TransformPointPen):
def __init__(self, outPointPen, transformation, modified=None):
super().__init__(outPointPen, transformation)
self.modified = modified if modified is not None else set()
self._inverted = self._transformation.inverse()

def addComponent(self, baseGlyph, transformation):
def addComponent(self, baseGlyph, transformation, identifier=None, **kwargs):
if baseGlyph in self.modified:

if transformation[:4] == (1, 0, 0, 1):
# if the component's transform only has a simple offset, then
# we don't need to transform the component again
self._outPen.addComponent(baseGlyph, transformation)
return

# multiply the component's transformation matrix with the inverse
# of the filter's transformation matrix to compensate for the
# transformation already applied to the base glyph
transformation = Transform(*transformation).transform(self._inverted)

super(TransformPen, self).addComponent(baseGlyph, transformation)
super().addComponent(baseGlyph, transformation, identifier=identifier, **kwargs)


class TransformationsFilter(BaseFilter):
Expand Down Expand Up @@ -123,13 +113,13 @@ def filter(self, glyph):
# transformed, or there are no more components
modified.add(base_name)

rec = RecordingPen()
glyph.draw(rec)
rec = RecordingPointPen()
glyph.drawPoints(rec)
glyph.clearContours()
glyph.clearComponents()

outpen = glyph.getPen()
filterpen = TransformPen(outpen, matrix, modified)
outpen = glyph.getPointPen()
filterpen = TransformPointPen(outpen, matrix, modified)
rec.replay(filterpen)

# anchors are not drawn through the pen API,
Expand Down
13 changes: 9 additions & 4 deletions tests/filters/transformations_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import print_function, division, absolute_import
from ufo2ft.filters.transformations import TransformationsFilter, log
from fontTools.misc.loggingTools import CapturingLogHandler
from fontTools.misc.py23 import isclose
from math import isclose
import pytest


Expand Down Expand Up @@ -30,6 +29,7 @@
"outline": [
("addComponent", ("a", (1, 0, 0, 1, 0, 0))),
("addComponent", ("c", (1, 0, 0, 1, 0, 0))),
("addComponent", ("a", (1, 0, 0, 1, 10, -10))),
],
},
{
Expand Down Expand Up @@ -173,11 +173,16 @@ def test_composite_glyphs(self, font):
assert filter_(font)

b = font["b"]
# component 'a' was not transformed, because it doesn't have a scale
# or skew and the base glyph was already included
# component 'a' #1 was not transformed, because the base glyph was already
# transformed, and the component's own transformation is identity
assert b.components[0].transformation == (1, 0, 0, 1, 0, 0)
# component 'c' was transformed, because base glyph was not included
assert b.components[1].transformation == (0.5, 0, 0, 0.5, -10, 51)
# component 'a' #2 was partly transformed: the base glyph was transformed, but
# the component's original transformation was not identity; thus
# it was modified to compensate for the transformation already applied to
# the base glyph (scale stays same, offsets are scaled)
assert b.components[2].transformation == (1, 0, 0, 1, 5, -5)

d = font["d"]
# component 'b' was transformed as well as its base glyph, because
Expand Down