From 07217be65cf4d58ae707e2cd4d1e242df5754dc8 Mon Sep 17 00:00:00 2001 From: Denis Moyogo Jacquerye Date: Mon, 26 Mar 2018 15:14:06 +0100 Subject: [PATCH 1/4] [transformations]: log -> logger --- Lib/ufo2ft/filters/transformations.py | 2 +- tests/filters/transformations_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ufo2ft/filters/transformations.py b/Lib/ufo2ft/filters/transformations.py index 183f66dc0..ccc78f41f 100644 --- a/Lib/ufo2ft/filters/transformations.py +++ b/Lib/ufo2ft/filters/transformations.py @@ -19,7 +19,7 @@ def IntEnum(typename, field_names): range(len(field_names))) -log = logging.getLogger(__name__) +logger = logging.getLogger(__name__) class TransformPen(_TransformPen): diff --git a/tests/filters/transformations_test.py b/tests/filters/transformations_test.py index 1d05696af..0d595f37d 100644 --- a/tests/filters/transformations_test.py +++ b/tests/filters/transformations_test.py @@ -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 From 9bbdebbc6a839fb910f2c9da4b6a49a821cc2beb Mon Sep 17 00:00:00 2001 From: Denis Moyogo Jacquerye Date: Mon, 26 Mar 2018 15:44:30 +0100 Subject: [PATCH 2/4] [transformations]: add metrics --- Lib/ufo2ft/filters/transformations.py | 49 ++++++++++++- tests/filters/transformations_test.py | 102 ++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/Lib/ufo2ft/filters/transformations.py b/Lib/ufo2ft/filters/transformations.py index ccc78f41f..fddc4417d 100644 --- a/Lib/ufo2ft/filters/transformations.py +++ b/Lib/ufo2ft/filters/transformations.py @@ -66,6 +66,14 @@ 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): @@ -92,6 +100,23 @@ def set_context(self, font, glyphSet): origin_height = self.get_origin_height(font, self.options.Origin) + # Metrics + ctx.metrics = dict() + if self.options.Width: + ctx.metrics["width"] = self.options.Width + if self.options.RSB: + ctx.metrics["rightMargin"] = self.options.RSB + if self.options.LSB: + ctx.metrics["leftMargin"] = self.options.LSB + if self.options.Height: + ctx.metrics["height"] = self.options.Height + if self.options.TSB: + ctx.metrics["topMargin"] = self.options.TSB + if self.options.BSB: + ctx.metrics["bottomMargin"] = self.options.BSB + if self.options.VerticalOrigin: + ctx.metrics["verticalOrigin"] = self.options.VerticalOrigin + m = Identity dx, dy = self.options.OffsetX, self.options.OffsetY if dx != 0 or dy != 0: @@ -117,9 +142,10 @@ def set_context(self, font, glyphSet): return ctx - def filter(self, glyph): + def filter(self, glyph, isComponent=False): + metrics = self.context.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 @@ -130,12 +156,29 @@ 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 in ["height", "width", + "leftMargin", "rightMargin", + "topMargin", "bottomMargin", + "verticalOrigin"]: + if attr in metrics: + value = getattr(glyph, attr) + if value is not None: + value += metrics.get(attr) + setattr(glyph, attr, value) + else: + logger.warning( + "Cannot add %i to undefined %s in %s", + metrics.get(attr), attr, glyph.name + ) + rec = RecordingPen() glyph.draw(rec) glyph.clearContours() diff --git a/tests/filters/transformations_test.py b/tests/filters/transformations_test.py index 0d595f37d..c304756ea 100644 --- a/tests/filters/transformations_test.py +++ b/tests/filters/transformations_test.py @@ -18,6 +18,8 @@ { 'name': 'a', 'width': 350, + 'height': 400, + 'verticalOrigin': 350, 'outline': [ ('moveTo', ((0, 0),)), ('lineTo', ((300, 0),)), @@ -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))), @@ -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) @@ -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: + filter_ = TransformationsFilter( + VerticalOrigin=value, include={name}) + filter_(font) + captor.assertRegex( + "Cannot add %i to undefined verticalOrigin in %s" % (value, name)) From 000a49f767cd4971f87f4eb901af3afbceddbb89 Mon Sep 17 00:00:00 2001 From: Denis Moyogo Jacquerye Date: Mon, 26 Mar 2018 20:00:03 +0100 Subject: [PATCH 3/4] [transformations] move options-to-metrics mapping to start() --- Lib/ufo2ft/filters/transformations.py | 58 ++++++++++++--------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/Lib/ufo2ft/filters/transformations.py b/Lib/ufo2ft/filters/transformations.py index fddc4417d..839354039 100644 --- a/Lib/ufo2ft/filters/transformations.py +++ b/Lib/ufo2ft/filters/transformations.py @@ -80,6 +80,21 @@ 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: @@ -100,23 +115,6 @@ def set_context(self, font, glyphSet): origin_height = self.get_origin_height(font, self.options.Origin) - # Metrics - ctx.metrics = dict() - if self.options.Width: - ctx.metrics["width"] = self.options.Width - if self.options.RSB: - ctx.metrics["rightMargin"] = self.options.RSB - if self.options.LSB: - ctx.metrics["leftMargin"] = self.options.LSB - if self.options.Height: - ctx.metrics["height"] = self.options.Height - if self.options.TSB: - ctx.metrics["topMargin"] = self.options.TSB - if self.options.BSB: - ctx.metrics["bottomMargin"] = self.options.BSB - if self.options.VerticalOrigin: - ctx.metrics["verticalOrigin"] = self.options.VerticalOrigin - m = Identity dx, dy = self.options.OffsetX, self.options.OffsetY if dx != 0 or dy != 0: @@ -143,7 +141,7 @@ def set_context(self, font, glyphSet): return ctx def filter(self, glyph, isComponent=False): - metrics = self.context.metrics + metrics = self.metrics matrix = self.context.matrix if ((not metrics and matrix == Identity) or not (glyph or glyph.components or glyph.anchors)): @@ -164,20 +162,16 @@ def filter(self, glyph, isComponent=False): modified.add(base_name) if not isComponent: - for attr in ["height", "width", - "leftMargin", "rightMargin", - "topMargin", "bottomMargin", - "verticalOrigin"]: - if attr in metrics: - value = getattr(glyph, attr) - if value is not None: - value += metrics.get(attr) - setattr(glyph, attr, value) - else: - logger.warning( - "Cannot add %i to undefined %s in %s", - metrics.get(attr), attr, glyph.name - ) + 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 + ) rec = RecordingPen() glyph.draw(rec) From e43068a53d8077b9991917e3e9627fb65a94910c Mon Sep 17 00:00:00 2001 From: Denis Moyogo Jacquerye Date: Tue, 27 Mar 2018 08:25:43 +0100 Subject: [PATCH 4/4] fixup 000a49f --- Lib/ufo2ft/filters/transformations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/ufo2ft/filters/transformations.py b/Lib/ufo2ft/filters/transformations.py index 839354039..eba84f43e 100644 --- a/Lib/ufo2ft/filters/transformations.py +++ b/Lib/ufo2ft/filters/transformations.py @@ -165,8 +165,7 @@ def filter(self, glyph, isComponent=False): for attr, value in metrics.items(): current_value = getattr(glyph, attr) if current_value is not None: - value += current_value - setattr(glyph, attr, value) + setattr(glyph, attr, value + current_value) else: logger.warning( "Cannot add %i to undefined %s in %s",