1*e1fe3e4aSElliott Hughesfrom fontTools.cffLib import PrivateDict 2*e1fe3e4aSElliott Hughesfrom fontTools.cffLib.specializer import stringToProgram 3*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import getXML, parseXML 4*e1fe3e4aSElliott Hughesfrom fontTools.misc.psCharStrings import ( 5*e1fe3e4aSElliott Hughes T2CharString, 6*e1fe3e4aSElliott Hughes encodeFloat, 7*e1fe3e4aSElliott Hughes encodeFixed, 8*e1fe3e4aSElliott Hughes read_fixed1616, 9*e1fe3e4aSElliott Hughes read_realNumber, 10*e1fe3e4aSElliott Hughes) 11*e1fe3e4aSElliott Hughesfrom fontTools.pens.recordingPen import RecordingPen 12*e1fe3e4aSElliott Hughesimport unittest 13*e1fe3e4aSElliott Hughes 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughesdef hexenc(s): 16*e1fe3e4aSElliott Hughes return " ".join("%02x" % x for x in s) 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes 19*e1fe3e4aSElliott Hughesclass T2CharStringTest(unittest.TestCase): 20*e1fe3e4aSElliott Hughes @classmethod 21*e1fe3e4aSElliott Hughes def stringToT2CharString(cls, string): 22*e1fe3e4aSElliott Hughes return T2CharString(program=stringToProgram(string), private=PrivateDict()) 23*e1fe3e4aSElliott Hughes 24*e1fe3e4aSElliott Hughes def test_calcBounds_empty(self): 25*e1fe3e4aSElliott Hughes cs = self.stringToT2CharString("endchar") 26*e1fe3e4aSElliott Hughes bounds = cs.calcBounds(None) 27*e1fe3e4aSElliott Hughes self.assertEqual(bounds, None) 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes def test_calcBounds_line(self): 30*e1fe3e4aSElliott Hughes cs = self.stringToT2CharString( 31*e1fe3e4aSElliott Hughes "100 100 rmoveto 40 10 rlineto -20 50 rlineto endchar" 32*e1fe3e4aSElliott Hughes ) 33*e1fe3e4aSElliott Hughes bounds = cs.calcBounds(None) 34*e1fe3e4aSElliott Hughes self.assertEqual(bounds, (100, 100, 140, 160)) 35*e1fe3e4aSElliott Hughes 36*e1fe3e4aSElliott Hughes def test_calcBounds_curve(self): 37*e1fe3e4aSElliott Hughes cs = self.stringToT2CharString( 38*e1fe3e4aSElliott Hughes "100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto endchar" 39*e1fe3e4aSElliott Hughes ) 40*e1fe3e4aSElliott Hughes bounds = cs.calcBounds(None) 41*e1fe3e4aSElliott Hughes self.assertEqual(bounds, (91.90524980688875, -12.5, 208.09475019311125, 100)) 42*e1fe3e4aSElliott Hughes 43*e1fe3e4aSElliott Hughes def test_charstring_bytecode_optimization(self): 44*e1fe3e4aSElliott Hughes cs = self.stringToT2CharString( 45*e1fe3e4aSElliott Hughes "100.0 100 rmoveto -50.0 -150 200.5 0.0 -50 150 rrcurveto endchar" 46*e1fe3e4aSElliott Hughes ) 47*e1fe3e4aSElliott Hughes cs.isCFF2 = False 48*e1fe3e4aSElliott Hughes cs.private._isCFF2 = False 49*e1fe3e4aSElliott Hughes cs.compile() 50*e1fe3e4aSElliott Hughes cs.decompile() 51*e1fe3e4aSElliott Hughes self.assertEqual( 52*e1fe3e4aSElliott Hughes cs.program, 53*e1fe3e4aSElliott Hughes [ 54*e1fe3e4aSElliott Hughes 100, 55*e1fe3e4aSElliott Hughes 100, 56*e1fe3e4aSElliott Hughes "rmoveto", 57*e1fe3e4aSElliott Hughes -50, 58*e1fe3e4aSElliott Hughes -150, 59*e1fe3e4aSElliott Hughes 200.5, 60*e1fe3e4aSElliott Hughes 0, 61*e1fe3e4aSElliott Hughes -50, 62*e1fe3e4aSElliott Hughes 150, 63*e1fe3e4aSElliott Hughes "rrcurveto", 64*e1fe3e4aSElliott Hughes "endchar", 65*e1fe3e4aSElliott Hughes ], 66*e1fe3e4aSElliott Hughes ) 67*e1fe3e4aSElliott Hughes 68*e1fe3e4aSElliott Hughes cs2 = self.stringToT2CharString( 69*e1fe3e4aSElliott Hughes "100.0 rmoveto -50.0 -150 200.5 0.0 -50 150 rrcurveto" 70*e1fe3e4aSElliott Hughes ) 71*e1fe3e4aSElliott Hughes cs2.isCFF2 = True 72*e1fe3e4aSElliott Hughes cs2.private._isCFF2 = True 73*e1fe3e4aSElliott Hughes cs2.compile(isCFF2=True) 74*e1fe3e4aSElliott Hughes cs2.decompile() 75*e1fe3e4aSElliott Hughes self.assertEqual( 76*e1fe3e4aSElliott Hughes cs2.program, [100, "rmoveto", -50, -150, 200.5, 0, -50, 150, "rrcurveto"] 77*e1fe3e4aSElliott Hughes ) 78*e1fe3e4aSElliott Hughes 79*e1fe3e4aSElliott Hughes def test_encodeFloat(self): 80*e1fe3e4aSElliott Hughes testNums = [ 81*e1fe3e4aSElliott Hughes # value expected result 82*e1fe3e4aSElliott Hughes (-9.399999999999999, "1e e9 a4 ff"), # -9.4 83*e1fe3e4aSElliott Hughes (9.399999999999999999, "1e 9a 4f"), # 9.4 84*e1fe3e4aSElliott Hughes (456.8, "1e 45 6a 8f"), # 456.8 85*e1fe3e4aSElliott Hughes (0.0, "1e 0f"), # 0 86*e1fe3e4aSElliott Hughes (-0.0, "1e 0f"), # 0 87*e1fe3e4aSElliott Hughes (1.0, "1e 1f"), # 1 88*e1fe3e4aSElliott Hughes (-1.0, "1e e1 ff"), # -1 89*e1fe3e4aSElliott Hughes (98765.37e2, "1e 98 76 53 7f"), # 9876537 90*e1fe3e4aSElliott Hughes (1234567890.0, "1e 1a 23 45 67 9b 09 ff"), # 1234567890 91*e1fe3e4aSElliott Hughes (9.876537e-4, "1e a0 00 98 76 53 7f"), # 9.876537e-24 92*e1fe3e4aSElliott Hughes (9.876537e4, "1e 98 76 5a 37 ff"), # 9.876537e+24 93*e1fe3e4aSElliott Hughes ] 94*e1fe3e4aSElliott Hughes 95*e1fe3e4aSElliott Hughes for sample in testNums: 96*e1fe3e4aSElliott Hughes encoded_result = encodeFloat(sample[0]) 97*e1fe3e4aSElliott Hughes 98*e1fe3e4aSElliott Hughes # check to see if we got the expected bytes 99*e1fe3e4aSElliott Hughes self.assertEqual(hexenc(encoded_result), sample[1]) 100*e1fe3e4aSElliott Hughes 101*e1fe3e4aSElliott Hughes # check to see if we get the same value by decoding the data 102*e1fe3e4aSElliott Hughes decoded_result = read_realNumber( 103*e1fe3e4aSElliott Hughes None, 104*e1fe3e4aSElliott Hughes None, 105*e1fe3e4aSElliott Hughes encoded_result, 106*e1fe3e4aSElliott Hughes 1, 107*e1fe3e4aSElliott Hughes ) 108*e1fe3e4aSElliott Hughes self.assertEqual(decoded_result[0], float("%.8g" % sample[0])) 109*e1fe3e4aSElliott Hughes # We limit to 8 digits of precision to match the implementation 110*e1fe3e4aSElliott Hughes # of encodeFloat. 111*e1fe3e4aSElliott Hughes 112*e1fe3e4aSElliott Hughes def test_encode_decode_fixed(self): 113*e1fe3e4aSElliott Hughes testNums = [ 114*e1fe3e4aSElliott Hughes # value expected hex expected float 115*e1fe3e4aSElliott Hughes (-9.399999999999999, "ff ff f6 99 9a", -9.3999939), 116*e1fe3e4aSElliott Hughes (-9.4, "ff ff f6 99 9a", -9.3999939), 117*e1fe3e4aSElliott Hughes (9.399999999999999999, "ff 00 09 66 66", 9.3999939), 118*e1fe3e4aSElliott Hughes (9.4, "ff 00 09 66 66", 9.3999939), 119*e1fe3e4aSElliott Hughes (456.8, "ff 01 c8 cc cd", 456.8000031), 120*e1fe3e4aSElliott Hughes (-456.8, "ff fe 37 33 33", -456.8000031), 121*e1fe3e4aSElliott Hughes ] 122*e1fe3e4aSElliott Hughes 123*e1fe3e4aSElliott Hughes for value, expected_hex, expected_float in testNums: 124*e1fe3e4aSElliott Hughes encoded_result = encodeFixed(value) 125*e1fe3e4aSElliott Hughes 126*e1fe3e4aSElliott Hughes # check to see if we got the expected bytes 127*e1fe3e4aSElliott Hughes self.assertEqual(hexenc(encoded_result), expected_hex) 128*e1fe3e4aSElliott Hughes 129*e1fe3e4aSElliott Hughes # check to see if we get the same value by decoding the data 130*e1fe3e4aSElliott Hughes decoded_result = read_fixed1616( 131*e1fe3e4aSElliott Hughes None, 132*e1fe3e4aSElliott Hughes None, 133*e1fe3e4aSElliott Hughes encoded_result, 134*e1fe3e4aSElliott Hughes 1, 135*e1fe3e4aSElliott Hughes ) 136*e1fe3e4aSElliott Hughes self.assertAlmostEqual(decoded_result[0], expected_float) 137*e1fe3e4aSElliott Hughes 138*e1fe3e4aSElliott Hughes def test_toXML(self): 139*e1fe3e4aSElliott Hughes program = [ 140*e1fe3e4aSElliott Hughes "107 53.4004 166.199 hstem", 141*e1fe3e4aSElliott Hughes "174.6 163.801 vstem", 142*e1fe3e4aSElliott Hughes "338.4 142.8 rmoveto", 143*e1fe3e4aSElliott Hughes "28 0 21.9 9 15.8 18 15.8 18 7.9 20.79959 0 23.6 rrcurveto", 144*e1fe3e4aSElliott Hughes "endchar", 145*e1fe3e4aSElliott Hughes ] 146*e1fe3e4aSElliott Hughes cs = self.stringToT2CharString(" ".join(program)) 147*e1fe3e4aSElliott Hughes 148*e1fe3e4aSElliott Hughes self.assertEqual(getXML(cs.toXML), program) 149*e1fe3e4aSElliott Hughes 150*e1fe3e4aSElliott Hughes def test_fromXML(self): 151*e1fe3e4aSElliott Hughes cs = T2CharString() 152*e1fe3e4aSElliott Hughes for name, attrs, content in parseXML( 153*e1fe3e4aSElliott Hughes [ 154*e1fe3e4aSElliott Hughes '<CharString name="period">' " 338.4 142.8 rmoveto", 155*e1fe3e4aSElliott Hughes " 28 0 21.9 9 15.8 18 15.8 18 7.9 20.79959 0 23.6 rrcurveto", 156*e1fe3e4aSElliott Hughes " endchar" "</CharString>", 157*e1fe3e4aSElliott Hughes ] 158*e1fe3e4aSElliott Hughes ): 159*e1fe3e4aSElliott Hughes cs.fromXML(name, attrs, content) 160*e1fe3e4aSElliott Hughes 161*e1fe3e4aSElliott Hughes expected_program = [ 162*e1fe3e4aSElliott Hughes 338.3999939, 163*e1fe3e4aSElliott Hughes 142.8000031, 164*e1fe3e4aSElliott Hughes "rmoveto", 165*e1fe3e4aSElliott Hughes 28, 166*e1fe3e4aSElliott Hughes 0, 167*e1fe3e4aSElliott Hughes 21.8999939, 168*e1fe3e4aSElliott Hughes 9, 169*e1fe3e4aSElliott Hughes 15.8000031, 170*e1fe3e4aSElliott Hughes 18, 171*e1fe3e4aSElliott Hughes 15.8000031, 172*e1fe3e4aSElliott Hughes 18, 173*e1fe3e4aSElliott Hughes 7.8999939, 174*e1fe3e4aSElliott Hughes 20.7995911, 175*e1fe3e4aSElliott Hughes 0, 176*e1fe3e4aSElliott Hughes 23.6000061, 177*e1fe3e4aSElliott Hughes "rrcurveto", 178*e1fe3e4aSElliott Hughes "endchar", 179*e1fe3e4aSElliott Hughes ] 180*e1fe3e4aSElliott Hughes 181*e1fe3e4aSElliott Hughes self.assertEqual(len(cs.program), len(expected_program)) 182*e1fe3e4aSElliott Hughes for arg, expected_arg in zip(cs.program, expected_program): 183*e1fe3e4aSElliott Hughes if isinstance(arg, str): 184*e1fe3e4aSElliott Hughes self.assertIsInstance(expected_arg, str) 185*e1fe3e4aSElliott Hughes self.assertEqual(arg, expected_arg) 186*e1fe3e4aSElliott Hughes else: 187*e1fe3e4aSElliott Hughes self.assertNotIsInstance(expected_arg, str) 188*e1fe3e4aSElliott Hughes self.assertAlmostEqual(arg, expected_arg) 189*e1fe3e4aSElliott Hughes 190*e1fe3e4aSElliott Hughes def test_pen_closePath(self): 191*e1fe3e4aSElliott Hughes # Test CFF2/T2 charstring: it does NOT end in "endchar" 192*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/2455 193*e1fe3e4aSElliott Hughes cs = self.stringToT2CharString( 194*e1fe3e4aSElliott Hughes "100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto" 195*e1fe3e4aSElliott Hughes ) 196*e1fe3e4aSElliott Hughes pen = RecordingPen() 197*e1fe3e4aSElliott Hughes cs.draw(pen) 198*e1fe3e4aSElliott Hughes self.assertEqual(pen.value[-1], ("closePath", ())) 199*e1fe3e4aSElliott Hughes 200*e1fe3e4aSElliott Hughes 201*e1fe3e4aSElliott Hughesif __name__ == "__main__": 202*e1fe3e4aSElliott Hughes import sys 203*e1fe3e4aSElliott Hughes 204*e1fe3e4aSElliott Hughes sys.exit(unittest.main()) 205