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

Add metrics to transformations filter #238

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
45 changes: 41 additions & 4 deletions Lib/ufo2ft/filters/transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def IntEnum(typename, field_names):
range(len(field_names)))


log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)


class TransformPen(_TransformPen):
Expand Down Expand Up @@ -66,12 +66,35 @@ class TransformationsFilter(BaseFilter):
'ScaleY': 100,
'Slant': 0,
'Origin': 4, # BASELINE
'Width': None,
'LSB': None,
'RSB': None,
# Not in Glyphs SDK:
'Height': None,
'TSB': None,
'BSB': None,
'VerticalOrigin': None,
}

def start(self):
if self.options.Origin not in self.Origin:
raise ValueError("%r is not a valid Origin value"
% self.options.Origin)
metrics = dict()
options_to_ufo_metrics = dict(
Width="width",
LSB="leftMargin",
RSB="rightMargin",
Height="height",
TSB="topMargin",
BSB="bottomMargin",
VerticalOrigin="verticalOrigin",
)
for option, attr in options_to_ufo_metrics.items():
value = getattr(self.options, option)
if value is not None:
metrics[attr] = value
self.metrics = metrics

def get_origin_height(self, font, origin):
if origin is self.Origin.BASELINE:
Expand Down Expand Up @@ -117,9 +140,10 @@ def set_context(self, font, glyphSet):

return ctx

def filter(self, glyph):
def filter(self, glyph, isComponent=False):
metrics = self.metrics
matrix = self.context.matrix
if (matrix == Identity or
if ((not metrics and matrix == Identity) or
not (glyph or glyph.components or glyph.anchors)):
return False # nothing to do

Expand All @@ -130,12 +154,25 @@ def filter(self, glyph):
if base_name in modified:
continue
base_glyph = glyphSet[base_name]
if self.include(base_glyph) and self.filter(base_glyph):
if self.include(base_glyph) and \
self.filter(base_glyph, isComponent=True):
# base glyph is included but was not transformed yet; we
# call filter recursively until all the included bases are
# transformed, or there are no more components
modified.add(base_name)

if not isComponent:
for attr, value in metrics.items():
current_value = getattr(glyph, attr)
if current_value is not None:
value += current_value
setattr(glyph, attr, value)
else:
logger.warning(
"Cannot add %i to undefined %s in %s",
value, attr, glyph.name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor suggestion: you don’t need to increment value +=current_value, just setattr(glyph, attr, current_value+value)

)

rec = RecordingPen()
glyph.draw(rec)
glyph.clearContours()
Expand Down
104 changes: 103 additions & 1 deletion tests/filters/transformations_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import print_function, division, absolute_import
from ufo2ft.filters.transformations import TransformationsFilter, log
from ufo2ft.filters.transformations import TransformationsFilter, logger
from fontTools.misc.loggingTools import CapturingLogHandler
from fontTools.misc.py23 import isclose
import defcon
Expand All @@ -18,6 +18,8 @@
{
'name': 'a',
'width': 350,
'height': 400,
'verticalOrigin': 350,
'outline': [
('moveTo', ((0, 0),)),
('lineTo', ((300, 0),)),
Expand All @@ -33,6 +35,8 @@
{
'name': 'b',
'width': 450,
'height': 400,
'verticalOrigin': 350,
'outline': [
('addComponent', ('a', (1, 0, 0, 1, 0, 0))),
('addComponent', ('c', (1, 0, 0, 1, 0, 0))),
Expand Down Expand Up @@ -63,6 +67,8 @@ def font(request):
for param in request.param['glyphs']:
glyph = font.newGlyph(param['name'])
glyph.width = param.get('width', 0)
glyph.height = param.get('height', 0)
glyph.verticalOrigin = param.get('verticalOrigin', None)
pen = glyph.getPen()
for operator, operands in param.get('outline', []):
getattr(pen, operator)(*operands)
Expand Down Expand Up @@ -192,3 +198,99 @@ def test_composite_glyphs(self, font):
# its original transform had a scale, so it was necessary to
# compensate for the transformation applied on the base glyph
assert d.components[0].transformation == (1, 0, 0, -1, 0, 102)

@pytest.mark.parametrize(
"name",
["a", "b", "c", "d"])
def test_Width(self, font, name):
value = -2
filter_ = TransformationsFilter(
Width=value, include={name})
glyph = font[name]
previousWidth = glyph.width
assert {name} == filter_(font)
assert glyph.width == previousWidth + value

@pytest.mark.parametrize(
"name",
["a", "b", "c", "d"])
def test_Height(self, font, name):
value = -4
filter_ = TransformationsFilter(
Height=value, include={name})
glyph = font[name]
previousHeight = glyph.height
assert {name} == filter_(font)
assert glyph.height == previousHeight + value

@pytest.mark.parametrize(
"name",
["a", "b", "c", "d"])
def test_LSB(self, font, name):
value = -10
filter_ = TransformationsFilter(
LSB=value, include={name})
glyph = font[name]
previousLeftMargin = glyph.leftMargin
assert {name} == filter_(font)
assert glyph.leftMargin == previousLeftMargin + value

@pytest.mark.parametrize(
"name",
["a", "b", "c", "d"])
def test_RSB(self, font, name):
value = +10
filter_ = TransformationsFilter(
RSB=value, include={name})
glyph = font[name]
previousRightMargin = glyph.rightMargin
assert {name} == filter_(font)
assert glyph.rightMargin == previousRightMargin + value

@pytest.mark.parametrize(
"name",
["a", "b", "c", "d"])
def test_TSB(self, font, name):
value = -12
filter_ = TransformationsFilter(
TSB=value, include={name})
glyph = font[name]
previousTopMargin = glyph.topMargin
assert {name} == filter_(font)
assert glyph.topMargin == previousTopMargin + value

@pytest.mark.parametrize(
"name",
["a", "b", "c", "d"])
def test_BSB(self, font, name):
value = +12
filter_ = TransformationsFilter(
BSB=value, include={name})
glyph = font[name]
previousBottomMargin = glyph.bottomMargin
assert {name} == filter_(font)
assert glyph.bottomMargin == previousBottomMargin + value

@pytest.mark.parametrize(
"name",
["a", "b"])
def test_verticalOrigin(self, font, name):
value = +13
filter_ = TransformationsFilter(
VerticalOrigin=value, include={name})
glyph = font[name]
previousVerticalOrigin = glyph.verticalOrigin
assert {name} == filter_(font)
assert glyph.verticalOrigin == previousVerticalOrigin + value

@pytest.mark.parametrize(
"name",
["c", "d"])
def test_verticalOrigin_undefined(self, font, name):
value = +14
with CapturingLogHandler(logger, level="WARNING") as captor:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are doing pytest-style tests you could use the caplog fixture

filter_ = TransformationsFilter(
VerticalOrigin=value, include={name})
filter_(font)
captor.assertRegex(
"Cannot add %i to undefined verticalOrigin in %s" % (value, name))