1*e1fe3e4aSElliott Hughes# Copyright 2016 Google Inc. All Rights Reserved. 2*e1fe3e4aSElliott Hughes# 3*e1fe3e4aSElliott Hughes# Licensed under the Apache License, Version 2.0 (the "License"); 4*e1fe3e4aSElliott Hughes# you may not use this file except in compliance with the License. 5*e1fe3e4aSElliott Hughes# You may obtain a copy of the License at 6*e1fe3e4aSElliott Hughes# 7*e1fe3e4aSElliott Hughes# http://www.apache.org/licenses/LICENSE-2.0 8*e1fe3e4aSElliott Hughes# 9*e1fe3e4aSElliott Hughes# Unless required by applicable law or agreed to in writing, software 10*e1fe3e4aSElliott Hughes# distributed under the License is distributed on an "AS IS" BASIS, 11*e1fe3e4aSElliott Hughes# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e1fe3e4aSElliott Hughes# See the License for the specific language governing permissions and 13*e1fe3e4aSElliott Hughes# limitations under the License. 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughesimport sys 16*e1fe3e4aSElliott Hughesimport unittest 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughesfrom fontTools.pens.qu2cuPen import Qu2CuPen 19*e1fe3e4aSElliott Hughesfrom fontTools.pens.recordingPen import RecordingPen 20*e1fe3e4aSElliott Hughesfrom textwrap import dedent 21*e1fe3e4aSElliott Hughesimport pytest 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughestry: 24*e1fe3e4aSElliott Hughes from .utils import CUBIC_GLYPHS, QUAD_GLYPHS 25*e1fe3e4aSElliott Hughes from .utils import DummyGlyph 26*e1fe3e4aSElliott Hughes from .utils import DummyPen 27*e1fe3e4aSElliott Hughesexcept ImportError as e: 28*e1fe3e4aSElliott Hughes pytest.skip(str(e), allow_module_level=True) 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott HughesMAX_ERR = 1.0 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughes 33*e1fe3e4aSElliott Hughesclass _TestPenMixin(object): 34*e1fe3e4aSElliott Hughes """Collection of tests that are shared by both the SegmentPen and the 35*e1fe3e4aSElliott Hughes PointPen test cases, plus some helper methods. 36*e1fe3e4aSElliott Hughes Note: We currently don't have a PointPen. 37*e1fe3e4aSElliott Hughes """ 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott Hughes maxDiff = None 40*e1fe3e4aSElliott Hughes 41*e1fe3e4aSElliott Hughes def diff(self, expected, actual): 42*e1fe3e4aSElliott Hughes import difflib 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughes expected = str(self.Glyph(expected)).splitlines(True) 45*e1fe3e4aSElliott Hughes actual = str(self.Glyph(actual)).splitlines(True) 46*e1fe3e4aSElliott Hughes diff = difflib.unified_diff( 47*e1fe3e4aSElliott Hughes expected, actual, fromfile="expected", tofile="actual" 48*e1fe3e4aSElliott Hughes ) 49*e1fe3e4aSElliott Hughes return "".join(diff) 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughes def convert_glyph(self, glyph, **kwargs): 52*e1fe3e4aSElliott Hughes # draw source glyph onto a new glyph using a Cu2Qu pen and return it 53*e1fe3e4aSElliott Hughes converted = self.Glyph() 54*e1fe3e4aSElliott Hughes pen = getattr(converted, self.pen_getter_name)() 55*e1fe3e4aSElliott Hughes cubicpen = self.Qu2CuPen(pen, MAX_ERR, all_cubic=True, **kwargs) 56*e1fe3e4aSElliott Hughes getattr(glyph, self.draw_method_name)(cubicpen) 57*e1fe3e4aSElliott Hughes return converted 58*e1fe3e4aSElliott Hughes 59*e1fe3e4aSElliott Hughes def expect_glyph(self, source, expected): 60*e1fe3e4aSElliott Hughes converted = self.convert_glyph(source) 61*e1fe3e4aSElliott Hughes self.assertNotEqual(converted, source) 62*e1fe3e4aSElliott Hughes if not converted.approx(expected): 63*e1fe3e4aSElliott Hughes print(self.diff(expected, converted)) 64*e1fe3e4aSElliott Hughes self.fail("converted glyph is different from expected") 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughes def test_convert_simple_glyph(self): 67*e1fe3e4aSElliott Hughes self.expect_glyph(QUAD_GLYPHS["a"], CUBIC_GLYPHS["a"]) 68*e1fe3e4aSElliott Hughes self.expect_glyph(QUAD_GLYPHS["A"], CUBIC_GLYPHS["A"]) 69*e1fe3e4aSElliott Hughes 70*e1fe3e4aSElliott Hughes def test_convert_composite_glyph(self): 71*e1fe3e4aSElliott Hughes source = CUBIC_GLYPHS["Aacute"] 72*e1fe3e4aSElliott Hughes converted = self.convert_glyph(source) 73*e1fe3e4aSElliott Hughes # components don't change after quadratic conversion 74*e1fe3e4aSElliott Hughes self.assertEqual(converted, source) 75*e1fe3e4aSElliott Hughes 76*e1fe3e4aSElliott Hughes def test_reverse_direction(self): 77*e1fe3e4aSElliott Hughes for name in ("a", "A", "Eacute"): 78*e1fe3e4aSElliott Hughes source = QUAD_GLYPHS[name] 79*e1fe3e4aSElliott Hughes normal_glyph = self.convert_glyph(source) 80*e1fe3e4aSElliott Hughes reversed_glyph = self.convert_glyph(source, reverse_direction=True) 81*e1fe3e4aSElliott Hughes 82*e1fe3e4aSElliott Hughes # the number of commands is the same, just their order is iverted 83*e1fe3e4aSElliott Hughes self.assertTrue(len(normal_glyph.outline), len(reversed_glyph.outline)) 84*e1fe3e4aSElliott Hughes self.assertNotEqual(normal_glyph, reversed_glyph) 85*e1fe3e4aSElliott Hughes 86*e1fe3e4aSElliott Hughes def test_stats(self): 87*e1fe3e4aSElliott Hughes stats = {} 88*e1fe3e4aSElliott Hughes for name in QUAD_GLYPHS.keys(): 89*e1fe3e4aSElliott Hughes source = QUAD_GLYPHS[name] 90*e1fe3e4aSElliott Hughes self.convert_glyph(source, stats=stats) 91*e1fe3e4aSElliott Hughes 92*e1fe3e4aSElliott Hughes self.assertTrue(stats) 93*e1fe3e4aSElliott Hughes self.assertTrue("2" in stats) 94*e1fe3e4aSElliott Hughes self.assertEqual(type(stats["2"]), int) 95*e1fe3e4aSElliott Hughes 96*e1fe3e4aSElliott Hughes def test_addComponent(self): 97*e1fe3e4aSElliott Hughes pen = self.Pen() 98*e1fe3e4aSElliott Hughes cubicpen = self.Qu2CuPen(pen, MAX_ERR) 99*e1fe3e4aSElliott Hughes cubicpen.addComponent("a", (1, 2, 3, 4, 5.0, 6.0)) 100*e1fe3e4aSElliott Hughes 101*e1fe3e4aSElliott Hughes # components are passed through without changes 102*e1fe3e4aSElliott Hughes self.assertEqual( 103*e1fe3e4aSElliott Hughes str(pen).splitlines(), 104*e1fe3e4aSElliott Hughes [ 105*e1fe3e4aSElliott Hughes "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))", 106*e1fe3e4aSElliott Hughes ], 107*e1fe3e4aSElliott Hughes ) 108*e1fe3e4aSElliott Hughes 109*e1fe3e4aSElliott Hughes 110*e1fe3e4aSElliott Hughesclass TestQu2CuPen(unittest.TestCase, _TestPenMixin): 111*e1fe3e4aSElliott Hughes def __init__(self, *args, **kwargs): 112*e1fe3e4aSElliott Hughes super(TestQu2CuPen, self).__init__(*args, **kwargs) 113*e1fe3e4aSElliott Hughes self.Glyph = DummyGlyph 114*e1fe3e4aSElliott Hughes self.Pen = DummyPen 115*e1fe3e4aSElliott Hughes self.Qu2CuPen = Qu2CuPen 116*e1fe3e4aSElliott Hughes self.pen_getter_name = "getPen" 117*e1fe3e4aSElliott Hughes self.draw_method_name = "draw" 118*e1fe3e4aSElliott Hughes 119*e1fe3e4aSElliott Hughes def test_qCurveTo_1_point(self): 120*e1fe3e4aSElliott Hughes pen = DummyPen() 121*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 122*e1fe3e4aSElliott Hughes cubicpen.moveTo((0, 0)) 123*e1fe3e4aSElliott Hughes cubicpen.qCurveTo((1, 1)) 124*e1fe3e4aSElliott Hughes cubicpen.closePath() 125*e1fe3e4aSElliott Hughes 126*e1fe3e4aSElliott Hughes self.assertEqual( 127*e1fe3e4aSElliott Hughes str(pen).splitlines(), 128*e1fe3e4aSElliott Hughes [ 129*e1fe3e4aSElliott Hughes "pen.moveTo((0, 0))", 130*e1fe3e4aSElliott Hughes "pen.qCurveTo((1, 1))", 131*e1fe3e4aSElliott Hughes "pen.closePath()", 132*e1fe3e4aSElliott Hughes ], 133*e1fe3e4aSElliott Hughes ) 134*e1fe3e4aSElliott Hughes 135*e1fe3e4aSElliott Hughes def test_qCurveTo_2_points(self): 136*e1fe3e4aSElliott Hughes pen = DummyPen() 137*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 138*e1fe3e4aSElliott Hughes cubicpen.moveTo((0, 0)) 139*e1fe3e4aSElliott Hughes cubicpen.qCurveTo((1, 1), (2, 2)) 140*e1fe3e4aSElliott Hughes cubicpen.closePath() 141*e1fe3e4aSElliott Hughes 142*e1fe3e4aSElliott Hughes self.assertEqual( 143*e1fe3e4aSElliott Hughes str(pen).splitlines(), 144*e1fe3e4aSElliott Hughes [ 145*e1fe3e4aSElliott Hughes "pen.moveTo((0, 0))", 146*e1fe3e4aSElliott Hughes "pen.qCurveTo((1, 1), (2, 2))", 147*e1fe3e4aSElliott Hughes "pen.closePath()", 148*e1fe3e4aSElliott Hughes ], 149*e1fe3e4aSElliott Hughes ) 150*e1fe3e4aSElliott Hughes 151*e1fe3e4aSElliott Hughes def test_qCurveTo_3_points_no_conversion(self): 152*e1fe3e4aSElliott Hughes pen = DummyPen() 153*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 154*e1fe3e4aSElliott Hughes cubicpen.moveTo((0, 0)) 155*e1fe3e4aSElliott Hughes cubicpen.qCurveTo((0, 3), (1, 3), (1, 0)) 156*e1fe3e4aSElliott Hughes cubicpen.closePath() 157*e1fe3e4aSElliott Hughes 158*e1fe3e4aSElliott Hughes self.assertEqual( 159*e1fe3e4aSElliott Hughes str(pen).splitlines(), 160*e1fe3e4aSElliott Hughes [ 161*e1fe3e4aSElliott Hughes "pen.moveTo((0, 0))", 162*e1fe3e4aSElliott Hughes "pen.qCurveTo((0, 3), (1, 3), (1, 0))", 163*e1fe3e4aSElliott Hughes "pen.closePath()", 164*e1fe3e4aSElliott Hughes ], 165*e1fe3e4aSElliott Hughes ) 166*e1fe3e4aSElliott Hughes 167*e1fe3e4aSElliott Hughes def test_qCurveTo_no_oncurve_points(self): 168*e1fe3e4aSElliott Hughes pen = DummyPen() 169*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 170*e1fe3e4aSElliott Hughes cubicpen.qCurveTo((0, 0), (1, 0), (1, 1), (0, 1), None) 171*e1fe3e4aSElliott Hughes cubicpen.closePath() 172*e1fe3e4aSElliott Hughes 173*e1fe3e4aSElliott Hughes self.assertEqual( 174*e1fe3e4aSElliott Hughes str(pen).splitlines(), 175*e1fe3e4aSElliott Hughes ["pen.qCurveTo((0, 0), (1, 0), (1, 1), (0, 1), None)", "pen.closePath()"], 176*e1fe3e4aSElliott Hughes ) 177*e1fe3e4aSElliott Hughes 178*e1fe3e4aSElliott Hughes def test_curveTo_1_point(self): 179*e1fe3e4aSElliott Hughes pen = DummyPen() 180*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 181*e1fe3e4aSElliott Hughes cubicpen.moveTo((0, 0)) 182*e1fe3e4aSElliott Hughes cubicpen.curveTo((1, 1)) 183*e1fe3e4aSElliott Hughes cubicpen.closePath() 184*e1fe3e4aSElliott Hughes 185*e1fe3e4aSElliott Hughes self.assertEqual( 186*e1fe3e4aSElliott Hughes str(pen).splitlines(), 187*e1fe3e4aSElliott Hughes [ 188*e1fe3e4aSElliott Hughes "pen.moveTo((0, 0))", 189*e1fe3e4aSElliott Hughes "pen.curveTo((1, 1))", 190*e1fe3e4aSElliott Hughes "pen.closePath()", 191*e1fe3e4aSElliott Hughes ], 192*e1fe3e4aSElliott Hughes ) 193*e1fe3e4aSElliott Hughes 194*e1fe3e4aSElliott Hughes def test_curveTo_2_points(self): 195*e1fe3e4aSElliott Hughes pen = DummyPen() 196*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 197*e1fe3e4aSElliott Hughes cubicpen.moveTo((0, 0)) 198*e1fe3e4aSElliott Hughes cubicpen.curveTo((1, 1), (2, 2)) 199*e1fe3e4aSElliott Hughes cubicpen.closePath() 200*e1fe3e4aSElliott Hughes 201*e1fe3e4aSElliott Hughes self.assertEqual( 202*e1fe3e4aSElliott Hughes str(pen).splitlines(), 203*e1fe3e4aSElliott Hughes [ 204*e1fe3e4aSElliott Hughes "pen.moveTo((0, 0))", 205*e1fe3e4aSElliott Hughes "pen.curveTo((1, 1), (2, 2))", 206*e1fe3e4aSElliott Hughes "pen.closePath()", 207*e1fe3e4aSElliott Hughes ], 208*e1fe3e4aSElliott Hughes ) 209*e1fe3e4aSElliott Hughes 210*e1fe3e4aSElliott Hughes def test_curveTo_3_points(self): 211*e1fe3e4aSElliott Hughes pen = DummyPen() 212*e1fe3e4aSElliott Hughes cubicpen = Qu2CuPen(pen, MAX_ERR) 213*e1fe3e4aSElliott Hughes cubicpen.moveTo((0, 0)) 214*e1fe3e4aSElliott Hughes cubicpen.curveTo((1, 1), (2, 2), (3, 3)) 215*e1fe3e4aSElliott Hughes cubicpen.closePath() 216*e1fe3e4aSElliott Hughes 217*e1fe3e4aSElliott Hughes self.assertEqual( 218*e1fe3e4aSElliott Hughes str(pen).splitlines(), 219*e1fe3e4aSElliott Hughes [ 220*e1fe3e4aSElliott Hughes "pen.moveTo((0, 0))", 221*e1fe3e4aSElliott Hughes "pen.curveTo((1, 1), (2, 2), (3, 3))", 222*e1fe3e4aSElliott Hughes "pen.closePath()", 223*e1fe3e4aSElliott Hughes ], 224*e1fe3e4aSElliott Hughes ) 225*e1fe3e4aSElliott Hughes 226*e1fe3e4aSElliott Hughes def test_all_cubic(self): 227*e1fe3e4aSElliott Hughes inPen = RecordingPen() 228*e1fe3e4aSElliott Hughes inPen.value = [ 229*e1fe3e4aSElliott Hughes ("moveTo", ((1204, 347),)), 230*e1fe3e4aSElliott Hughes ("qCurveTo", ((1255, 347), (1323, 433), (1323, 467))), 231*e1fe3e4aSElliott Hughes ("qCurveTo", ((1323, 478), (1310, 492), (1302, 492))), 232*e1fe3e4aSElliott Hughes ("qCurveTo", ((1295, 492), (1289, 484))), 233*e1fe3e4aSElliott Hughes ("lineTo", ((1272, 461),)), 234*e1fe3e4aSElliott Hughes ("qCurveTo", ((1256, 439), (1221, 416), (1200, 416))), 235*e1fe3e4aSElliott Hughes ("qCurveTo", ((1181, 416), (1141, 440), (1141, 462))), 236*e1fe3e4aSElliott Hughes ("qCurveTo", ((1141, 484), (1190, 565), (1190, 594))), 237*e1fe3e4aSElliott Hughes ("qCurveTo", ((1190, 607), (1181, 634), (1168, 634))), 238*e1fe3e4aSElliott Hughes ("qCurveTo", ((1149, 634), (1146, 583), (1081, 496), (1081, 463))), 239*e1fe3e4aSElliott Hughes ("qCurveTo", ((1081, 417), (1164, 347), (1204, 347))), 240*e1fe3e4aSElliott Hughes ("closePath", ()), 241*e1fe3e4aSElliott Hughes ] 242*e1fe3e4aSElliott Hughes 243*e1fe3e4aSElliott Hughes outPen = RecordingPen() 244*e1fe3e4aSElliott Hughes q2cPen = Qu2CuPen(outPen, 1.0, all_cubic=True) 245*e1fe3e4aSElliott Hughes inPen.replay(q2cPen) 246*e1fe3e4aSElliott Hughes 247*e1fe3e4aSElliott Hughes print(outPen.value) 248*e1fe3e4aSElliott Hughes 249*e1fe3e4aSElliott Hughes assert not any(typ == "qCurveTo" for typ, _ in outPen.value) 250*e1fe3e4aSElliott Hughes 251*e1fe3e4aSElliott Hughes 252*e1fe3e4aSElliott Hughesif __name__ == "__main__": 253*e1fe3e4aSElliott Hughes unittest.main() 254