summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorJames Godfrey-Kittle <jamesgk@google.com>2015-08-31 16:54:15 -0700
committerJames Godfrey-Kittle <jamesgk@google.com>2015-08-31 16:54:15 -0700
commita527f8faa39de6957b093b5b3f0c4a128ac3b4b0 (patch)
treeff47fa90bc78e1b096adf37dde3a4156388805a7 /scripts
parent9d6cd8e1df2941e63d65e651f3cf4954fd07924c (diff)
Add test to compare glyph sizes between fonts.
For now, we just check which glyphs are unchanged between the Thin and Bold masters, and make sure only those glyphs are unchanged between Thin, Regular, and Bold output (ignoring empty glyphs).
Diffstat (limited to 'scripts')
-rw-r--r--scripts/common_tests.py54
-rw-r--r--scripts/glyph_area_pen.py77
-rwxr-xr-xscripts/run_general_tests.py6
3 files changed, 137 insertions, 0 deletions
diff --git a/scripts/common_tests.py b/scripts/common_tests.py
index 56f0b9d..54dff86 100644
--- a/scripts/common_tests.py
+++ b/scripts/common_tests.py
@@ -27,6 +27,7 @@ import freetype
import layout
import roboto_data
+from glyph_area_pen import GlyphAreaPen
def get_rendered_char_height(font_filename, font_size, char, target='mono'):
@@ -331,3 +332,56 @@ class TestVerticalMetrics(FontTest):
self.assertEqual(hhea_table.ascent, 1900)
self.assertEqual(hhea_table.lineGap, 0)
+
+class TestGlyphAreas(unittest.TestCase):
+ """Tests that glyph areas between weights have the right ratios."""
+
+ def setUp(self):
+ """Determine which glyphs are intentionally unchanged."""
+
+ self.unchanged = set()
+ pen = self.pen = GlyphAreaPen()
+ thin, bold = self.getFonts(self.masters[1], "Roboto", "Thin", "Bold")
+ for glyph in thin:
+ glyph.draw(pen)
+ thin_area = pen.unload()
+ bold[glyph.name].draw(pen)
+ bold_area = pen.unload()
+ if thin_area == bold_area:
+ if thin_area:
+ self.unchanged.add(glyph.name)
+ else:
+ assert thin_area and bold_area
+
+ def getFonts(self, fonts, family, *weights):
+ """Extract fonts of certain family and weights from given font list."""
+
+ fonts = dict((f.info.styleName, f) for f in fonts
+ if f.info.familyName == family)
+ return [fonts[w] for w in weights]
+
+ def test_output(self):
+ """Test that only empty or intentionally unchanged glyphs are unchanged.
+ """
+
+ pen = self.pen
+ thin, regular, bold = self.getFonts(
+ self.loaded_fonts[1], "Roboto", "Thin", "Regular", "Bold")
+ regular_areas = {}
+ for glyph in regular:
+ glyph.draw(pen)
+ regular_areas[glyph.name] = pen.unload()
+
+ for other in [thin, bold]:
+ for name, regular_area in regular_areas.iteritems():
+ other[name].draw(pen)
+ other_area = pen.unload()
+ if not regular_area: # glyph probably contains only components
+ self.assertFalse(other_area)
+ continue
+ unchanged = regular_area == other_area
+ if unchanged:
+ msg = name + " has not changed, but should have."
+ else:
+ msg = name + " has changed, but should not have."
+ self.assertEqual(unchanged, name in self.unchanged, msg)
diff --git a/scripts/glyph_area_pen.py b/scripts/glyph_area_pen.py
new file mode 100644
index 0000000..1251b6a
--- /dev/null
+++ b/scripts/glyph_area_pen.py
@@ -0,0 +1,77 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from fontTools.pens.basePen import AbstractPen
+
+
+class GlyphAreaPen(AbstractPen):
+ """Pen used for calculating the area of the contours in a glyph."""
+
+ def __init__(self):
+ self.area = 0
+
+ def unload(self):
+ """Return and then reset the calculated area."""
+
+ area = self.area
+ self.area = 0
+ return area
+
+ def moveTo(self, pt):
+ """Remember the first point in this contour, in case it's closed. Also
+ set the initial value for p0 in this contour, which will always refer to
+ the most recent point.
+ """
+
+ self.first = pt
+ self.p0 = pt
+
+ def lineTo(self, pt):
+ """Add the signed area beneath the line from the latest point to this
+ one. Signed areas cancel each other based on the horizontal direction of
+ the line.
+ """
+
+ (x0, y0), (x1, y1) = self.p0, pt
+ self.area += (x1 - x0) * (y1 + y0) / 2.0
+ self.p0 = pt
+
+ def curveTo(self, *points):
+ """Add the signed area of this cubic curve.
+ https://github.com/Pomax/bezierinfo/issues/44
+ """
+
+ p1, p2, p3 = points
+ x0, y0 = self.p0
+ x1, y1 = p1[0] - x0, p1[1] - y0
+ x2, y2 = p2[0] - x0, p2[1] - y0
+ x3, y3 = p3[0] - x0, p3[1] - y0
+ self.area += (
+ x1 * ( - y2 - y3) +
+ x2 * (y1 - 2 * y3) +
+ x3 * (y1 + 2 * y2 )
+ ) * 3.0 / 20
+ self.lineTo(p3)
+
+ def closePath(self):
+ """Add the area beneath this contour's closing line."""
+ self.lineTo(self.first)
+
+ def endPath(self):
+ pass
+
+ def addComponent(self, glyphName, transformation):
+ """Don't count components towards the area, for now."""
+ pass
diff --git a/scripts/run_general_tests.py b/scripts/run_general_tests.py
index b91e54a..297c37c 100755
--- a/scripts/run_general_tests.py
+++ b/scripts/run_general_tests.py
@@ -67,6 +67,12 @@ class TestVerticalMetrics(common_tests.TestVerticalMetrics):
test_ymin_ymax = None
test_hhea_table_metrics = None
+
+class TestGlyphAreas(common_tests.TestGlyphAreas):
+ loaded_fonts = UFOS
+ masters = UFO_MASTERS
+
+
if __name__ == '__main__':
unittest.main()