Home | History | Annotate | Download | only in tables
      1 from __future__ import print_function, division, absolute_import
      2 from fontTools.misc.py23 import *
      3 from fontTools.misc.fixedTools import otRound
      4 from fontTools.pens.ttGlyphPen import TTGlyphPen
      5 from fontTools.ttLib import TTFont, newTable, TTLibError
      6 from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
      7 import sys
      8 import array
      9 import pytest
     10 import re
     11 import os
     12 import unittest
     13 
     14 
     15 class GlyphCoordinatesTest(object):
     16 
     17     def test_translate(self):
     18         g = GlyphCoordinates([(1,2)])
     19         g.translate((.5,0))
     20         assert g == GlyphCoordinates([(1.5,2.0)])
     21 
     22     def test_scale(self):
     23         g = GlyphCoordinates([(1,2)])
     24         g.scale((.5,0))
     25         assert g == GlyphCoordinates([(0.5,0.0)])
     26 
     27     def test_transform(self):
     28         g = GlyphCoordinates([(1,2)])
     29         g.transform(((.5,0),(.2,.5)))
     30         assert g[0] == GlyphCoordinates([(0.9,1.0)])[0]
     31 
     32     def test__eq__(self):
     33         g = GlyphCoordinates([(1,2)])
     34         g2 = GlyphCoordinates([(1.0,2)])
     35         g3 = GlyphCoordinates([(1.5,2)])
     36         assert g == g2
     37         assert not g == g3
     38         assert not g2 == g3
     39         assert not g == object()
     40 
     41     def test__ne__(self):
     42         g = GlyphCoordinates([(1,2)])
     43         g2 = GlyphCoordinates([(1.0,2)])
     44         g3 = GlyphCoordinates([(1.5,2)])
     45         assert not (g != g2)
     46         assert g != g3
     47         assert g2 != g3
     48         assert g != object()
     49 
     50     def test__pos__(self):
     51         g = GlyphCoordinates([(1,2)])
     52         g2 = +g
     53         assert g == g2
     54 
     55     def test__neg__(self):
     56         g = GlyphCoordinates([(1,2)])
     57         g2 = -g
     58         assert g2 == GlyphCoordinates([(-1, -2)])
     59 
     60     @pytest.mark.skipif(sys.version_info[0] < 3,
     61                         reason="__round___ requires Python 3")
     62     def test__round__(self):
     63         g = GlyphCoordinates([(-1.5,2)])
     64         g2 = round(g)
     65         assert g2 == GlyphCoordinates([(-1,2)])
     66 
     67     def test__add__(self):
     68         g1 = GlyphCoordinates([(1,2)])
     69         g2 = GlyphCoordinates([(3,4)])
     70         g3 = GlyphCoordinates([(4,6)])
     71         assert g1 + g2 == g3
     72         assert g1 + (1, 1) == GlyphCoordinates([(2,3)])
     73         with pytest.raises(TypeError) as excinfo:
     74             assert g1 + object()
     75         assert 'unsupported operand' in str(excinfo.value)
     76 
     77     def test__sub__(self):
     78         g1 = GlyphCoordinates([(1,2)])
     79         g2 = GlyphCoordinates([(3,4)])
     80         g3 = GlyphCoordinates([(-2,-2)])
     81         assert g1 - g2 == g3
     82         assert g1 - (1, 1) == GlyphCoordinates([(0,1)])
     83         with pytest.raises(TypeError) as excinfo:
     84             assert g1 - object()
     85         assert 'unsupported operand' in str(excinfo.value)
     86 
     87     def test__rsub__(self):
     88         g = GlyphCoordinates([(1,2)])
     89         # other + (-self)
     90         assert (1, 1) - g == GlyphCoordinates([(0,-1)])
     91 
     92     def test__mul__(self):
     93         g = GlyphCoordinates([(1,2)])
     94         assert g * 3 == GlyphCoordinates([(3,6)])
     95         assert g * (3,2) == GlyphCoordinates([(3,4)])
     96         assert g * (1,1) == g
     97         with pytest.raises(TypeError) as excinfo:
     98             assert g * object()
     99         assert 'unsupported operand' in str(excinfo.value)
    100 
    101     def test__truediv__(self):
    102         g = GlyphCoordinates([(1,2)])
    103         assert g / 2 == GlyphCoordinates([(.5,1)])
    104         assert g / (1, 2) == GlyphCoordinates([(1,1)])
    105         assert g / (1, 1) == g
    106         with pytest.raises(TypeError) as excinfo:
    107             assert g / object()
    108         assert 'unsupported operand' in str(excinfo.value)
    109 
    110     def test__iadd__(self):
    111         g = GlyphCoordinates([(1,2)])
    112         g += (.5,0)
    113         assert g == GlyphCoordinates([(1.5, 2.0)])
    114         g2 = GlyphCoordinates([(3,4)])
    115         g += g2
    116         assert g == GlyphCoordinates([(4.5, 6.0)])
    117 
    118     def test__isub__(self):
    119         g = GlyphCoordinates([(1,2)])
    120         g -= (.5, 0)
    121         assert g == GlyphCoordinates([(0.5, 2.0)])
    122         g2 = GlyphCoordinates([(3,4)])
    123         g -= g2
    124         assert g == GlyphCoordinates([(-2.5, -2.0)])
    125 
    126     def __test__imul__(self):
    127         g = GlyphCoordinates([(1,2)])
    128         g *= (2,.5)
    129         g *= 2
    130         assert g == GlyphCoordinates([(4.0, 2.0)])
    131         g = GlyphCoordinates([(1,2)])
    132         g *= 2
    133         assert g == GlyphCoordinates([(2, 4)])
    134 
    135     def test__itruediv__(self):
    136         g = GlyphCoordinates([(1,3)])
    137         g /= (.5,1.5)
    138         g /= 2
    139         assert g == GlyphCoordinates([(1.0, 1.0)])
    140 
    141     def test__bool__(self):
    142         g = GlyphCoordinates([])
    143         assert bool(g) == False
    144         g = GlyphCoordinates([(0,0), (0.,0)])
    145         assert bool(g) == True
    146         g = GlyphCoordinates([(0,0), (1,0)])
    147         assert bool(g) == True
    148         g = GlyphCoordinates([(0,.5), (0,0)])
    149         assert bool(g) == True
    150 
    151     def test_double_precision_float(self):
    152         # https://github.com/fonttools/fonttools/issues/963
    153         afloat = 242.50000000000003
    154         g = GlyphCoordinates([(afloat, 0)])
    155         g.toInt()
    156         # this would return 242 if the internal array.array typecode is 'f',
    157         # since the Python float is truncated to a C float.
    158         # when using typecode 'd' it should return the correct value 243
    159         assert g[0][0] == otRound(afloat)
    160 
    161     def test__checkFloat_overflow(self):
    162         g = GlyphCoordinates([(1, 1)], typecode="h")
    163         g.append((0x8000, 0))
    164         assert g.array.typecode == "d"
    165         assert g.array == array.array("d", [1.0, 1.0, 32768.0, 0.0])
    166 
    167 
    168 CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
    169 DATA_DIR = os.path.join(CURR_DIR, 'data')
    170 
    171 GLYF_TTX = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.ttx")
    172 GLYF_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.glyf.bin")
    173 HEAD_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.head.bin")
    174 LOCA_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.loca.bin")
    175 MAXP_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.maxp.bin")
    176 
    177 
    178 def strip_ttLibVersion(string):
    179     return re.sub(' ttLibVersion=".*"', '', string)
    180 
    181 
    182 class glyfTableTest(unittest.TestCase):
    183 
    184     def __init__(self, methodName):
    185         unittest.TestCase.__init__(self, methodName)
    186         # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
    187         # and fires deprecation warnings if a program uses the old name.
    188         if not hasattr(self, "assertRaisesRegex"):
    189             self.assertRaisesRegex = self.assertRaisesRegexp
    190 
    191     @classmethod
    192     def setUpClass(cls):
    193         with open(GLYF_BIN, 'rb') as f:
    194             cls.glyfData = f.read()
    195         with open(HEAD_BIN, 'rb') as f:
    196             cls.headData = f.read()
    197         with open(LOCA_BIN, 'rb') as f:
    198             cls.locaData = f.read()
    199         with open(MAXP_BIN, 'rb') as f:
    200             cls.maxpData = f.read()
    201         with open(GLYF_TTX, 'r') as f:
    202             cls.glyfXML = strip_ttLibVersion(f.read()).splitlines()
    203 
    204     def test_toXML(self):
    205         font = TTFont(sfntVersion="\x00\x01\x00\x00")
    206         glyfTable = font['glyf'] = newTable('glyf')
    207         font['head'] = newTable('head')
    208         font['loca'] = newTable('loca')
    209         font['maxp'] = newTable('maxp')
    210         font['maxp'].decompile(self.maxpData, font)
    211         font['head'].decompile(self.headData, font)
    212         font['loca'].decompile(self.locaData, font)
    213         glyfTable.decompile(self.glyfData, font)
    214         out = UnicodeIO()
    215         font.saveXML(out)
    216         glyfXML = strip_ttLibVersion(out.getvalue()).splitlines()
    217         self.assertEqual(glyfXML, self.glyfXML)
    218 
    219     def test_fromXML(self):
    220         font = TTFont(sfntVersion="\x00\x01\x00\x00")
    221         font.importXML(GLYF_TTX)
    222         glyfTable = font['glyf']
    223         glyfData = glyfTable.compile(font)
    224         self.assertEqual(glyfData, self.glyfData)
    225 
    226     def test_recursiveComponent(self):
    227         glyphSet = {}
    228         pen_dummy = TTGlyphPen(glyphSet)
    229         glyph_dummy = pen_dummy.glyph()
    230         glyphSet["A"] = glyph_dummy
    231         glyphSet["B"] = glyph_dummy
    232         pen_A = TTGlyphPen(glyphSet)
    233         pen_A.addComponent("B", (1, 0, 0, 1, 0, 0))
    234         pen_B = TTGlyphPen(glyphSet)
    235         pen_B.addComponent("A", (1, 0, 0, 1, 0, 0))
    236         glyph_A = pen_A.glyph()
    237         glyph_B = pen_B.glyph()
    238         glyphSet["A"] = glyph_A
    239         glyphSet["B"] = glyph_B
    240         with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"):
    241             glyph_A.getCoordinates(glyphSet)
    242 
    243 
    244 if __name__ == "__main__":
    245     import sys
    246     sys.exit(unittest.main())
    247