1from fontTools.pens.basePen import ( 2 AbstractPen, 3 BasePen, 4 decomposeSuperBezierSegment, 5 decomposeQuadraticSegment, 6) 7from fontTools.pens.pointPen import AbstractPointPen 8from fontTools.misc.loggingTools import CapturingLogHandler 9import unittest 10 11 12class _TestPen(BasePen): 13 def __init__(self): 14 BasePen.__init__(self, glyphSet={}) 15 self._commands = [] 16 17 def __repr__(self): 18 return " ".join(self._commands) 19 20 def getCurrentPoint(self): 21 return self._getCurrentPoint() 22 23 def _moveTo(self, pt): 24 self._commands.append("%s %s moveto" % (pt[0], pt[1])) 25 26 def _lineTo(self, pt): 27 self._commands.append("%s %s lineto" % (pt[0], pt[1])) 28 29 def _curveToOne(self, bcp1, bcp2, pt): 30 self._commands.append( 31 "%s %s %s %s %s %s curveto" 32 % (bcp1[0], bcp1[1], bcp2[0], bcp2[1], pt[0], pt[1]) 33 ) 34 35 def _closePath(self): 36 self._commands.append("closepath") 37 38 def _endPath(self): 39 self._commands.append("endpath") 40 41 42class _TestGlyph: 43 def draw(self, pen): 44 pen.moveTo((0.0, 0.0)) 45 pen.lineTo((0.0, 100.0)) 46 pen.curveTo((50.0, 75.0), (60.0, 50.0), (50.0, 25.0), (0.0, 0.0)) 47 pen.closePath() 48 49 50class BasePenTest(unittest.TestCase): 51 def test_moveTo(self): 52 pen = _TestPen() 53 pen.moveTo((0.5, -4.3)) 54 self.assertEqual("0.5 -4.3 moveto", repr(pen)) 55 self.assertEqual((0.5, -4.3), pen.getCurrentPoint()) 56 57 def test_lineTo(self): 58 pen = _TestPen() 59 pen.moveTo((4, 5)) 60 pen.lineTo((7, 8)) 61 self.assertEqual("4 5 moveto 7 8 lineto", repr(pen)) 62 self.assertEqual((7, 8), pen.getCurrentPoint()) 63 64 def test_curveTo_zeroPoints(self): 65 pen = _TestPen() 66 pen.moveTo((0.0, 0.0)) 67 self.assertRaises(AssertionError, pen.curveTo) 68 69 def test_curveTo_onePoint(self): 70 pen = _TestPen() 71 pen.moveTo((0.0, 0.0)) 72 pen.curveTo((1.0, 1.1)) 73 self.assertEqual("0.0 0.0 moveto 1.0 1.1 lineto", repr(pen)) 74 self.assertEqual((1.0, 1.1), pen.getCurrentPoint()) 75 76 def test_curveTo_twoPoints(self): 77 pen = _TestPen() 78 pen.moveTo((0.0, 0.0)) 79 pen.curveTo((6.0, 3.0), (3.0, 6.0)) 80 self.assertEqual("0.0 0.0 moveto 4.0 2.0 5.0 4.0 3.0 6.0 curveto", repr(pen)) 81 self.assertEqual((3.0, 6.0), pen.getCurrentPoint()) 82 83 def test_curveTo_manyPoints(self): 84 pen = _TestPen() 85 pen.moveTo((0.0, 0.0)) 86 pen.curveTo((1.0, 1.1), (2.0, 2.1), (3.0, 3.1), (4.0, 4.1)) 87 self.assertEqual( 88 "0.0 0.0 moveto " 89 "1.0 1.1 1.5 1.6 2.0 2.1 curveto " 90 "2.5 2.6 3.0 3.1 4.0 4.1 curveto", 91 repr(pen), 92 ) 93 self.assertEqual((4.0, 4.1), pen.getCurrentPoint()) 94 95 def test_qCurveTo_zeroPoints(self): 96 pen = _TestPen() 97 pen.moveTo((0.0, 0.0)) 98 self.assertRaises(AssertionError, pen.qCurveTo) 99 100 def test_qCurveTo_onePoint(self): 101 pen = _TestPen() 102 pen.moveTo((0.0, 0.0)) 103 pen.qCurveTo((77.7, 99.9)) 104 self.assertEqual("0.0 0.0 moveto 77.7 99.9 lineto", repr(pen)) 105 self.assertEqual((77.7, 99.9), pen.getCurrentPoint()) 106 107 def test_qCurveTo_manyPoints(self): 108 pen = _TestPen() 109 pen.moveTo((0.0, 0.0)) 110 pen.qCurveTo((6.0, 3.0), (3.0, 6.0)) 111 self.assertEqual("0.0 0.0 moveto 4.0 2.0 5.0 4.0 3.0 6.0 curveto", repr(pen)) 112 self.assertEqual((3.0, 6.0), pen.getCurrentPoint()) 113 114 def test_qCurveTo_onlyOffCurvePoints(self): 115 pen = _TestPen() 116 pen.moveTo((0.0, 0.0)) 117 pen.qCurveTo((6.0, -6.0), (12.0, 12.0), (18.0, -18.0), None) 118 self.assertEqual( 119 "0.0 0.0 moveto " 120 "12.0 -12.0 moveto " 121 "8.0 -8.0 7.0 -3.0 9.0 3.0 curveto " 122 "11.0 9.0 13.0 7.0 15.0 -3.0 curveto " 123 "17.0 -13.0 16.0 -16.0 12.0 -12.0 curveto", 124 repr(pen), 125 ) 126 self.assertEqual((12.0, -12.0), pen.getCurrentPoint()) 127 128 def test_closePath(self): 129 pen = _TestPen() 130 pen.lineTo((3, 4)) 131 pen.closePath() 132 self.assertEqual("3 4 lineto closepath", repr(pen)) 133 self.assertEqual(None, pen.getCurrentPoint()) 134 135 def test_endPath(self): 136 pen = _TestPen() 137 pen.lineTo((3, 4)) 138 pen.endPath() 139 self.assertEqual("3 4 lineto endpath", repr(pen)) 140 self.assertEqual(None, pen.getCurrentPoint()) 141 142 def test_addComponent(self): 143 pen = _TestPen() 144 pen.glyphSet["oslash"] = _TestGlyph() 145 pen.addComponent("oslash", (2, 3, 0.5, 2, -10, 0)) 146 self.assertEqual( 147 "-10.0 0.0 moveto " 148 "40.0 200.0 lineto " 149 "127.5 300.0 131.25 290.0 125.0 265.0 curveto " 150 "118.75 240.0 102.5 200.0 -10.0 0.0 curveto " 151 "closepath", 152 repr(pen), 153 ) 154 self.assertEqual(None, pen.getCurrentPoint()) 155 156 def test_addComponent_skip_missing(self): 157 pen = _TestPen() 158 with CapturingLogHandler(pen.log, "WARNING") as captor: 159 pen.addComponent("nonexistent", (1, 0, 0, 1, 0, 0)) 160 captor.assertRegex("glyph '.*' is missing from glyphSet; skipped") 161 162 163class DecomposeSegmentTest(unittest.TestCase): 164 def test_decomposeSuperBezierSegment(self): 165 decompose = decomposeSuperBezierSegment 166 self.assertRaises(AssertionError, decompose, []) 167 self.assertRaises(AssertionError, decompose, [(0, 0)]) 168 self.assertRaises(AssertionError, decompose, [(0, 0), (1, 1)]) 169 self.assertEqual( 170 [((0, 0), (1, 1), (2, 2))], decompose([(0, 0), (1, 1), (2, 2)]) 171 ) 172 self.assertEqual( 173 [((0, 0), (2, -2), (4, 0)), ((6, 2), (8, 8), (12, -12))], 174 decompose([(0, 0), (4, -4), (8, 8), (12, -12)]), 175 ) 176 177 def test_decomposeQuadraticSegment(self): 178 decompose = decomposeQuadraticSegment 179 self.assertRaises(AssertionError, decompose, []) 180 self.assertRaises(AssertionError, decompose, [(0, 0)]) 181 self.assertEqual([((0, 0), (4, 8))], decompose([(0, 0), (4, 8)])) 182 self.assertEqual( 183 [((0, 0), (2, 4)), ((4, 8), (9, -9))], decompose([(0, 0), (4, 8), (9, -9)]) 184 ) 185 self.assertEqual( 186 [((0, 0), (2.0, 4.0)), ((4, 8), (6.5, -0.5)), ((9, -9), (10, 10))], 187 decompose([(0, 0), (4, 8), (9, -9), (10, 10)]), 188 ) 189 190 191if __name__ == "__main__": 192 import sys 193 194 sys.exit(unittest.main()) 195