1from __future__ import print_function, division, absolute_import, unicode_literals 2from fontTools.misc.py23 import * 3from fontTools.misc.testTools import FakeFont, getXML, parseXML 4from fontTools.misc.textTools import deHexStr, hexStr 5from fontTools.ttLib import TTLibError, getTableClass, getTableModule, newTable 6import unittest 7from fontTools.ttLib.tables.TupleVariation import TupleVariation 8 9 10gvarClass = getTableClass("gvar") 11 12 13GVAR_DATA = deHexStr( 14 "0001 0000 " # 0: majorVersion=1 minorVersion=0 15 "0002 0000 " # 4: axisCount=2 sharedTupleCount=0 16 "0000001C " # 8: offsetToSharedTuples=28 17 "0003 0000 " # 12: glyphCount=3 flags=0 18 "0000001C " # 16: offsetToGlyphVariationData=28 19 "0000 0000 000C 002F " # 20: offsets=[0,0,12,47], times 2: [0,0,24,94], 20 # # +offsetToGlyphVariationData: [28,28,52,122] 21 # 22 # 28: Glyph variation data for glyph #0, ".notdef" 23 # ------------------------------------------------ 24 # (no variation data for this glyph) 25 # 26 # 28: Glyph variation data for glyph #1, "space" 27 # ---------------------------------------------- 28 "8001 000C " # 28: tupleVariationCount=1|TUPLES_SHARE_POINT_NUMBERS, offsetToData=12(+28=40) 29 "000A " # 32: tvHeader[0].variationDataSize=10 30 "8000 " # 34: tvHeader[0].tupleIndex=EMBEDDED_PEAK 31 "0000 2CCD " # 36: tvHeader[0].peakTuple={wght:0.0, wdth:0.7} 32 "00 " # 40: all points 33 "03 01 02 03 04 " # 41: deltaX=[1, 2, 3, 4] 34 "03 0b 16 21 2C " # 46: deltaY=[11, 22, 33, 44] 35 "00 " # 51: padding 36 # 37 # 52: Glyph variation data for glyph #2, "I" 38 # ------------------------------------------ 39 "8002 001c " # 52: tupleVariationCount=2|TUPLES_SHARE_POINT_NUMBERS, offsetToData=28(+52=80) 40 "0012 " # 56: tvHeader[0].variationDataSize=18 41 "C000 " # 58: tvHeader[0].tupleIndex=EMBEDDED_PEAK|INTERMEDIATE_REGION 42 "2000 0000 " # 60: tvHeader[0].peakTuple={wght:0.5, wdth:0.0} 43 "0000 0000 " # 64: tvHeader[0].intermediateStart={wght:0.0, wdth:0.0} 44 "4000 0000 " # 68: tvHeader[0].intermediateEnd={wght:1.0, wdth:0.0} 45 "0016 " # 72: tvHeader[1].variationDataSize=22 46 "A000 " # 74: tvHeader[1].tupleIndex=EMBEDDED_PEAK|PRIVATE_POINTS 47 "C000 3333 " # 76: tvHeader[1].peakTuple={wght:-1.0, wdth:0.8} 48 "00 " # 80: all points 49 "07 03 01 04 01 " # 81: deltaX.len=7, deltaX=[3, 1, 4, 1, 50 "05 09 02 06 " # 86: 5, 9, 2, 6] 51 "07 03 01 04 01 " # 90: deltaY.len=7, deltaY=[3, 1, 4, 1, 52 "05 09 02 06 " # 95: 5, 9, 2, 6] 53 "06 " # 99: 6 points 54 "05 00 01 03 01 " # 100: runLen=5(+1=6); delta-encoded run=[0, 1, 4, 5, 55 "01 01 " # 105: 6, 7] 56 "05 f8 07 fc 03 fe 01 " # 107: deltaX.len=5, deltaX=[-8,7,-4,3,-2,1] 57 "05 a8 4d 2c 21 ea 0b " # 114: deltaY.len=5, deltaY=[-88,77,44,33,-22,11] 58 "00" # 121: padding 59) # 122: <end> 60assert len(GVAR_DATA) == 122 61 62 63GVAR_VARIATIONS = { 64 ".notdef": [ 65 ], 66 "space": [ 67 TupleVariation( 68 {"wdth": (0.0, 0.7, 0.7)}, 69 [(1, 11), (2, 22), (3, 33), (4, 44)]), 70 ], 71 "I": [ 72 TupleVariation( 73 {"wght": (0.0, 0.5, 1.0)}, 74 [(3,3), (1,1), (4,4), (1,1), (5,5), (9,9), (2,2), (6,6)]), 75 TupleVariation( 76 {"wght": (-1.0, -1.0, 0.0), "wdth": (0.0, 0.8, 0.8)}, 77 [(-8,-88), (7,77), None, None, (-4,44), (3,33), (-2,-22), (1,11)]), 78 ], 79} 80 81 82GVAR_XML = [ 83 '<version value="1"/>', 84 '<reserved value="0"/>', 85 '<glyphVariations glyph="space">', 86 ' <tuple>', 87 ' <coord axis="wdth" value="0.7"/>', 88 ' <delta pt="0" x="1" y="11"/>', 89 ' <delta pt="1" x="2" y="22"/>', 90 ' <delta pt="2" x="3" y="33"/>', 91 ' <delta pt="3" x="4" y="44"/>', 92 ' </tuple>', 93 '</glyphVariations>', 94 '<glyphVariations glyph="I">', 95 ' <tuple>', 96 ' <coord axis="wght" min="0.0" value="0.5" max="1.0"/>', 97 ' <delta pt="0" x="3" y="3"/>', 98 ' <delta pt="1" x="1" y="1"/>', 99 ' <delta pt="2" x="4" y="4"/>', 100 ' <delta pt="3" x="1" y="1"/>', 101 ' <delta pt="4" x="5" y="5"/>', 102 ' <delta pt="5" x="9" y="9"/>', 103 ' <delta pt="6" x="2" y="2"/>', 104 ' <delta pt="7" x="6" y="6"/>', 105 ' </tuple>', 106 ' <tuple>', 107 ' <coord axis="wght" value="-1.0"/>', 108 ' <coord axis="wdth" value="0.8"/>', 109 ' <delta pt="0" x="-8" y="-88"/>', 110 ' <delta pt="1" x="7" y="77"/>', 111 ' <delta pt="4" x="-4" y="44"/>', 112 ' <delta pt="5" x="3" y="33"/>', 113 ' <delta pt="6" x="-2" y="-22"/>', 114 ' <delta pt="7" x="1" y="11"/>', 115 ' </tuple>', 116 '</glyphVariations>', 117] 118 119 120GVAR_DATA_EMPTY_VARIATIONS = deHexStr( 121 "0001 0000 " # 0: majorVersion=1 minorVersion=0 122 "0002 0000 " # 4: axisCount=2 sharedTupleCount=0 123 "0000001c " # 8: offsetToSharedTuples=28 124 "0003 0000 " # 12: glyphCount=3 flags=0 125 "0000001c " # 16: offsetToGlyphVariationData=28 126 "0000 0000 0000 0000" # 20: offsets=[0, 0, 0, 0] 127) # 28: <end> 128 129 130def hexencode(s): 131 h = hexStr(s).upper() 132 return ' '.join([h[i:i+2] for i in range(0, len(h), 2)]) 133 134 135class GVARTableTest(unittest.TestCase): 136 def makeFont(self, variations): 137 glyphs=[".notdef", "space", "I"] 138 Axis = getTableModule("fvar").Axis 139 Glyph = getTableModule("glyf").Glyph 140 glyf, fvar, gvar = newTable("glyf"), newTable("fvar"), newTable("gvar") 141 font = FakeFont(glyphs) 142 font.tables = {"glyf": glyf, "gvar": gvar, "fvar": fvar} 143 glyf.glyphs = {glyph: Glyph() for glyph in glyphs} 144 glyf.glyphs["I"].coordinates = [(10, 10), (10, 20), (20, 20), (20, 10)] 145 fvar.axes = [Axis(), Axis()] 146 fvar.axes[0].axisTag, fvar.axes[1].axisTag = "wght", "wdth" 147 gvar.variations = variations 148 return font, gvar 149 150 def test_compile(self): 151 font, gvar = self.makeFont(GVAR_VARIATIONS) 152 self.assertEqual(hexStr(gvar.compile(font)), hexStr(GVAR_DATA)) 153 154 def test_compile_noVariations(self): 155 font, gvar = self.makeFont({}) 156 self.assertEqual(hexStr(gvar.compile(font)), 157 hexStr(GVAR_DATA_EMPTY_VARIATIONS)) 158 159 def test_compile_emptyVariations(self): 160 font, gvar = self.makeFont({".notdef": [], "space": [], "I": []}) 161 self.assertEqual(hexStr(gvar.compile(font)), 162 hexStr(GVAR_DATA_EMPTY_VARIATIONS)) 163 164 def test_decompile(self): 165 font, gvar = self.makeFont({}) 166 gvar.decompile(GVAR_DATA, font) 167 self.assertEqual(gvar.variations, GVAR_VARIATIONS) 168 169 def test_decompile_noVariations(self): 170 font, gvar = self.makeFont({}) 171 gvar.decompile(GVAR_DATA_EMPTY_VARIATIONS, font) 172 self.assertEqual(gvar.variations, 173 {".notdef": [], "space": [], "I": []}) 174 175 def test_fromXML(self): 176 font, gvar = self.makeFont({}) 177 for name, attrs, content in parseXML(GVAR_XML): 178 gvar.fromXML(name, attrs, content, ttFont=font) 179 self.assertEqual(gvar.variations, 180 {g:v for g,v in GVAR_VARIATIONS.items() if v}) 181 182 def test_toXML(self): 183 font, gvar = self.makeFont(GVAR_VARIATIONS) 184 self.assertEqual(getXML(gvar.toXML, font), GVAR_XML) 185 186 def test_compileOffsets_shortFormat(self): 187 self.assertEqual((deHexStr("00 00 00 02 FF C0"), 0), 188 gvarClass.compileOffsets_([0, 4, 0x1ff80])) 189 190 def test_compileOffsets_longFormat(self): 191 self.assertEqual((deHexStr("00 00 00 00 00 00 00 04 CA FE BE EF"), 1), 192 gvarClass.compileOffsets_([0, 4, 0xCAFEBEEF])) 193 194 def test_decompileOffsets_shortFormat(self): 195 decompileOffsets = gvarClass.decompileOffsets_ 196 data = deHexStr("00 11 22 33 44 55 66 77 88 99 aa bb") 197 self.assertEqual( 198 [2*0x0011, 2*0x2233, 2*0x4455, 2*0x6677, 2*0x8899, 2*0xaabb], 199 list(decompileOffsets(data, tableFormat=0, glyphCount=5))) 200 201 def test_decompileOffsets_longFormat(self): 202 decompileOffsets = gvarClass.decompileOffsets_ 203 data = deHexStr("00 11 22 33 44 55 66 77 88 99 aa bb") 204 self.assertEqual( 205 [0x00112233, 0x44556677, 0x8899aabb], 206 list(decompileOffsets(data, tableFormat=1, glyphCount=2))) 207 208 209if __name__ == "__main__": 210 import sys 211 sys.exit(unittest.main()) 212