summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Godfrey-Kittle <jamesgk19@gmail.com>2015-09-01 17:01:07 -0700
committerJames Godfrey-Kittle <jamesgk19@gmail.com>2015-09-01 17:01:07 -0700
commitda5a7c35c3bc024415ac521caf36d61e6e7d36cf (patch)
treeff47fa90bc78e1b096adf37dde3a4156388805a7
parent6e08f23f346f60cd861c2115e6f780603bbc7ac3 (diff)
parenta527f8faa39de6957b093b5b3f0c4a128ac3b4b0 (diff)
Merge pull request #103 from jamesgk/add-tests
Add test to compare glyph areas between weights
-rw-r--r--scripts/common_tests.py62
-rw-r--r--scripts/glyph_area_pen.py77
-rwxr-xr-xscripts/run_general_tests.py18
3 files changed, 155 insertions, 2 deletions
diff --git a/scripts/common_tests.py b/scripts/common_tests.py
index f5a7793..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'):
@@ -42,15 +43,19 @@ def get_rendered_char_height(font_filename, font_size, char, target='mono'):
return face.glyph.bitmap.rows
-def load_fonts(patterns, expected_count=None):
+def load_fonts(patterns, expected_count=None, font_class=None):
"""Load all fonts specified in the patterns.
Also assert that the number of the fonts found is exactly the same as
expected_count."""
+
+ if font_class is None:
+ font_class = ttLib.TTFont
+
all_font_files = []
for pattern in patterns:
all_font_files += glob.glob(pattern)
- all_fonts = [ttLib.TTFont(font) for font in all_font_files]
+ all_fonts = [font_class(font) for font in all_font_files]
if expected_count:
assert len(all_font_files) == expected_count
return all_font_files, all_fonts
@@ -327,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 68ca269..297c37c 100755
--- a/scripts/run_general_tests.py
+++ b/scripts/run_general_tests.py
@@ -18,12 +18,24 @@
import unittest
+from robofab.world import OpenFont
+
import common_tests
FONTS = common_tests.load_fonts(
['hinted/*.ttf'],
expected_count=18)
+UFOS = common_tests.load_fonts(
+ ['out/RobotoUFO/*.ufo', 'out/RobotoCondensedUFO/*.ufo'],
+ expected_count=18,
+ font_class=OpenFont)
+
+UFO_MASTERS = common_tests.load_fonts(
+ ['src/v2/*.ufo'],
+ expected_count=3,
+ font_class=OpenFont)
+
class TestItalicAngle(common_tests.TestItalicAngle):
loaded_fonts = FONTS
@@ -55,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()