Home | History | Annotate | Download | only in tables
      1 from __future__ import print_function, division, absolute_import, unicode_literals
      2 from fontTools.misc.py23 import *
      3 from fontTools.misc.testTools import FakeFont, getXML, parseXML
      4 from fontTools.misc.textTools import deHexStr, hexStr
      5 from fontTools.ttLib import TTLibError, getTableClass, getTableModule, newTable
      6 import unittest
      7 from fontTools.ttLib.tables.TupleVariation import TupleVariation
      8 
      9 
     10 gvarClass = getTableClass("gvar")
     11 
     12 
     13 GVAR_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>
     60 assert len(GVAR_DATA) == 122
     61 
     62 
     63 GVAR_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 
     82 GVAR_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 
    120 GVAR_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 
    130 def hexencode(s):
    131 	h = hexStr(s).upper()
    132 	return ' '.join([h[i:i+2] for i in range(0, len(h), 2)])
    133 
    134 
    135 class 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 
    209 if __name__ == "__main__":
    210 	import sys
    211 	sys.exit(unittest.main())
    212