1*e1fe3e4aSElliott Hughesfrom fontTools.misc.loggingTools import CapturingLogHandler 2*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import parseXML 3*e1fe3e4aSElliott Hughesfrom fontTools.misc.textTools import deHexStr, hexStr 4*e1fe3e4aSElliott Hughesfrom fontTools.misc.xmlWriter import XMLWriter 5*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables.TupleVariation import ( 6*e1fe3e4aSElliott Hughes log, 7*e1fe3e4aSElliott Hughes TupleVariation, 8*e1fe3e4aSElliott Hughes compileSharedTuples, 9*e1fe3e4aSElliott Hughes decompileSharedTuples, 10*e1fe3e4aSElliott Hughes compileTupleVariationStore, 11*e1fe3e4aSElliott Hughes decompileTupleVariationStore, 12*e1fe3e4aSElliott Hughes inferRegion_, 13*e1fe3e4aSElliott Hughes) 14*e1fe3e4aSElliott Hughesfrom io import BytesIO 15*e1fe3e4aSElliott Hughesimport random 16*e1fe3e4aSElliott Hughesimport unittest 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes 19*e1fe3e4aSElliott Hughesdef hexencode(s): 20*e1fe3e4aSElliott Hughes h = hexStr(s).upper() 21*e1fe3e4aSElliott Hughes return " ".join([h[i : i + 2] for i in range(0, len(h), 2)]) 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughes 24*e1fe3e4aSElliott HughesAXES = { 25*e1fe3e4aSElliott Hughes "wdth": (0.25, 0.375, 0.5), 26*e1fe3e4aSElliott Hughes "wght": (0.0, 1.0, 1.0), 27*e1fe3e4aSElliott Hughes "opsz": (-0.75, -0.75, 0.0), 28*e1fe3e4aSElliott Hughes} 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughes 31*e1fe3e4aSElliott Hughes# Shared tuples in the 'gvar' table of the Skia font, as printed 32*e1fe3e4aSElliott Hughes# in Apple's TrueType specification. 33*e1fe3e4aSElliott Hughes# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html 34*e1fe3e4aSElliott HughesSKIA_GVAR_SHARED_TUPLES_DATA = deHexStr( 35*e1fe3e4aSElliott Hughes "40 00 00 00 C0 00 00 00 00 00 40 00 00 00 C0 00 " 36*e1fe3e4aSElliott Hughes "C0 00 C0 00 40 00 C0 00 40 00 40 00 C0 00 40 00" 37*e1fe3e4aSElliott Hughes) 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott HughesSKIA_GVAR_SHARED_TUPLES = [ 40*e1fe3e4aSElliott Hughes {"wght": 1.0, "wdth": 0.0}, 41*e1fe3e4aSElliott Hughes {"wght": -1.0, "wdth": 0.0}, 42*e1fe3e4aSElliott Hughes {"wght": 0.0, "wdth": 1.0}, 43*e1fe3e4aSElliott Hughes {"wght": 0.0, "wdth": -1.0}, 44*e1fe3e4aSElliott Hughes {"wght": -1.0, "wdth": -1.0}, 45*e1fe3e4aSElliott Hughes {"wght": 1.0, "wdth": -1.0}, 46*e1fe3e4aSElliott Hughes {"wght": 1.0, "wdth": 1.0}, 47*e1fe3e4aSElliott Hughes {"wght": -1.0, "wdth": 1.0}, 48*e1fe3e4aSElliott Hughes] 49*e1fe3e4aSElliott Hughes 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughes# Tuple Variation Store of uppercase I in the Skia font, as printed in Apple's 52*e1fe3e4aSElliott Hughes# TrueType spec. The actual Skia font uses a different table for uppercase I 53*e1fe3e4aSElliott Hughes# than what is printed in Apple's spec, but we still want to make sure that 54*e1fe3e4aSElliott Hughes# we can parse the data as it appears in the specification. 55*e1fe3e4aSElliott Hughes# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html 56*e1fe3e4aSElliott HughesSKIA_GVAR_I_DATA = deHexStr( 57*e1fe3e4aSElliott Hughes "00 08 00 24 00 33 20 00 00 15 20 01 00 1B 20 02 " 58*e1fe3e4aSElliott Hughes "00 24 20 03 00 15 20 04 00 26 20 07 00 0D 20 06 " 59*e1fe3e4aSElliott Hughes "00 1A 20 05 00 40 01 01 01 81 80 43 FF 7E FF 7E " 60*e1fe3e4aSElliott Hughes "FF 7E FF 7E 00 81 45 01 01 01 03 01 04 01 04 01 " 61*e1fe3e4aSElliott Hughes "04 01 02 80 40 00 82 81 81 04 3A 5A 3E 43 20 81 " 62*e1fe3e4aSElliott Hughes "04 0E 40 15 45 7C 83 00 0D 9E F3 F2 F0 F0 F0 F0 " 63*e1fe3e4aSElliott Hughes "F3 9E A0 A1 A1 A1 9F 80 00 91 81 91 00 0D 0A 0A " 64*e1fe3e4aSElliott Hughes "09 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0B 80 00 15 81 " 65*e1fe3e4aSElliott Hughes "81 00 C4 89 00 C4 83 00 0D 80 99 98 96 96 96 96 " 66*e1fe3e4aSElliott Hughes "99 80 82 83 83 83 81 80 40 FF 18 81 81 04 E6 F9 " 67*e1fe3e4aSElliott Hughes "10 21 02 81 04 E8 E5 EB 4D DA 83 00 0D CE D3 D4 " 68*e1fe3e4aSElliott Hughes "D3 D3 D3 D5 D2 CE CC CD CD CD CD 80 00 A1 81 91 " 69*e1fe3e4aSElliott Hughes "00 0D 07 03 04 02 02 02 03 03 07 07 08 08 08 07 " 70*e1fe3e4aSElliott Hughes "80 00 09 81 81 00 28 40 00 A4 02 24 24 66 81 04 " 71*e1fe3e4aSElliott Hughes "08 FA FA FA 28 83 00 82 02 FF FF FF 83 02 01 01 " 72*e1fe3e4aSElliott Hughes "01 84 91 00 80 06 07 08 08 08 08 0A 07 80 03 FE " 73*e1fe3e4aSElliott Hughes "FF FF FF 81 00 08 81 82 02 EE EE EE 8B 6D 00" 74*e1fe3e4aSElliott Hughes) 75*e1fe3e4aSElliott Hughes 76*e1fe3e4aSElliott Hughes 77*e1fe3e4aSElliott Hughesclass TupleVariationTest(unittest.TestCase): 78*e1fe3e4aSElliott Hughes def __init__(self, methodName): 79*e1fe3e4aSElliott Hughes unittest.TestCase.__init__(self, methodName) 80*e1fe3e4aSElliott Hughes # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, 81*e1fe3e4aSElliott Hughes # and fires deprecation warnings if a program uses the old name. 82*e1fe3e4aSElliott Hughes if not hasattr(self, "assertRaisesRegex"): 83*e1fe3e4aSElliott Hughes self.assertRaisesRegex = self.assertRaisesRegexp 84*e1fe3e4aSElliott Hughes 85*e1fe3e4aSElliott Hughes def test_equal(self): 86*e1fe3e4aSElliott Hughes var1 = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0), (9, 8), (7, 6)]) 87*e1fe3e4aSElliott Hughes var2 = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0), (9, 8), (7, 6)]) 88*e1fe3e4aSElliott Hughes self.assertEqual(var1, var2) 89*e1fe3e4aSElliott Hughes 90*e1fe3e4aSElliott Hughes def test_equal_differentAxes(self): 91*e1fe3e4aSElliott Hughes var1 = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0), (9, 8), (7, 6)]) 92*e1fe3e4aSElliott Hughes var2 = TupleVariation({"wght": (0.7, 0.8, 0.9)}, [(0, 0), (9, 8), (7, 6)]) 93*e1fe3e4aSElliott Hughes self.assertNotEqual(var1, var2) 94*e1fe3e4aSElliott Hughes 95*e1fe3e4aSElliott Hughes def test_equal_differentCoordinates(self): 96*e1fe3e4aSElliott Hughes var1 = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0), (9, 8), (7, 6)]) 97*e1fe3e4aSElliott Hughes var2 = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0), (9, 8)]) 98*e1fe3e4aSElliott Hughes self.assertNotEqual(var1, var2) 99*e1fe3e4aSElliott Hughes 100*e1fe3e4aSElliott Hughes def test_hasImpact_someDeltasNotZero(self): 101*e1fe3e4aSElliott Hughes axes = {"wght": (0.0, 1.0, 1.0)} 102*e1fe3e4aSElliott Hughes var = TupleVariation(axes, [(0, 0), (9, 8), (7, 6)]) 103*e1fe3e4aSElliott Hughes self.assertTrue(var.hasImpact()) 104*e1fe3e4aSElliott Hughes 105*e1fe3e4aSElliott Hughes def test_hasImpact_allDeltasZero(self): 106*e1fe3e4aSElliott Hughes axes = {"wght": (0.0, 1.0, 1.0)} 107*e1fe3e4aSElliott Hughes var = TupleVariation(axes, [(0, 0), (0, 0), (0, 0)]) 108*e1fe3e4aSElliott Hughes self.assertTrue(var.hasImpact()) 109*e1fe3e4aSElliott Hughes 110*e1fe3e4aSElliott Hughes def test_hasImpact_allDeltasNone(self): 111*e1fe3e4aSElliott Hughes axes = {"wght": (0.0, 1.0, 1.0)} 112*e1fe3e4aSElliott Hughes var = TupleVariation(axes, [None, None, None]) 113*e1fe3e4aSElliott Hughes self.assertFalse(var.hasImpact()) 114*e1fe3e4aSElliott Hughes 115*e1fe3e4aSElliott Hughes def test_toXML_badDeltaFormat(self): 116*e1fe3e4aSElliott Hughes writer = XMLWriter(BytesIO()) 117*e1fe3e4aSElliott Hughes g = TupleVariation(AXES, ["String"]) 118*e1fe3e4aSElliott Hughes with CapturingLogHandler(log, "ERROR") as captor: 119*e1fe3e4aSElliott Hughes g.toXML(writer, ["wdth"]) 120*e1fe3e4aSElliott Hughes self.assertIn("bad delta format", [r.msg for r in captor.records]) 121*e1fe3e4aSElliott Hughes self.assertEqual( 122*e1fe3e4aSElliott Hughes [ 123*e1fe3e4aSElliott Hughes "<tuple>", 124*e1fe3e4aSElliott Hughes '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>', 125*e1fe3e4aSElliott Hughes "<!-- bad delta #0 -->", 126*e1fe3e4aSElliott Hughes "</tuple>", 127*e1fe3e4aSElliott Hughes ], 128*e1fe3e4aSElliott Hughes TupleVariationTest.xml_lines(writer), 129*e1fe3e4aSElliott Hughes ) 130*e1fe3e4aSElliott Hughes 131*e1fe3e4aSElliott Hughes def test_toXML_constants(self): 132*e1fe3e4aSElliott Hughes writer = XMLWriter(BytesIO()) 133*e1fe3e4aSElliott Hughes g = TupleVariation(AXES, [42, None, 23, 0, -17, None]) 134*e1fe3e4aSElliott Hughes g.toXML(writer, ["wdth", "wght", "opsz"]) 135*e1fe3e4aSElliott Hughes self.assertEqual( 136*e1fe3e4aSElliott Hughes [ 137*e1fe3e4aSElliott Hughes "<tuple>", 138*e1fe3e4aSElliott Hughes '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>', 139*e1fe3e4aSElliott Hughes '<coord axis="wght" value="1.0"/>', 140*e1fe3e4aSElliott Hughes '<coord axis="opsz" value="-0.75"/>', 141*e1fe3e4aSElliott Hughes '<delta cvt="0" value="42"/>', 142*e1fe3e4aSElliott Hughes '<delta cvt="2" value="23"/>', 143*e1fe3e4aSElliott Hughes '<delta cvt="3" value="0"/>', 144*e1fe3e4aSElliott Hughes '<delta cvt="4" value="-17"/>', 145*e1fe3e4aSElliott Hughes "</tuple>", 146*e1fe3e4aSElliott Hughes ], 147*e1fe3e4aSElliott Hughes TupleVariationTest.xml_lines(writer), 148*e1fe3e4aSElliott Hughes ) 149*e1fe3e4aSElliott Hughes 150*e1fe3e4aSElliott Hughes def test_toXML_points(self): 151*e1fe3e4aSElliott Hughes writer = XMLWriter(BytesIO()) 152*e1fe3e4aSElliott Hughes g = TupleVariation(AXES, [(9, 8), None, (7, 6), (0, 0), (-1, -2), None]) 153*e1fe3e4aSElliott Hughes g.toXML(writer, ["wdth", "wght", "opsz"]) 154*e1fe3e4aSElliott Hughes self.assertEqual( 155*e1fe3e4aSElliott Hughes [ 156*e1fe3e4aSElliott Hughes "<tuple>", 157*e1fe3e4aSElliott Hughes '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>', 158*e1fe3e4aSElliott Hughes '<coord axis="wght" value="1.0"/>', 159*e1fe3e4aSElliott Hughes '<coord axis="opsz" value="-0.75"/>', 160*e1fe3e4aSElliott Hughes '<delta pt="0" x="9" y="8"/>', 161*e1fe3e4aSElliott Hughes '<delta pt="2" x="7" y="6"/>', 162*e1fe3e4aSElliott Hughes '<delta pt="3" x="0" y="0"/>', 163*e1fe3e4aSElliott Hughes '<delta pt="4" x="-1" y="-2"/>', 164*e1fe3e4aSElliott Hughes "</tuple>", 165*e1fe3e4aSElliott Hughes ], 166*e1fe3e4aSElliott Hughes TupleVariationTest.xml_lines(writer), 167*e1fe3e4aSElliott Hughes ) 168*e1fe3e4aSElliott Hughes 169*e1fe3e4aSElliott Hughes def test_toXML_allDeltasNone(self): 170*e1fe3e4aSElliott Hughes writer = XMLWriter(BytesIO()) 171*e1fe3e4aSElliott Hughes axes = {"wght": (0.0, 1.0, 1.0)} 172*e1fe3e4aSElliott Hughes g = TupleVariation(axes, [None] * 5) 173*e1fe3e4aSElliott Hughes g.toXML(writer, ["wght", "wdth"]) 174*e1fe3e4aSElliott Hughes self.assertEqual( 175*e1fe3e4aSElliott Hughes [ 176*e1fe3e4aSElliott Hughes "<tuple>", 177*e1fe3e4aSElliott Hughes '<coord axis="wght" value="1.0"/>', 178*e1fe3e4aSElliott Hughes "<!-- no deltas -->", 179*e1fe3e4aSElliott Hughes "</tuple>", 180*e1fe3e4aSElliott Hughes ], 181*e1fe3e4aSElliott Hughes TupleVariationTest.xml_lines(writer), 182*e1fe3e4aSElliott Hughes ) 183*e1fe3e4aSElliott Hughes 184*e1fe3e4aSElliott Hughes def test_toXML_axes_floats(self): 185*e1fe3e4aSElliott Hughes writer = XMLWriter(BytesIO()) 186*e1fe3e4aSElliott Hughes axes = { 187*e1fe3e4aSElliott Hughes "wght": (0.0, 0.2999878, 0.7000122), 188*e1fe3e4aSElliott Hughes "wdth": (0.0, 0.4000244, 0.4000244), 189*e1fe3e4aSElliott Hughes } 190*e1fe3e4aSElliott Hughes g = TupleVariation(axes, [None] * 5) 191*e1fe3e4aSElliott Hughes g.toXML(writer, ["wght", "wdth"]) 192*e1fe3e4aSElliott Hughes self.assertEqual( 193*e1fe3e4aSElliott Hughes [ 194*e1fe3e4aSElliott Hughes '<coord axis="wght" min="0.0" value="0.3" max="0.7"/>', 195*e1fe3e4aSElliott Hughes '<coord axis="wdth" value="0.4"/>', 196*e1fe3e4aSElliott Hughes ], 197*e1fe3e4aSElliott Hughes TupleVariationTest.xml_lines(writer)[1:3], 198*e1fe3e4aSElliott Hughes ) 199*e1fe3e4aSElliott Hughes 200*e1fe3e4aSElliott Hughes def test_fromXML_badDeltaFormat(self): 201*e1fe3e4aSElliott Hughes g = TupleVariation({}, []) 202*e1fe3e4aSElliott Hughes with CapturingLogHandler(log, "WARNING") as captor: 203*e1fe3e4aSElliott Hughes for name, attrs, content in parseXML('<delta a="1" b="2"/>'): 204*e1fe3e4aSElliott Hughes g.fromXML(name, attrs, content) 205*e1fe3e4aSElliott Hughes self.assertIn("bad delta format: a, b", [r.msg for r in captor.records]) 206*e1fe3e4aSElliott Hughes 207*e1fe3e4aSElliott Hughes def test_fromXML_constants(self): 208*e1fe3e4aSElliott Hughes g = TupleVariation({}, [None] * 4) 209*e1fe3e4aSElliott Hughes for name, attrs, content in parseXML( 210*e1fe3e4aSElliott Hughes '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>' 211*e1fe3e4aSElliott Hughes '<coord axis="wght" value="1.0"/>' 212*e1fe3e4aSElliott Hughes '<coord axis="opsz" value="-0.75"/>' 213*e1fe3e4aSElliott Hughes '<delta cvt="1" value="42"/>' 214*e1fe3e4aSElliott Hughes '<delta cvt="2" value="-23"/>' 215*e1fe3e4aSElliott Hughes ): 216*e1fe3e4aSElliott Hughes g.fromXML(name, attrs, content) 217*e1fe3e4aSElliott Hughes self.assertEqual(AXES, g.axes) 218*e1fe3e4aSElliott Hughes self.assertEqual([None, 42, -23, None], g.coordinates) 219*e1fe3e4aSElliott Hughes 220*e1fe3e4aSElliott Hughes def test_fromXML_points(self): 221*e1fe3e4aSElliott Hughes g = TupleVariation({}, [None] * 4) 222*e1fe3e4aSElliott Hughes for name, attrs, content in parseXML( 223*e1fe3e4aSElliott Hughes '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>' 224*e1fe3e4aSElliott Hughes '<coord axis="wght" value="1.0"/>' 225*e1fe3e4aSElliott Hughes '<coord axis="opsz" value="-0.75"/>' 226*e1fe3e4aSElliott Hughes '<delta pt="1" x="33" y="44"/>' 227*e1fe3e4aSElliott Hughes '<delta pt="2" x="-2" y="170"/>' 228*e1fe3e4aSElliott Hughes ): 229*e1fe3e4aSElliott Hughes g.fromXML(name, attrs, content) 230*e1fe3e4aSElliott Hughes self.assertEqual(AXES, g.axes) 231*e1fe3e4aSElliott Hughes self.assertEqual([None, (33, 44), (-2, 170), None], g.coordinates) 232*e1fe3e4aSElliott Hughes 233*e1fe3e4aSElliott Hughes def test_fromXML_axes_floats(self): 234*e1fe3e4aSElliott Hughes g = TupleVariation({}, [None] * 4) 235*e1fe3e4aSElliott Hughes for name, attrs, content in parseXML( 236*e1fe3e4aSElliott Hughes '<coord axis="wght" min="0.0" value="0.3" max="0.7"/>' 237*e1fe3e4aSElliott Hughes '<coord axis="wdth" value="0.4"/>' 238*e1fe3e4aSElliott Hughes ): 239*e1fe3e4aSElliott Hughes g.fromXML(name, attrs, content) 240*e1fe3e4aSElliott Hughes 241*e1fe3e4aSElliott Hughes self.assertEqual(g.axes["wght"][0], 0) 242*e1fe3e4aSElliott Hughes self.assertAlmostEqual(g.axes["wght"][1], 0.2999878) 243*e1fe3e4aSElliott Hughes self.assertAlmostEqual(g.axes["wght"][2], 0.7000122) 244*e1fe3e4aSElliott Hughes 245*e1fe3e4aSElliott Hughes self.assertEqual(g.axes["wdth"][0], 0) 246*e1fe3e4aSElliott Hughes self.assertAlmostEqual(g.axes["wdth"][1], 0.4000244) 247*e1fe3e4aSElliott Hughes self.assertAlmostEqual(g.axes["wdth"][2], 0.4000244) 248*e1fe3e4aSElliott Hughes 249*e1fe3e4aSElliott Hughes def test_compile_sharedPeaks_nonIntermediate_sharedPoints(self): 250*e1fe3e4aSElliott Hughes var = TupleVariation( 251*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)}, [(7, 4), (8, 5), (9, 6)] 252*e1fe3e4aSElliott Hughes ) 253*e1fe3e4aSElliott Hughes axisTags = ["wght", "wdth"] 254*e1fe3e4aSElliott Hughes sharedPeakIndices = {var.compileCoord(axisTags): 0x77} 255*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags, sharedPeakIndices, pointData=b"") 256*e1fe3e4aSElliott Hughes # len(deltas)=8; flags=None; tupleIndex=0x77 257*e1fe3e4aSElliott Hughes # embeddedPeaks=[]; intermediateCoord=[] 258*e1fe3e4aSElliott Hughes self.assertEqual("00 08 00 77", hexencode(tup)) 259*e1fe3e4aSElliott Hughes self.assertEqual( 260*e1fe3e4aSElliott Hughes "02 07 08 09 " "02 04 05 06", # deltaX: [7, 8, 9] # deltaY: [4, 5, 6] 261*e1fe3e4aSElliott Hughes hexencode(deltas), 262*e1fe3e4aSElliott Hughes ) 263*e1fe3e4aSElliott Hughes 264*e1fe3e4aSElliott Hughes def test_compile_sharedPeaks_intermediate_sharedPoints(self): 265*e1fe3e4aSElliott Hughes var = TupleVariation( 266*e1fe3e4aSElliott Hughes {"wght": (0.3, 0.5, 0.7), "wdth": (0.1, 0.8, 0.9)}, [(7, 4), (8, 5), (9, 6)] 267*e1fe3e4aSElliott Hughes ) 268*e1fe3e4aSElliott Hughes axisTags = ["wght", "wdth"] 269*e1fe3e4aSElliott Hughes sharedPeakIndices = {var.compileCoord(axisTags): 0x77} 270*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags, sharedPeakIndices, pointData=b"") 271*e1fe3e4aSElliott Hughes # len(deltas)=8; flags=INTERMEDIATE_REGION; tupleIndex=0x77 272*e1fe3e4aSElliott Hughes # embeddedPeak=[]; intermediateCoord=[(0.3, 0.1), (0.7, 0.9)] 273*e1fe3e4aSElliott Hughes self.assertEqual("00 08 40 77 13 33 06 66 2C CD 39 9A", hexencode(tup)) 274*e1fe3e4aSElliott Hughes self.assertEqual( 275*e1fe3e4aSElliott Hughes "02 07 08 09 " "02 04 05 06", # deltaX: [7, 8, 9] # deltaY: [4, 5, 6] 276*e1fe3e4aSElliott Hughes hexencode(deltas), 277*e1fe3e4aSElliott Hughes ) 278*e1fe3e4aSElliott Hughes 279*e1fe3e4aSElliott Hughes def test_compile_sharedPeaks_nonIntermediate_privatePoints(self): 280*e1fe3e4aSElliott Hughes var = TupleVariation( 281*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)}, [(7, 4), (8, 5), (9, 6)] 282*e1fe3e4aSElliott Hughes ) 283*e1fe3e4aSElliott Hughes axisTags = ["wght", "wdth"] 284*e1fe3e4aSElliott Hughes sharedPeakIndices = {var.compileCoord(axisTags): 0x77} 285*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags, sharedPeakIndices) 286*e1fe3e4aSElliott Hughes # len(deltas)=9; flags=PRIVATE_POINT_NUMBERS; tupleIndex=0x77 287*e1fe3e4aSElliott Hughes # embeddedPeak=[]; intermediateCoord=[] 288*e1fe3e4aSElliott Hughes self.assertEqual("00 09 20 77", hexencode(tup)) 289*e1fe3e4aSElliott Hughes self.assertEqual( 290*e1fe3e4aSElliott Hughes "00 " # all points in glyph 291*e1fe3e4aSElliott Hughes "02 07 08 09 " # deltaX: [7, 8, 9] 292*e1fe3e4aSElliott Hughes "02 04 05 06", # deltaY: [4, 5, 6] 293*e1fe3e4aSElliott Hughes hexencode(deltas), 294*e1fe3e4aSElliott Hughes ) 295*e1fe3e4aSElliott Hughes 296*e1fe3e4aSElliott Hughes def test_compile_sharedPeaks_intermediate_privatePoints(self): 297*e1fe3e4aSElliott Hughes var = TupleVariation( 298*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 1.0), "wdth": (0.0, 0.8, 1.0)}, [(7, 4), (8, 5), (9, 6)] 299*e1fe3e4aSElliott Hughes ) 300*e1fe3e4aSElliott Hughes axisTags = ["wght", "wdth"] 301*e1fe3e4aSElliott Hughes sharedPeakIndices = {var.compileCoord(axisTags): 0x77} 302*e1fe3e4aSElliott Hughes tuple, deltas = var.compile(axisTags, sharedPeakIndices) 303*e1fe3e4aSElliott Hughes # len(deltas)=9; flags=PRIVATE_POINT_NUMBERS; tupleIndex=0x77 304*e1fe3e4aSElliott Hughes # embeddedPeak=[]; intermediateCoord=[(0.0, 0.0), (1.0, 1.0)] 305*e1fe3e4aSElliott Hughes self.assertEqual("00 09 60 77 00 00 00 00 40 00 40 00", hexencode(tuple)) 306*e1fe3e4aSElliott Hughes self.assertEqual( 307*e1fe3e4aSElliott Hughes "00 " # all points in glyph 308*e1fe3e4aSElliott Hughes "02 07 08 09 " # deltaX: [7, 8, 9] 309*e1fe3e4aSElliott Hughes "02 04 05 06", # deltaY: [4, 5, 6] 310*e1fe3e4aSElliott Hughes hexencode(deltas), 311*e1fe3e4aSElliott Hughes ) 312*e1fe3e4aSElliott Hughes 313*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_nonIntermediate_sharedPoints(self): 314*e1fe3e4aSElliott Hughes var = TupleVariation( 315*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)}, [(7, 4), (8, 5), (9, 6)] 316*e1fe3e4aSElliott Hughes ) 317*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b"") 318*e1fe3e4aSElliott Hughes # len(deltas)=8; flags=EMBEDDED_PEAK_TUPLE 319*e1fe3e4aSElliott Hughes # embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[] 320*e1fe3e4aSElliott Hughes self.assertEqual("00 08 80 00 20 00 33 33", hexencode(tup)) 321*e1fe3e4aSElliott Hughes self.assertEqual( 322*e1fe3e4aSElliott Hughes "02 07 08 09 " "02 04 05 06", # deltaX: [7, 8, 9] # deltaY: [4, 5, 6] 323*e1fe3e4aSElliott Hughes hexencode(deltas), 324*e1fe3e4aSElliott Hughes ) 325*e1fe3e4aSElliott Hughes 326*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_nonIntermediate_sharedConstants(self): 327*e1fe3e4aSElliott Hughes var = TupleVariation( 328*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)}, [3, 1, 4] 329*e1fe3e4aSElliott Hughes ) 330*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b"") 331*e1fe3e4aSElliott Hughes # len(deltas)=4; flags=EMBEDDED_PEAK_TUPLE 332*e1fe3e4aSElliott Hughes # embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[] 333*e1fe3e4aSElliott Hughes self.assertEqual("00 04 80 00 20 00 33 33", hexencode(tup)) 334*e1fe3e4aSElliott Hughes self.assertEqual("02 03 01 04", hexencode(deltas)) # delta: [3, 1, 4] 335*e1fe3e4aSElliott Hughes 336*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_intermediate_sharedPoints(self): 337*e1fe3e4aSElliott Hughes var = TupleVariation( 338*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 1.0), "wdth": (0.0, 0.8, 0.8)}, [(7, 4), (8, 5), (9, 6)] 339*e1fe3e4aSElliott Hughes ) 340*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b"") 341*e1fe3e4aSElliott Hughes # len(deltas)=8; flags=EMBEDDED_PEAK_TUPLE 342*e1fe3e4aSElliott Hughes # embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[(0.0, 0.0), (1.0, 0.8)] 343*e1fe3e4aSElliott Hughes self.assertEqual( 344*e1fe3e4aSElliott Hughes "00 08 C0 00 20 00 33 33 00 00 00 00 40 00 33 33", hexencode(tup) 345*e1fe3e4aSElliott Hughes ) 346*e1fe3e4aSElliott Hughes self.assertEqual( 347*e1fe3e4aSElliott Hughes "02 07 08 09 " "02 04 05 06", # deltaX: [7, 8, 9] # deltaY: [4, 5, 6] 348*e1fe3e4aSElliott Hughes hexencode(deltas), 349*e1fe3e4aSElliott Hughes ) 350*e1fe3e4aSElliott Hughes 351*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_nonIntermediate_privatePoints(self): 352*e1fe3e4aSElliott Hughes var = TupleVariation( 353*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)}, [(7, 4), (8, 5), (9, 6)] 354*e1fe3e4aSElliott Hughes ) 355*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"]) 356*e1fe3e4aSElliott Hughes # len(deltas)=9; flags=PRIVATE_POINT_NUMBERS|EMBEDDED_PEAK_TUPLE 357*e1fe3e4aSElliott Hughes # embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[] 358*e1fe3e4aSElliott Hughes self.assertEqual("00 09 A0 00 20 00 33 33", hexencode(tup)) 359*e1fe3e4aSElliott Hughes self.assertEqual( 360*e1fe3e4aSElliott Hughes "00 " # all points in glyph 361*e1fe3e4aSElliott Hughes "02 07 08 09 " # deltaX: [7, 8, 9] 362*e1fe3e4aSElliott Hughes "02 04 05 06", # deltaY: [4, 5, 6] 363*e1fe3e4aSElliott Hughes hexencode(deltas), 364*e1fe3e4aSElliott Hughes ) 365*e1fe3e4aSElliott Hughes 366*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_nonIntermediate_privateConstants(self): 367*e1fe3e4aSElliott Hughes var = TupleVariation( 368*e1fe3e4aSElliott Hughes {"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)}, [7, 8, 9] 369*e1fe3e4aSElliott Hughes ) 370*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"]) 371*e1fe3e4aSElliott Hughes # len(deltas)=5; flags=PRIVATE_POINT_NUMBERS|EMBEDDED_PEAK_TUPLE 372*e1fe3e4aSElliott Hughes # embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[] 373*e1fe3e4aSElliott Hughes self.assertEqual("00 05 A0 00 20 00 33 33", hexencode(tup)) 374*e1fe3e4aSElliott Hughes self.assertEqual( 375*e1fe3e4aSElliott Hughes "00 " "02 07 08 09", # all points in glyph # delta: [7, 8, 9] 376*e1fe3e4aSElliott Hughes hexencode(deltas), 377*e1fe3e4aSElliott Hughes ) 378*e1fe3e4aSElliott Hughes 379*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_intermediate_privatePoints(self): 380*e1fe3e4aSElliott Hughes var = TupleVariation( 381*e1fe3e4aSElliott Hughes {"wght": (0.4, 0.5, 0.6), "wdth": (0.7, 0.8, 0.9)}, [(7, 4), (8, 5), (9, 6)] 382*e1fe3e4aSElliott Hughes ) 383*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"]) 384*e1fe3e4aSElliott Hughes # len(deltas)=9; 385*e1fe3e4aSElliott Hughes # flags=PRIVATE_POINT_NUMBERS|INTERMEDIATE_REGION|EMBEDDED_PEAK_TUPLE 386*e1fe3e4aSElliott Hughes # embeddedPeak=(0.5, 0.8); intermediateCoord=[(0.4, 0.7), (0.6, 0.9)] 387*e1fe3e4aSElliott Hughes self.assertEqual( 388*e1fe3e4aSElliott Hughes "00 09 E0 00 20 00 33 33 19 9A 2C CD 26 66 39 9A", hexencode(tup) 389*e1fe3e4aSElliott Hughes ) 390*e1fe3e4aSElliott Hughes self.assertEqual( 391*e1fe3e4aSElliott Hughes "00 " # all points in glyph 392*e1fe3e4aSElliott Hughes "02 07 08 09 " # deltaX: [7, 8, 9] 393*e1fe3e4aSElliott Hughes "02 04 05 06", # deltaY: [4, 5, 6] 394*e1fe3e4aSElliott Hughes hexencode(deltas), 395*e1fe3e4aSElliott Hughes ) 396*e1fe3e4aSElliott Hughes 397*e1fe3e4aSElliott Hughes def test_compile_embeddedPeak_intermediate_privateConstants(self): 398*e1fe3e4aSElliott Hughes var = TupleVariation( 399*e1fe3e4aSElliott Hughes {"wght": (0.4, 0.5, 0.6), "wdth": (0.7, 0.8, 0.9)}, [7, 8, 9] 400*e1fe3e4aSElliott Hughes ) 401*e1fe3e4aSElliott Hughes tup, deltas = var.compile(axisTags=["wght", "wdth"]) 402*e1fe3e4aSElliott Hughes # len(deltas)=5; 403*e1fe3e4aSElliott Hughes # flags=PRIVATE_POINT_NUMBERS|INTERMEDIATE_REGION|EMBEDDED_PEAK_TUPLE 404*e1fe3e4aSElliott Hughes # embeddedPeak=(0.5, 0.8); intermediateCoord=[(0.4, 0.7), (0.6, 0.9)] 405*e1fe3e4aSElliott Hughes self.assertEqual( 406*e1fe3e4aSElliott Hughes "00 05 E0 00 20 00 33 33 19 9A 2C CD 26 66 39 9A", hexencode(tup) 407*e1fe3e4aSElliott Hughes ) 408*e1fe3e4aSElliott Hughes self.assertEqual( 409*e1fe3e4aSElliott Hughes "00 " "02 07 08 09", # all points in glyph # delta: [7, 8, 9] 410*e1fe3e4aSElliott Hughes hexencode(deltas), 411*e1fe3e4aSElliott Hughes ) 412*e1fe3e4aSElliott Hughes 413*e1fe3e4aSElliott Hughes def test_compileCoord(self): 414*e1fe3e4aSElliott Hughes var = TupleVariation( 415*e1fe3e4aSElliott Hughes {"wght": (-1.0, -1.0, -1.0), "wdth": (0.4, 0.5, 0.6)}, [None] * 4 416*e1fe3e4aSElliott Hughes ) 417*e1fe3e4aSElliott Hughes self.assertEqual("C0 00 20 00", hexencode(var.compileCoord(["wght", "wdth"]))) 418*e1fe3e4aSElliott Hughes self.assertEqual("20 00 C0 00", hexencode(var.compileCoord(["wdth", "wght"]))) 419*e1fe3e4aSElliott Hughes self.assertEqual("C0 00", hexencode(var.compileCoord(["wght"]))) 420*e1fe3e4aSElliott Hughes 421*e1fe3e4aSElliott Hughes def test_compileIntermediateCoord(self): 422*e1fe3e4aSElliott Hughes var = TupleVariation( 423*e1fe3e4aSElliott Hughes {"wght": (-1.0, -1.0, 0.0), "wdth": (0.4, 0.5, 0.6)}, [None] * 4 424*e1fe3e4aSElliott Hughes ) 425*e1fe3e4aSElliott Hughes self.assertEqual( 426*e1fe3e4aSElliott Hughes "C0 00 19 9A 00 00 26 66", 427*e1fe3e4aSElliott Hughes hexencode(var.compileIntermediateCoord(["wght", "wdth"])), 428*e1fe3e4aSElliott Hughes ) 429*e1fe3e4aSElliott Hughes self.assertEqual( 430*e1fe3e4aSElliott Hughes "19 9A C0 00 26 66 00 00", 431*e1fe3e4aSElliott Hughes hexencode(var.compileIntermediateCoord(["wdth", "wght"])), 432*e1fe3e4aSElliott Hughes ) 433*e1fe3e4aSElliott Hughes self.assertEqual(None, var.compileIntermediateCoord(["wght"])) 434*e1fe3e4aSElliott Hughes self.assertEqual( 435*e1fe3e4aSElliott Hughes "19 9A 26 66", hexencode(var.compileIntermediateCoord(["wdth"])) 436*e1fe3e4aSElliott Hughes ) 437*e1fe3e4aSElliott Hughes 438*e1fe3e4aSElliott Hughes def test_decompileCoord(self): 439*e1fe3e4aSElliott Hughes decompileCoord = TupleVariation.decompileCoord_ 440*e1fe3e4aSElliott Hughes data = deHexStr("DE AD C0 00 20 00 DE AD") 441*e1fe3e4aSElliott Hughes self.assertEqual( 442*e1fe3e4aSElliott Hughes ({"wght": -1.0, "wdth": 0.5}, 6), decompileCoord(["wght", "wdth"], data, 2) 443*e1fe3e4aSElliott Hughes ) 444*e1fe3e4aSElliott Hughes 445*e1fe3e4aSElliott Hughes def test_decompileCoord_roundTrip(self): 446*e1fe3e4aSElliott Hughes # Make sure we are not affected by https://github.com/fonttools/fonttools/issues/286 447*e1fe3e4aSElliott Hughes data = deHexStr("7F B9 80 35") 448*e1fe3e4aSElliott Hughes values, _ = TupleVariation.decompileCoord_(["wght", "wdth"], data, 0) 449*e1fe3e4aSElliott Hughes axisValues = {axis: (val, val, val) for axis, val in values.items()} 450*e1fe3e4aSElliott Hughes var = TupleVariation(axisValues, [None] * 4) 451*e1fe3e4aSElliott Hughes self.assertEqual("7F B9 80 35", hexencode(var.compileCoord(["wght", "wdth"]))) 452*e1fe3e4aSElliott Hughes 453*e1fe3e4aSElliott Hughes def test_compilePoints(self): 454*e1fe3e4aSElliott Hughes compilePoints = lambda p: TupleVariation.compilePoints(set(p)) 455*e1fe3e4aSElliott Hughes self.assertEqual("00", hexencode(compilePoints(set()))) # all points in glyph 456*e1fe3e4aSElliott Hughes self.assertEqual("01 00 07", hexencode(compilePoints([7]))) 457*e1fe3e4aSElliott Hughes self.assertEqual("01 80 FF FF", hexencode(compilePoints([65535]))) 458*e1fe3e4aSElliott Hughes self.assertEqual("02 01 09 06", hexencode(compilePoints([9, 15]))) 459*e1fe3e4aSElliott Hughes self.assertEqual( 460*e1fe3e4aSElliott Hughes "06 05 07 01 F7 02 01 F2", 461*e1fe3e4aSElliott Hughes hexencode(compilePoints([7, 8, 255, 257, 258, 500])), 462*e1fe3e4aSElliott Hughes ) 463*e1fe3e4aSElliott Hughes self.assertEqual("03 01 07 01 80 01 EC", hexencode(compilePoints([7, 8, 500]))) 464*e1fe3e4aSElliott Hughes self.assertEqual( 465*e1fe3e4aSElliott Hughes "04 01 07 01 81 BE E7 0C 0F", 466*e1fe3e4aSElliott Hughes hexencode(compilePoints([7, 8, 0xBEEF, 0xCAFE])), 467*e1fe3e4aSElliott Hughes ) 468*e1fe3e4aSElliott Hughes self.maxDiff = None 469*e1fe3e4aSElliott Hughes self.assertEqual( 470*e1fe3e4aSElliott Hughes "81 2C" 471*e1fe3e4aSElliott Hughes + " 7F 00" # 300 points (0x12c) in total 472*e1fe3e4aSElliott Hughes + (127 * " 01") 473*e1fe3e4aSElliott Hughes + " 7F" # first run, contains 128 points: [0 .. 127] 474*e1fe3e4aSElliott Hughes + (128 * " 01") 475*e1fe3e4aSElliott Hughes + " 2B" # second run, contains 128 points: [128 .. 255] 476*e1fe3e4aSElliott Hughes + (44 * " 01"), # third run, contains 44 points: [256 .. 299] 477*e1fe3e4aSElliott Hughes hexencode(compilePoints(range(300))), 478*e1fe3e4aSElliott Hughes ) 479*e1fe3e4aSElliott Hughes self.assertEqual( 480*e1fe3e4aSElliott Hughes "81 8F" 481*e1fe3e4aSElliott Hughes + " 7F 00" # 399 points (0x18f) in total 482*e1fe3e4aSElliott Hughes + (127 * " 01") 483*e1fe3e4aSElliott Hughes + " 7F" # first run, contains 128 points: [0 .. 127] 484*e1fe3e4aSElliott Hughes + (128 * " 01") 485*e1fe3e4aSElliott Hughes + " 7F" # second run, contains 128 points: [128 .. 255] 486*e1fe3e4aSElliott Hughes + (128 * " 01") 487*e1fe3e4aSElliott Hughes + " 0E" # third run, contains 128 points: [256 .. 383] 488*e1fe3e4aSElliott Hughes + (15 * " 01"), # fourth run, contains 15 points: [384 .. 398] 489*e1fe3e4aSElliott Hughes hexencode(compilePoints(range(399))), 490*e1fe3e4aSElliott Hughes ) 491*e1fe3e4aSElliott Hughes 492*e1fe3e4aSElliott Hughes def test_decompilePoints(self): 493*e1fe3e4aSElliott Hughes numPointsInGlyph = 65536 494*e1fe3e4aSElliott Hughes allPoints = list(range(numPointsInGlyph)) 495*e1fe3e4aSElliott Hughes 496*e1fe3e4aSElliott Hughes def decompilePoints(data, offset): 497*e1fe3e4aSElliott Hughes points, offset = TupleVariation.decompilePoints_( 498*e1fe3e4aSElliott Hughes numPointsInGlyph, deHexStr(data), offset, "gvar" 499*e1fe3e4aSElliott Hughes ) 500*e1fe3e4aSElliott Hughes # Conversion to list needed for Python 3. 501*e1fe3e4aSElliott Hughes return (list(points), offset) 502*e1fe3e4aSElliott Hughes 503*e1fe3e4aSElliott Hughes # all points in glyph 504*e1fe3e4aSElliott Hughes self.assertEqual((allPoints, 1), decompilePoints("00", 0)) 505*e1fe3e4aSElliott Hughes # all points in glyph (in overly verbose encoding, not explicitly prohibited by spec) 506*e1fe3e4aSElliott Hughes self.assertEqual((allPoints, 2), decompilePoints("80 00", 0)) 507*e1fe3e4aSElliott Hughes # 2 points; first run: [9, 9+6] 508*e1fe3e4aSElliott Hughes self.assertEqual(([9, 15], 4), decompilePoints("02 01 09 06", 0)) 509*e1fe3e4aSElliott Hughes # 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF) 510*e1fe3e4aSElliott Hughes self.assertEqual(([0xBEEF, 0xCAFE], 6), decompilePoints("02 81 BE EF 0C 0F", 0)) 511*e1fe3e4aSElliott Hughes # 1 point; first run: [7] 512*e1fe3e4aSElliott Hughes self.assertEqual(([7], 3), decompilePoints("01 00 07", 0)) 513*e1fe3e4aSElliott Hughes # 1 point; first run: [7] in overly verbose encoding 514*e1fe3e4aSElliott Hughes self.assertEqual(([7], 4), decompilePoints("01 80 00 07", 0)) 515*e1fe3e4aSElliott Hughes # 1 point; first run: [65535]; requires words to be treated as unsigned numbers 516*e1fe3e4aSElliott Hughes self.assertEqual(([65535], 4), decompilePoints("01 80 FF FF", 0)) 517*e1fe3e4aSElliott Hughes # 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2). 518*e1fe3e4aSElliott Hughes self.assertEqual( 519*e1fe3e4aSElliott Hughes ([7, 8, 263, 265], 7), decompilePoints("04 01 07 01 01 FF 02", 0) 520*e1fe3e4aSElliott Hughes ) 521*e1fe3e4aSElliott Hughes # combination of all encodings, preceded and followed by 4 bytes of unused data 522*e1fe3e4aSElliott Hughes data = "DE AD DE AD 04 01 07 01 81 BE E7 0C 0F DE AD DE AD" 523*e1fe3e4aSElliott Hughes self.assertEqual(([7, 8, 0xBEEF, 0xCAFE], 13), decompilePoints(data, 4)) 524*e1fe3e4aSElliott Hughes self.assertSetEqual( 525*e1fe3e4aSElliott Hughes set(range(300)), 526*e1fe3e4aSElliott Hughes set( 527*e1fe3e4aSElliott Hughes decompilePoints( 528*e1fe3e4aSElliott Hughes "81 2C" 529*e1fe3e4aSElliott Hughes + " 7F 00" # 300 points (0x12c) in total 530*e1fe3e4aSElliott Hughes + (127 * " 01") 531*e1fe3e4aSElliott Hughes + " 7F" # first run, contains 128 points: [0 .. 127] 532*e1fe3e4aSElliott Hughes + (128 * " 01") 533*e1fe3e4aSElliott Hughes + " AB" # second run, contains 128 points: [128 .. 255] 534*e1fe3e4aSElliott Hughes + (44 * " 00 01"), # third run, contains 44 points: [256 .. 299] 535*e1fe3e4aSElliott Hughes 0, 536*e1fe3e4aSElliott Hughes )[0] 537*e1fe3e4aSElliott Hughes ), 538*e1fe3e4aSElliott Hughes ) 539*e1fe3e4aSElliott Hughes self.assertSetEqual( 540*e1fe3e4aSElliott Hughes set(range(399)), 541*e1fe3e4aSElliott Hughes set( 542*e1fe3e4aSElliott Hughes decompilePoints( 543*e1fe3e4aSElliott Hughes "81 8F" 544*e1fe3e4aSElliott Hughes + " 7F 00" # 399 points (0x18f) in total 545*e1fe3e4aSElliott Hughes + (127 * " 01") 546*e1fe3e4aSElliott Hughes + " 7F" # first run, contains 128 points: [0 .. 127] 547*e1fe3e4aSElliott Hughes + (128 * " 01") 548*e1fe3e4aSElliott Hughes + " FF" # second run, contains 128 points: [128 .. 255] 549*e1fe3e4aSElliott Hughes + (128 * " 00 01") 550*e1fe3e4aSElliott Hughes + " 8E" # third run, contains 128 points: [256 .. 383] 551*e1fe3e4aSElliott Hughes + (15 * " 00 01"), # fourth run, contains 15 points: [384 .. 398] 552*e1fe3e4aSElliott Hughes 0, 553*e1fe3e4aSElliott Hughes )[0] 554*e1fe3e4aSElliott Hughes ), 555*e1fe3e4aSElliott Hughes ) 556*e1fe3e4aSElliott Hughes 557*e1fe3e4aSElliott Hughes def test_decompilePoints_shouldAcceptBadPointNumbers(self): 558*e1fe3e4aSElliott Hughes decompilePoints = TupleVariation.decompilePoints_ 559*e1fe3e4aSElliott Hughes # 2 points; first run: [3, 9]. 560*e1fe3e4aSElliott Hughes numPointsInGlyph = 8 561*e1fe3e4aSElliott Hughes with CapturingLogHandler(log, "WARNING") as captor: 562*e1fe3e4aSElliott Hughes decompilePoints(numPointsInGlyph, deHexStr("02 01 03 06"), 0, "cvar") 563*e1fe3e4aSElliott Hughes self.assertIn( 564*e1fe3e4aSElliott Hughes "point 9 out of range in 'cvar' table", [r.msg for r in captor.records] 565*e1fe3e4aSElliott Hughes ) 566*e1fe3e4aSElliott Hughes 567*e1fe3e4aSElliott Hughes def test_decompilePoints_roundTrip(self): 568*e1fe3e4aSElliott Hughes numPointsInGlyph = ( 569*e1fe3e4aSElliott Hughes 500 # greater than 255, so we also exercise code path for 16-bit encoding 570*e1fe3e4aSElliott Hughes ) 571*e1fe3e4aSElliott Hughes compile = lambda points: TupleVariation.compilePoints(points) 572*e1fe3e4aSElliott Hughes decompile = lambda data: set( 573*e1fe3e4aSElliott Hughes TupleVariation.decompilePoints_(numPointsInGlyph, data, 0, "gvar")[0] 574*e1fe3e4aSElliott Hughes ) 575*e1fe3e4aSElliott Hughes for i in range(50): 576*e1fe3e4aSElliott Hughes points = set(random.sample(range(numPointsInGlyph), 30)) 577*e1fe3e4aSElliott Hughes self.assertSetEqual( 578*e1fe3e4aSElliott Hughes points, 579*e1fe3e4aSElliott Hughes decompile(compile(points)), 580*e1fe3e4aSElliott Hughes "failed round-trip decompile/compilePoints; points=%s" % points, 581*e1fe3e4aSElliott Hughes ) 582*e1fe3e4aSElliott Hughes allPoints = set(range(numPointsInGlyph)) 583*e1fe3e4aSElliott Hughes self.assertSetEqual(allPoints, decompile(compile(allPoints))) 584*e1fe3e4aSElliott Hughes self.assertSetEqual(allPoints, decompile(compile(set()))) 585*e1fe3e4aSElliott Hughes 586*e1fe3e4aSElliott Hughes def test_compileDeltas_points(self): 587*e1fe3e4aSElliott Hughes var = TupleVariation({}, [None, (1, 0), (2, 0), None, (4, 0), None]) 588*e1fe3e4aSElliott Hughes # deltaX for points: [1, 2, 4]; deltaY for points: [0, 0, 0] 589*e1fe3e4aSElliott Hughes self.assertEqual("02 01 02 04 82", hexencode(var.compileDeltas())) 590*e1fe3e4aSElliott Hughes 591*e1fe3e4aSElliott Hughes def test_compileDeltas_constants(self): 592*e1fe3e4aSElliott Hughes var = TupleVariation({}, [None, 1, 2, None, 4, None]) 593*e1fe3e4aSElliott Hughes # delta for cvts: [1, 2, 4] 594*e1fe3e4aSElliott Hughes self.assertEqual("02 01 02 04", hexencode(var.compileDeltas())) 595*e1fe3e4aSElliott Hughes 596*e1fe3e4aSElliott Hughes def test_compileDeltaValues(self): 597*e1fe3e4aSElliott Hughes compileDeltaValues = lambda values: hexencode( 598*e1fe3e4aSElliott Hughes TupleVariation.compileDeltaValues_(values) 599*e1fe3e4aSElliott Hughes ) 600*e1fe3e4aSElliott Hughes # zeroes 601*e1fe3e4aSElliott Hughes self.assertEqual("80", compileDeltaValues([0])) 602*e1fe3e4aSElliott Hughes self.assertEqual("BF", compileDeltaValues([0] * 64)) 603*e1fe3e4aSElliott Hughes self.assertEqual("BF 80", compileDeltaValues([0] * 65)) 604*e1fe3e4aSElliott Hughes self.assertEqual("BF A3", compileDeltaValues([0] * 100)) 605*e1fe3e4aSElliott Hughes self.assertEqual("BF BF BF BF", compileDeltaValues([0] * 256)) 606*e1fe3e4aSElliott Hughes # bytes 607*e1fe3e4aSElliott Hughes self.assertEqual("00 01", compileDeltaValues([1])) 608*e1fe3e4aSElliott Hughes self.assertEqual( 609*e1fe3e4aSElliott Hughes "06 01 02 03 7F 80 FF FE", compileDeltaValues([1, 2, 3, 127, -128, -1, -2]) 610*e1fe3e4aSElliott Hughes ) 611*e1fe3e4aSElliott Hughes self.assertEqual("3F" + (64 * " 7F"), compileDeltaValues([127] * 64)) 612*e1fe3e4aSElliott Hughes self.assertEqual("3F" + (64 * " 7F") + " 00 7F", compileDeltaValues([127] * 65)) 613*e1fe3e4aSElliott Hughes # words 614*e1fe3e4aSElliott Hughes self.assertEqual("40 66 66", compileDeltaValues([0x6666])) 615*e1fe3e4aSElliott Hughes self.assertEqual( 616*e1fe3e4aSElliott Hughes "43 66 66 7F FF FF FF 80 00", 617*e1fe3e4aSElliott Hughes compileDeltaValues([0x6666, 32767, -1, -32768]), 618*e1fe3e4aSElliott Hughes ) 619*e1fe3e4aSElliott Hughes self.assertEqual("7F" + (64 * " 11 22"), compileDeltaValues([0x1122] * 64)) 620*e1fe3e4aSElliott Hughes self.assertEqual( 621*e1fe3e4aSElliott Hughes "7F" + (64 * " 11 22") + " 40 11 22", compileDeltaValues([0x1122] * 65) 622*e1fe3e4aSElliott Hughes ) 623*e1fe3e4aSElliott Hughes # bytes, zeroes, bytes: a single zero is more compact when encoded as part of the bytes run 624*e1fe3e4aSElliott Hughes self.assertEqual( 625*e1fe3e4aSElliott Hughes "04 7F 7F 00 7F 7F", compileDeltaValues([127, 127, 0, 127, 127]) 626*e1fe3e4aSElliott Hughes ) 627*e1fe3e4aSElliott Hughes self.assertEqual( 628*e1fe3e4aSElliott Hughes "01 7F 7F 81 01 7F 7F", compileDeltaValues([127, 127, 0, 0, 127, 127]) 629*e1fe3e4aSElliott Hughes ) 630*e1fe3e4aSElliott Hughes self.assertEqual( 631*e1fe3e4aSElliott Hughes "01 7F 7F 82 01 7F 7F", compileDeltaValues([127, 127, 0, 0, 0, 127, 127]) 632*e1fe3e4aSElliott Hughes ) 633*e1fe3e4aSElliott Hughes self.assertEqual( 634*e1fe3e4aSElliott Hughes "01 7F 7F 83 01 7F 7F", compileDeltaValues([127, 127, 0, 0, 0, 0, 127, 127]) 635*e1fe3e4aSElliott Hughes ) 636*e1fe3e4aSElliott Hughes # bytes, zeroes 637*e1fe3e4aSElliott Hughes self.assertEqual("01 01 00", compileDeltaValues([1, 0])) 638*e1fe3e4aSElliott Hughes self.assertEqual("00 01 81", compileDeltaValues([1, 0, 0])) 639*e1fe3e4aSElliott Hughes # words, bytes, words: a single byte is more compact when encoded as part of the words run 640*e1fe3e4aSElliott Hughes self.assertEqual( 641*e1fe3e4aSElliott Hughes "42 66 66 00 02 77 77", compileDeltaValues([0x6666, 2, 0x7777]) 642*e1fe3e4aSElliott Hughes ) 643*e1fe3e4aSElliott Hughes self.assertEqual( 644*e1fe3e4aSElliott Hughes "40 66 66 01 02 02 40 77 77", compileDeltaValues([0x6666, 2, 2, 0x7777]) 645*e1fe3e4aSElliott Hughes ) 646*e1fe3e4aSElliott Hughes # words, zeroes, words 647*e1fe3e4aSElliott Hughes self.assertEqual( 648*e1fe3e4aSElliott Hughes "40 66 66 80 40 77 77", compileDeltaValues([0x6666, 0, 0x7777]) 649*e1fe3e4aSElliott Hughes ) 650*e1fe3e4aSElliott Hughes self.assertEqual( 651*e1fe3e4aSElliott Hughes "40 66 66 81 40 77 77", compileDeltaValues([0x6666, 0, 0, 0x7777]) 652*e1fe3e4aSElliott Hughes ) 653*e1fe3e4aSElliott Hughes self.assertEqual( 654*e1fe3e4aSElliott Hughes "40 66 66 82 40 77 77", compileDeltaValues([0x6666, 0, 0, 0, 0x7777]) 655*e1fe3e4aSElliott Hughes ) 656*e1fe3e4aSElliott Hughes # words, zeroes, bytes 657*e1fe3e4aSElliott Hughes self.assertEqual( 658*e1fe3e4aSElliott Hughes "40 66 66 80 02 01 02 03", compileDeltaValues([0x6666, 0, 1, 2, 3]) 659*e1fe3e4aSElliott Hughes ) 660*e1fe3e4aSElliott Hughes self.assertEqual( 661*e1fe3e4aSElliott Hughes "40 66 66 81 02 01 02 03", compileDeltaValues([0x6666, 0, 0, 1, 2, 3]) 662*e1fe3e4aSElliott Hughes ) 663*e1fe3e4aSElliott Hughes self.assertEqual( 664*e1fe3e4aSElliott Hughes "40 66 66 82 02 01 02 03", compileDeltaValues([0x6666, 0, 0, 0, 1, 2, 3]) 665*e1fe3e4aSElliott Hughes ) 666*e1fe3e4aSElliott Hughes # words, zeroes 667*e1fe3e4aSElliott Hughes self.assertEqual("40 66 66 80", compileDeltaValues([0x6666, 0])) 668*e1fe3e4aSElliott Hughes self.assertEqual("40 66 66 81", compileDeltaValues([0x6666, 0, 0])) 669*e1fe3e4aSElliott Hughes 670*e1fe3e4aSElliott Hughes def test_decompileDeltas(self): 671*e1fe3e4aSElliott Hughes decompileDeltas = TupleVariation.decompileDeltas_ 672*e1fe3e4aSElliott Hughes # 83 = zero values (0x80), count = 4 (1 + 0x83 & 0x3F) 673*e1fe3e4aSElliott Hughes self.assertEqual(([0, 0, 0, 0], 1), decompileDeltas(4, deHexStr("83"), 0)) 674*e1fe3e4aSElliott Hughes # 41 01 02 FF FF = signed 16-bit values (0x40), count = 2 (1 + 0x41 & 0x3F) 675*e1fe3e4aSElliott Hughes self.assertEqual( 676*e1fe3e4aSElliott Hughes ([258, -1], 5), decompileDeltas(2, deHexStr("41 01 02 FF FF"), 0) 677*e1fe3e4aSElliott Hughes ) 678*e1fe3e4aSElliott Hughes # 01 81 07 = signed 8-bit values, count = 2 (1 + 0x01 & 0x3F) 679*e1fe3e4aSElliott Hughes self.assertEqual(([-127, 7], 3), decompileDeltas(2, deHexStr("01 81 07"), 0)) 680*e1fe3e4aSElliott Hughes # combination of all three encodings, preceded and followed by 4 bytes of unused data 681*e1fe3e4aSElliott Hughes data = deHexStr("DE AD BE EF 83 40 01 02 01 81 80 DE AD BE EF") 682*e1fe3e4aSElliott Hughes self.assertEqual( 683*e1fe3e4aSElliott Hughes ([0, 0, 0, 0, 258, -127, -128], 11), decompileDeltas(7, data, 4) 684*e1fe3e4aSElliott Hughes ) 685*e1fe3e4aSElliott Hughes 686*e1fe3e4aSElliott Hughes def test_decompileDeltas_roundTrip(self): 687*e1fe3e4aSElliott Hughes numDeltas = 30 688*e1fe3e4aSElliott Hughes compile = TupleVariation.compileDeltaValues_ 689*e1fe3e4aSElliott Hughes decompile = lambda data: TupleVariation.decompileDeltas_(numDeltas, data, 0)[0] 690*e1fe3e4aSElliott Hughes for i in range(50): 691*e1fe3e4aSElliott Hughes deltas = random.sample(range(-128, 127), 10) 692*e1fe3e4aSElliott Hughes deltas.extend(random.sample(range(-32768, 32767), 10)) 693*e1fe3e4aSElliott Hughes deltas.extend([0] * 10) 694*e1fe3e4aSElliott Hughes random.shuffle(deltas) 695*e1fe3e4aSElliott Hughes self.assertListEqual(deltas, decompile(compile(deltas))) 696*e1fe3e4aSElliott Hughes 697*e1fe3e4aSElliott Hughes def test_compileSharedTuples(self): 698*e1fe3e4aSElliott Hughes # Below, the peak coordinate {"wght": 1.0, "wdth": 0.8} appears 699*e1fe3e4aSElliott Hughes # three times (most frequent sorted first); {"wght": 1.0, "wdth": 0.5} 700*e1fe3e4aSElliott Hughes # and {"wght": 1.0, "wdth": 0.7} both appears two times (tie) and 701*e1fe3e4aSElliott Hughes # are sorted alphanumerically to ensure determinism. 702*e1fe3e4aSElliott Hughes # The peak coordinate {"wght": 1.0, "wdth": 0.9} appears only once 703*e1fe3e4aSElliott Hughes # and is thus ignored. 704*e1fe3e4aSElliott Hughes # Because the start and end of variation ranges is not encoded 705*e1fe3e4aSElliott Hughes # into the shared pool, they should get ignored. 706*e1fe3e4aSElliott Hughes deltas = [None] * 4 707*e1fe3e4aSElliott Hughes variations = [ 708*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.5, 0.7, 1.0)}, deltas), 709*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.2, 0.7, 1.0)}, deltas), 710*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.2, 0.8, 1.0)}, deltas), 711*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.3, 0.5, 1.0)}, deltas), 712*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.3, 0.8, 1.0)}, deltas), 713*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.3, 0.9, 1.0)}, deltas), 714*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.4, 0.8, 1.0)}, deltas), 715*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (0.5, 0.5, 1.0)}, deltas), 716*e1fe3e4aSElliott Hughes ] 717*e1fe3e4aSElliott Hughes result = compileSharedTuples(["wght", "wdth"], variations) 718*e1fe3e4aSElliott Hughes self.assertEqual( 719*e1fe3e4aSElliott Hughes [hexencode(c) for c in result], 720*e1fe3e4aSElliott Hughes ["40 00 33 33", "40 00 20 00", "40 00 2C CD"], 721*e1fe3e4aSElliott Hughes ) 722*e1fe3e4aSElliott Hughes 723*e1fe3e4aSElliott Hughes def test_decompileSharedTuples_Skia(self): 724*e1fe3e4aSElliott Hughes sharedTuples = decompileSharedTuples( 725*e1fe3e4aSElliott Hughes axisTags=["wght", "wdth"], 726*e1fe3e4aSElliott Hughes sharedTupleCount=8, 727*e1fe3e4aSElliott Hughes data=SKIA_GVAR_SHARED_TUPLES_DATA, 728*e1fe3e4aSElliott Hughes offset=0, 729*e1fe3e4aSElliott Hughes ) 730*e1fe3e4aSElliott Hughes self.assertEqual(sharedTuples, SKIA_GVAR_SHARED_TUPLES) 731*e1fe3e4aSElliott Hughes 732*e1fe3e4aSElliott Hughes def test_decompileSharedTuples_empty(self): 733*e1fe3e4aSElliott Hughes self.assertEqual(decompileSharedTuples(["wght"], 0, b"", 0), []) 734*e1fe3e4aSElliott Hughes 735*e1fe3e4aSElliott Hughes def test_compileTupleVariationStore_allVariationsRedundant(self): 736*e1fe3e4aSElliott Hughes axes = {"wght": (0.3, 0.4, 0.5), "opsz": (0.7, 0.8, 0.9)} 737*e1fe3e4aSElliott Hughes variations = [ 738*e1fe3e4aSElliott Hughes TupleVariation(axes, [None] * 4), 739*e1fe3e4aSElliott Hughes TupleVariation(axes, [None] * 4), 740*e1fe3e4aSElliott Hughes TupleVariation(axes, [None] * 4), 741*e1fe3e4aSElliott Hughes ] 742*e1fe3e4aSElliott Hughes self.assertEqual( 743*e1fe3e4aSElliott Hughes compileTupleVariationStore( 744*e1fe3e4aSElliott Hughes variations, 745*e1fe3e4aSElliott Hughes pointCount=8, 746*e1fe3e4aSElliott Hughes axisTags=["wght", "opsz"], 747*e1fe3e4aSElliott Hughes sharedTupleIndices={}, 748*e1fe3e4aSElliott Hughes ), 749*e1fe3e4aSElliott Hughes (0, b"", b""), 750*e1fe3e4aSElliott Hughes ) 751*e1fe3e4aSElliott Hughes 752*e1fe3e4aSElliott Hughes def test_compileTupleVariationStore_noVariations(self): 753*e1fe3e4aSElliott Hughes self.assertEqual( 754*e1fe3e4aSElliott Hughes compileTupleVariationStore( 755*e1fe3e4aSElliott Hughes variations=[], 756*e1fe3e4aSElliott Hughes pointCount=8, 757*e1fe3e4aSElliott Hughes axisTags=["wght", "opsz"], 758*e1fe3e4aSElliott Hughes sharedTupleIndices={}, 759*e1fe3e4aSElliott Hughes ), 760*e1fe3e4aSElliott Hughes (0, b"", b""), 761*e1fe3e4aSElliott Hughes ) 762*e1fe3e4aSElliott Hughes 763*e1fe3e4aSElliott Hughes def test_compileTupleVariationStore_roundTrip_cvar(self): 764*e1fe3e4aSElliott Hughes deltas = [1, 2, 3, 4] 765*e1fe3e4aSElliott Hughes variations = [ 766*e1fe3e4aSElliott Hughes TupleVariation({"wght": (0.5, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)}, deltas), 767*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)}, deltas), 768*e1fe3e4aSElliott Hughes ] 769*e1fe3e4aSElliott Hughes tupleVariationCount, tuples, data = compileTupleVariationStore( 770*e1fe3e4aSElliott Hughes variations, pointCount=4, axisTags=["wght", "wdth"], sharedTupleIndices={} 771*e1fe3e4aSElliott Hughes ) 772*e1fe3e4aSElliott Hughes self.assertEqual( 773*e1fe3e4aSElliott Hughes decompileTupleVariationStore( 774*e1fe3e4aSElliott Hughes "cvar", 775*e1fe3e4aSElliott Hughes ["wght", "wdth"], 776*e1fe3e4aSElliott Hughes tupleVariationCount, 777*e1fe3e4aSElliott Hughes pointCount=4, 778*e1fe3e4aSElliott Hughes sharedTuples={}, 779*e1fe3e4aSElliott Hughes data=(tuples + data), 780*e1fe3e4aSElliott Hughes pos=0, 781*e1fe3e4aSElliott Hughes dataPos=len(tuples), 782*e1fe3e4aSElliott Hughes ), 783*e1fe3e4aSElliott Hughes variations, 784*e1fe3e4aSElliott Hughes ) 785*e1fe3e4aSElliott Hughes 786*e1fe3e4aSElliott Hughes def test_compileTupleVariationStore_roundTrip_gvar(self): 787*e1fe3e4aSElliott Hughes deltas = [(1, 1), (2, 2), (3, 3), (4, 4)] 788*e1fe3e4aSElliott Hughes variations = [ 789*e1fe3e4aSElliott Hughes TupleVariation({"wght": (0.5, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)}, deltas), 790*e1fe3e4aSElliott Hughes TupleVariation({"wght": (1.0, 1.0, 1.0), "wdth": (1.0, 1.0, 1.0)}, deltas), 791*e1fe3e4aSElliott Hughes ] 792*e1fe3e4aSElliott Hughes tupleVariationCount, tuples, data = compileTupleVariationStore( 793*e1fe3e4aSElliott Hughes variations, pointCount=4, axisTags=["wght", "wdth"], sharedTupleIndices={} 794*e1fe3e4aSElliott Hughes ) 795*e1fe3e4aSElliott Hughes self.assertEqual( 796*e1fe3e4aSElliott Hughes decompileTupleVariationStore( 797*e1fe3e4aSElliott Hughes "gvar", 798*e1fe3e4aSElliott Hughes ["wght", "wdth"], 799*e1fe3e4aSElliott Hughes tupleVariationCount, 800*e1fe3e4aSElliott Hughes pointCount=4, 801*e1fe3e4aSElliott Hughes sharedTuples={}, 802*e1fe3e4aSElliott Hughes data=(tuples + data), 803*e1fe3e4aSElliott Hughes pos=0, 804*e1fe3e4aSElliott Hughes dataPos=len(tuples), 805*e1fe3e4aSElliott Hughes ), 806*e1fe3e4aSElliott Hughes variations, 807*e1fe3e4aSElliott Hughes ) 808*e1fe3e4aSElliott Hughes 809*e1fe3e4aSElliott Hughes def test_decompileTupleVariationStore_Skia_I(self): 810*e1fe3e4aSElliott Hughes tvar = decompileTupleVariationStore( 811*e1fe3e4aSElliott Hughes tableTag="gvar", 812*e1fe3e4aSElliott Hughes axisTags=["wght", "wdth"], 813*e1fe3e4aSElliott Hughes tupleVariationCount=8, 814*e1fe3e4aSElliott Hughes pointCount=18, 815*e1fe3e4aSElliott Hughes sharedTuples=SKIA_GVAR_SHARED_TUPLES, 816*e1fe3e4aSElliott Hughes data=SKIA_GVAR_I_DATA, 817*e1fe3e4aSElliott Hughes pos=4, 818*e1fe3e4aSElliott Hughes dataPos=36, 819*e1fe3e4aSElliott Hughes ) 820*e1fe3e4aSElliott Hughes self.assertEqual(len(tvar), 8) 821*e1fe3e4aSElliott Hughes self.assertEqual(tvar[0].axes, {"wght": (0.0, 1.0, 1.0)}) 822*e1fe3e4aSElliott Hughes self.assertEqual( 823*e1fe3e4aSElliott Hughes " ".join(["%d,%d" % c for c in tvar[0].coordinates]), 824*e1fe3e4aSElliott Hughes "257,0 -127,0 -128,58 -130,90 -130,62 -130,67 -130,32 -127,0 " 825*e1fe3e4aSElliott Hughes "257,0 259,14 260,64 260,21 260,69 258,124 0,0 130,0 0,0 0,0", 826*e1fe3e4aSElliott Hughes ) 827*e1fe3e4aSElliott Hughes 828*e1fe3e4aSElliott Hughes def test_decompileTupleVariationStore_empty(self): 829*e1fe3e4aSElliott Hughes self.assertEqual( 830*e1fe3e4aSElliott Hughes decompileTupleVariationStore( 831*e1fe3e4aSElliott Hughes tableTag="gvar", 832*e1fe3e4aSElliott Hughes axisTags=[], 833*e1fe3e4aSElliott Hughes tupleVariationCount=0, 834*e1fe3e4aSElliott Hughes pointCount=5, 835*e1fe3e4aSElliott Hughes sharedTuples=[], 836*e1fe3e4aSElliott Hughes data=b"", 837*e1fe3e4aSElliott Hughes pos=4, 838*e1fe3e4aSElliott Hughes dataPos=4, 839*e1fe3e4aSElliott Hughes ), 840*e1fe3e4aSElliott Hughes [], 841*e1fe3e4aSElliott Hughes ) 842*e1fe3e4aSElliott Hughes 843*e1fe3e4aSElliott Hughes def test_getTupleSize(self): 844*e1fe3e4aSElliott Hughes getTupleSize = TupleVariation.getTupleSize_ 845*e1fe3e4aSElliott Hughes numAxes = 3 846*e1fe3e4aSElliott Hughes self.assertEqual(4 + numAxes * 2, getTupleSize(0x8042, numAxes)) 847*e1fe3e4aSElliott Hughes self.assertEqual(4 + numAxes * 4, getTupleSize(0x4077, numAxes)) 848*e1fe3e4aSElliott Hughes self.assertEqual(4, getTupleSize(0x2077, numAxes)) 849*e1fe3e4aSElliott Hughes self.assertEqual(4, getTupleSize(11, numAxes)) 850*e1fe3e4aSElliott Hughes 851*e1fe3e4aSElliott Hughes def test_inferRegion(self): 852*e1fe3e4aSElliott Hughes start, end = inferRegion_({"wght": -0.3, "wdth": 0.7}) 853*e1fe3e4aSElliott Hughes self.assertEqual(start, {"wght": -0.3, "wdth": 0.0}) 854*e1fe3e4aSElliott Hughes self.assertEqual(end, {"wght": 0.0, "wdth": 0.7}) 855*e1fe3e4aSElliott Hughes 856*e1fe3e4aSElliott Hughes @staticmethod 857*e1fe3e4aSElliott Hughes def xml_lines(writer): 858*e1fe3e4aSElliott Hughes content = writer.file.getvalue().decode("utf-8") 859*e1fe3e4aSElliott Hughes return [line.strip() for line in content.splitlines()][1:] 860*e1fe3e4aSElliott Hughes 861*e1fe3e4aSElliott Hughes def test_getCoordWidth(self): 862*e1fe3e4aSElliott Hughes empty = TupleVariation({}, []) 863*e1fe3e4aSElliott Hughes self.assertEqual(empty.getCoordWidth(), 0) 864*e1fe3e4aSElliott Hughes 865*e1fe3e4aSElliott Hughes empty = TupleVariation({}, [None]) 866*e1fe3e4aSElliott Hughes self.assertEqual(empty.getCoordWidth(), 0) 867*e1fe3e4aSElliott Hughes 868*e1fe3e4aSElliott Hughes gvarTuple = TupleVariation({}, [None, (0, 0)]) 869*e1fe3e4aSElliott Hughes self.assertEqual(gvarTuple.getCoordWidth(), 2) 870*e1fe3e4aSElliott Hughes 871*e1fe3e4aSElliott Hughes cvarTuple = TupleVariation({}, [None, 0]) 872*e1fe3e4aSElliott Hughes self.assertEqual(cvarTuple.getCoordWidth(), 1) 873*e1fe3e4aSElliott Hughes 874*e1fe3e4aSElliott Hughes cvarTuple.coordinates[1] *= 1.0 875*e1fe3e4aSElliott Hughes self.assertEqual(cvarTuple.getCoordWidth(), 1) 876*e1fe3e4aSElliott Hughes 877*e1fe3e4aSElliott Hughes with self.assertRaises(TypeError): 878*e1fe3e4aSElliott Hughes TupleVariation({}, [None, "a"]).getCoordWidth() 879*e1fe3e4aSElliott Hughes 880*e1fe3e4aSElliott Hughes def test_scaleDeltas_cvar(self): 881*e1fe3e4aSElliott Hughes var = TupleVariation({}, [100, None]) 882*e1fe3e4aSElliott Hughes 883*e1fe3e4aSElliott Hughes var.scaleDeltas(1.0) 884*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [100, None]) 885*e1fe3e4aSElliott Hughes 886*e1fe3e4aSElliott Hughes var.scaleDeltas(0.333) 887*e1fe3e4aSElliott Hughes self.assertAlmostEqual(var.coordinates[0], 33.3) 888*e1fe3e4aSElliott Hughes self.assertIsNone(var.coordinates[1]) 889*e1fe3e4aSElliott Hughes 890*e1fe3e4aSElliott Hughes var.scaleDeltas(0.0) 891*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [0, None]) 892*e1fe3e4aSElliott Hughes 893*e1fe3e4aSElliott Hughes def test_scaleDeltas_gvar(self): 894*e1fe3e4aSElliott Hughes var = TupleVariation({}, [(100, 200), None]) 895*e1fe3e4aSElliott Hughes 896*e1fe3e4aSElliott Hughes var.scaleDeltas(1.0) 897*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [(100, 200), None]) 898*e1fe3e4aSElliott Hughes 899*e1fe3e4aSElliott Hughes var.scaleDeltas(0.333) 900*e1fe3e4aSElliott Hughes self.assertAlmostEqual(var.coordinates[0][0], 33.3) 901*e1fe3e4aSElliott Hughes self.assertAlmostEqual(var.coordinates[0][1], 66.6) 902*e1fe3e4aSElliott Hughes self.assertIsNone(var.coordinates[1]) 903*e1fe3e4aSElliott Hughes 904*e1fe3e4aSElliott Hughes var.scaleDeltas(0.0) 905*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [(0, 0), None]) 906*e1fe3e4aSElliott Hughes 907*e1fe3e4aSElliott Hughes def test_roundDeltas_cvar(self): 908*e1fe3e4aSElliott Hughes var = TupleVariation({}, [55.5, None, 99.9]) 909*e1fe3e4aSElliott Hughes var.roundDeltas() 910*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [56, None, 100]) 911*e1fe3e4aSElliott Hughes 912*e1fe3e4aSElliott Hughes def test_roundDeltas_gvar(self): 913*e1fe3e4aSElliott Hughes var = TupleVariation({}, [(55.5, 100.0), None, (99.9, 100.0)]) 914*e1fe3e4aSElliott Hughes var.roundDeltas() 915*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [(56, 100), None, (100, 100)]) 916*e1fe3e4aSElliott Hughes 917*e1fe3e4aSElliott Hughes def test_calcInferredDeltas(self): 918*e1fe3e4aSElliott Hughes var = TupleVariation({}, [(0, 0), None, None, None]) 919*e1fe3e4aSElliott Hughes coords = [(1, 1), (1, 1), (1, 1), (1, 1)] 920*e1fe3e4aSElliott Hughes 921*e1fe3e4aSElliott Hughes var.calcInferredDeltas(coords, []) 922*e1fe3e4aSElliott Hughes 923*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [(0, 0), (0, 0), (0, 0), (0, 0)]) 924*e1fe3e4aSElliott Hughes 925*e1fe3e4aSElliott Hughes def test_calcInferredDeltas_invalid(self): 926*e1fe3e4aSElliott Hughes # cvar tuples can't have inferred deltas 927*e1fe3e4aSElliott Hughes with self.assertRaises(TypeError): 928*e1fe3e4aSElliott Hughes TupleVariation({}, [0]).calcInferredDeltas([], []) 929*e1fe3e4aSElliott Hughes 930*e1fe3e4aSElliott Hughes # origCoords must have same length as self.coordinates 931*e1fe3e4aSElliott Hughes with self.assertRaises(ValueError): 932*e1fe3e4aSElliott Hughes TupleVariation({}, [(0, 0), None]).calcInferredDeltas([], []) 933*e1fe3e4aSElliott Hughes 934*e1fe3e4aSElliott Hughes # at least 4 phantom points required 935*e1fe3e4aSElliott Hughes with self.assertRaises(AssertionError): 936*e1fe3e4aSElliott Hughes TupleVariation({}, [(0, 0), None]).calcInferredDeltas([(0, 0), (0, 0)], []) 937*e1fe3e4aSElliott Hughes 938*e1fe3e4aSElliott Hughes with self.assertRaises(AssertionError): 939*e1fe3e4aSElliott Hughes TupleVariation({}, [(0, 0)] + [None] * 5).calcInferredDeltas( 940*e1fe3e4aSElliott Hughes [(0, 0)] * 6, [1, 0] # endPts not in increasing order 941*e1fe3e4aSElliott Hughes ) 942*e1fe3e4aSElliott Hughes 943*e1fe3e4aSElliott Hughes def test_optimize(self): 944*e1fe3e4aSElliott Hughes var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)] * 5) 945*e1fe3e4aSElliott Hughes 946*e1fe3e4aSElliott Hughes var.optimize([(0, 0)] * 5, [0]) 947*e1fe3e4aSElliott Hughes 948*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [None, None, None, None, None]) 949*e1fe3e4aSElliott Hughes 950*e1fe3e4aSElliott Hughes def test_optimize_isComposite(self): 951*e1fe3e4aSElliott Hughes # when a composite glyph's deltas are all (0, 0), we still want 952*e1fe3e4aSElliott Hughes # to write out an entry in gvar, else macOS doesn't apply any 953*e1fe3e4aSElliott Hughes # variations to the composite glyph (even if its individual components 954*e1fe3e4aSElliott Hughes # do vary). 955*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/1381 956*e1fe3e4aSElliott Hughes var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)] * 5) 957*e1fe3e4aSElliott Hughes var.optimize([(0, 0)] * 5, [0], isComposite=True) 958*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [(0, 0)] * 5) 959*e1fe3e4aSElliott Hughes 960*e1fe3e4aSElliott Hughes # it takes more than 128 (0, 0) deltas before the optimized tuple with 961*e1fe3e4aSElliott Hughes # (None) inferred deltas (except for the first) becomes smaller than 962*e1fe3e4aSElliott Hughes # the un-optimized one that has all deltas explicitly set to (0, 0). 963*e1fe3e4aSElliott Hughes var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)] * 129) 964*e1fe3e4aSElliott Hughes var.optimize([(0, 0)] * 129, list(range(129 - 4)), isComposite=True) 965*e1fe3e4aSElliott Hughes self.assertEqual(var.coordinates, [(0, 0)] + [None] * 128) 966*e1fe3e4aSElliott Hughes 967*e1fe3e4aSElliott Hughes def test_sum_deltas_gvar(self): 968*e1fe3e4aSElliott Hughes var1 = TupleVariation( 969*e1fe3e4aSElliott Hughes {}, 970*e1fe3e4aSElliott Hughes [ 971*e1fe3e4aSElliott Hughes (-20, 0), 972*e1fe3e4aSElliott Hughes (-20, 0), 973*e1fe3e4aSElliott Hughes (20, 0), 974*e1fe3e4aSElliott Hughes (20, 0), 975*e1fe3e4aSElliott Hughes (0, 0), 976*e1fe3e4aSElliott Hughes (0, 0), 977*e1fe3e4aSElliott Hughes (0, 0), 978*e1fe3e4aSElliott Hughes (0, 0), 979*e1fe3e4aSElliott Hughes ], 980*e1fe3e4aSElliott Hughes ) 981*e1fe3e4aSElliott Hughes var2 = TupleVariation( 982*e1fe3e4aSElliott Hughes {}, 983*e1fe3e4aSElliott Hughes [ 984*e1fe3e4aSElliott Hughes (-10, 0), 985*e1fe3e4aSElliott Hughes (-10, 0), 986*e1fe3e4aSElliott Hughes (10, 0), 987*e1fe3e4aSElliott Hughes (10, 0), 988*e1fe3e4aSElliott Hughes (0, 0), 989*e1fe3e4aSElliott Hughes (20, 0), 990*e1fe3e4aSElliott Hughes (0, 0), 991*e1fe3e4aSElliott Hughes (0, 0), 992*e1fe3e4aSElliott Hughes ], 993*e1fe3e4aSElliott Hughes ) 994*e1fe3e4aSElliott Hughes 995*e1fe3e4aSElliott Hughes var1 += var2 996*e1fe3e4aSElliott Hughes 997*e1fe3e4aSElliott Hughes self.assertEqual( 998*e1fe3e4aSElliott Hughes var1.coordinates, 999*e1fe3e4aSElliott Hughes [ 1000*e1fe3e4aSElliott Hughes (-30, 0), 1001*e1fe3e4aSElliott Hughes (-30, 0), 1002*e1fe3e4aSElliott Hughes (30, 0), 1003*e1fe3e4aSElliott Hughes (30, 0), 1004*e1fe3e4aSElliott Hughes (0, 0), 1005*e1fe3e4aSElliott Hughes (20, 0), 1006*e1fe3e4aSElliott Hughes (0, 0), 1007*e1fe3e4aSElliott Hughes (0, 0), 1008*e1fe3e4aSElliott Hughes ], 1009*e1fe3e4aSElliott Hughes ) 1010*e1fe3e4aSElliott Hughes 1011*e1fe3e4aSElliott Hughes def test_sum_deltas_gvar_invalid_length(self): 1012*e1fe3e4aSElliott Hughes var1 = TupleVariation({}, [(1, 2)]) 1013*e1fe3e4aSElliott Hughes var2 = TupleVariation({}, [(1, 2), (3, 4)]) 1014*e1fe3e4aSElliott Hughes 1015*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(ValueError, "deltas with different lengths"): 1016*e1fe3e4aSElliott Hughes var1 += var2 1017*e1fe3e4aSElliott Hughes 1018*e1fe3e4aSElliott Hughes def test_sum_deltas_gvar_with_inferred_points(self): 1019*e1fe3e4aSElliott Hughes var1 = TupleVariation({}, [(1, 2), None]) 1020*e1fe3e4aSElliott Hughes var2 = TupleVariation({}, [(2, 3), None]) 1021*e1fe3e4aSElliott Hughes 1022*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(ValueError, "deltas with inferred points"): 1023*e1fe3e4aSElliott Hughes var1 += var2 1024*e1fe3e4aSElliott Hughes 1025*e1fe3e4aSElliott Hughes def test_sum_deltas_cvar(self): 1026*e1fe3e4aSElliott Hughes axes = {"wght": (0.0, 1.0, 1.0)} 1027*e1fe3e4aSElliott Hughes var1 = TupleVariation(axes, [0, 1, None, None]) 1028*e1fe3e4aSElliott Hughes var2 = TupleVariation(axes, [None, 2, None, 3]) 1029*e1fe3e4aSElliott Hughes var3 = TupleVariation(axes, [None, None, None, 4]) 1030*e1fe3e4aSElliott Hughes 1031*e1fe3e4aSElliott Hughes var1 += var2 1032*e1fe3e4aSElliott Hughes var1 += var3 1033*e1fe3e4aSElliott Hughes 1034*e1fe3e4aSElliott Hughes self.assertEqual(var1.coordinates, [0, 3, None, 7]) 1035*e1fe3e4aSElliott Hughes 1036*e1fe3e4aSElliott Hughes 1037*e1fe3e4aSElliott Hughesif __name__ == "__main__": 1038*e1fe3e4aSElliott Hughes import sys 1039*e1fe3e4aSElliott Hughes 1040*e1fe3e4aSElliott Hughes sys.exit(unittest.main()) 1041