Home | History | Annotate | Download | only in pens
      1 from __future__ import print_function, division, absolute_import
      2 from fontTools.misc.py23 import *
      3 from fontTools.misc.loggingTools import CapturingLogHandler
      4 import unittest
      5 
      6 from fontTools.pens.basePen import AbstractPen
      7 from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen, \
      8     SegmentToPointPen, GuessSmoothPointPen, ReverseContourPointPen
      9 
     10 
     11 class _TestSegmentPen(AbstractPen):
     12 
     13     def __init__(self):
     14         self._commands = []
     15 
     16     def __repr__(self):
     17         return " ".join(self._commands)
     18 
     19     def moveTo(self, pt):
     20         self._commands.append("%s %s moveto" % (pt[0], pt[1]))
     21 
     22     def lineTo(self, pt):
     23         self._commands.append("%s %s lineto" % (pt[0], pt[1]))
     24 
     25     def curveTo(self, *pts):
     26         pts = ["%s %s" % pt for pt in pts]
     27         self._commands.append("%s curveto" % " ".join(pts))
     28 
     29     def qCurveTo(self, *pts):
     30         pts = ["%s %s" % pt if pt is not None else "None" for pt in pts]
     31         self._commands.append("%s qcurveto" % " ".join(pts))
     32 
     33     def closePath(self):
     34         self._commands.append("closepath")
     35 
     36     def endPath(self):
     37         self._commands.append("endpath")
     38 
     39     def addComponent(self, glyphName, transformation):
     40         self._commands.append("'%s' %s addcomponent" % (glyphName, transformation))
     41 
     42 
     43 def _reprKwargs(kwargs):
     44     items = []
     45     for key in sorted(kwargs):
     46         value = kwargs[key]
     47         if isinstance(value, basestring):
     48             items.append("%s='%s'" % (key, value))
     49         else:
     50             items.append("%s=%s" % (key, value))
     51     return items
     52 
     53 
     54 class _TestPointPen(AbstractPointPen):
     55 
     56     def __init__(self):
     57         self._commands = []
     58 
     59     def __repr__(self):
     60         return " ".join(self._commands)
     61 
     62     def beginPath(self, identifier=None, **kwargs):
     63         items = []
     64         if identifier is not None:
     65             items.append("identifier='%s'" % identifier)
     66         items.extend(_reprKwargs(kwargs))
     67         self._commands.append("beginPath(%s)" % ", ".join(items))
     68 
     69     def addPoint(self, pt, segmentType=None, smooth=False, name=None,
     70                  identifier=None, **kwargs):
     71         items = ["%s" % (pt,)]
     72         if segmentType is not None:
     73             items.append("segmentType='%s'" % segmentType)
     74         if smooth:
     75             items.append("smooth=True")
     76         if name is not None:
     77             items.append("name='%s'" % name)
     78         if identifier is not None:
     79             items.append("identifier='%s'" % identifier)
     80         items.extend(_reprKwargs(kwargs))
     81         self._commands.append("addPoint(%s)" % ", ".join(items))
     82 
     83     def endPath(self):
     84         self._commands.append("endPath()")
     85 
     86     def addComponent(self, glyphName, transform, identifier=None, **kwargs):
     87         items = ["'%s'" % glyphName, "%s" % transform]
     88         if identifier is not None:
     89             items.append("identifier='%s'" % identifier)
     90         items.extend(_reprKwargs(kwargs))
     91         self._commands.append("addComponent(%s)" % ", ".join(items))
     92 
     93 
     94 class PointToSegmentPenTest(unittest.TestCase):
     95 
     96     def test_open(self):
     97         pen = _TestSegmentPen()
     98         ppen = PointToSegmentPen(pen)
     99         ppen.beginPath()
    100         ppen.addPoint((10, 10), "move")
    101         ppen.addPoint((10, 20), "line")
    102         ppen.endPath()
    103         self.assertEqual("10 10 moveto 10 20 lineto endpath", repr(pen))
    104 
    105     def test_closed(self):
    106         pen = _TestSegmentPen()
    107         ppen = PointToSegmentPen(pen)
    108         ppen.beginPath()
    109         ppen.addPoint((10, 10), "line")
    110         ppen.addPoint((10, 20), "line")
    111         ppen.addPoint((20, 20), "line")
    112         ppen.endPath()
    113         self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(pen))
    114 
    115     def test_cubic(self):
    116         pen = _TestSegmentPen()
    117         ppen = PointToSegmentPen(pen)
    118         ppen.beginPath()
    119         ppen.addPoint((10, 10), "line")
    120         ppen.addPoint((10, 20))
    121         ppen.addPoint((20, 20))
    122         ppen.addPoint((20, 40), "curve")
    123         ppen.endPath()
    124         self.assertEqual("10 10 moveto 10 20 20 20 20 40 curveto closepath", repr(pen))
    125 
    126     def test_quad(self):
    127         pen = _TestSegmentPen()
    128         ppen = PointToSegmentPen(pen)
    129         ppen.beginPath(identifier='foo')
    130         ppen.addPoint((10, 10), "line")
    131         ppen.addPoint((10, 40))
    132         ppen.addPoint((40, 40))
    133         ppen.addPoint((10, 40), "qcurve")
    134         ppen.endPath()
    135         self.assertEqual("10 10 moveto 10 40 40 40 10 40 qcurveto closepath", repr(pen))
    136 
    137     def test_quad_onlyOffCurvePoints(self):
    138         pen = _TestSegmentPen()
    139         ppen = PointToSegmentPen(pen)
    140         ppen.beginPath()
    141         ppen.addPoint((10, 10))
    142         ppen.addPoint((10, 40))
    143         ppen.addPoint((40, 40))
    144         ppen.endPath()
    145         self.assertEqual("10 10 10 40 40 40 None qcurveto closepath", repr(pen))
    146 
    147     def test_roundTrip1(self):
    148         tpen = _TestPointPen()
    149         ppen = PointToSegmentPen(SegmentToPointPen(tpen))
    150         ppen.beginPath()
    151         ppen.addPoint((10, 10), "line")
    152         ppen.addPoint((10, 20))
    153         ppen.addPoint((20, 20))
    154         ppen.addPoint((20, 40), "curve")
    155         ppen.endPath()
    156         self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') addPoint((10, 20)) "
    157                          "addPoint((20, 20)) addPoint((20, 40), segmentType='curve') endPath()",
    158                          repr(tpen))
    159 
    160 
    161 class TestSegmentToPointPen(unittest.TestCase):
    162 
    163     def test_move(self):
    164         tpen = _TestPointPen()
    165         pen = SegmentToPointPen(tpen)
    166         pen.moveTo((10, 10))
    167         pen.endPath()
    168         self.assertEqual("beginPath() addPoint((10, 10), segmentType='move') endPath()",
    169                          repr(tpen))
    170 
    171     def test_poly(self):
    172         tpen = _TestPointPen()
    173         pen = SegmentToPointPen(tpen)
    174         pen.moveTo((10, 10))
    175         pen.lineTo((10, 20))
    176         pen.lineTo((20, 20))
    177         pen.closePath()
    178         self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') "
    179                          "addPoint((10, 20), segmentType='line') "
    180                          "addPoint((20, 20), segmentType='line') endPath()",
    181                          repr(tpen))
    182 
    183     def test_cubic(self):
    184         tpen = _TestPointPen()
    185         pen = SegmentToPointPen(tpen)
    186         pen.moveTo((10, 10))
    187         pen.curveTo((10, 20), (20, 20), (20, 10))
    188         pen.closePath()
    189         self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') "
    190                          "addPoint((10, 20)) addPoint((20, 20)) addPoint((20, 10), "
    191                          "segmentType='curve') endPath()", repr(tpen))
    192 
    193     def test_quad(self):
    194         tpen = _TestPointPen()
    195         pen = SegmentToPointPen(tpen)
    196         pen.moveTo((10, 10))
    197         pen.qCurveTo((10, 20), (20, 20), (20, 10))
    198         pen.closePath()
    199         self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') "
    200                          "addPoint((10, 20)) addPoint((20, 20)) "
    201                          "addPoint((20, 10), segmentType=qcurve) endPath()",
    202                          repr(tpen))
    203 
    204     def test_quad(self):
    205         tpen = _TestPointPen()
    206         pen = SegmentToPointPen(tpen)
    207         pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
    208         pen.closePath()
    209         self.assertEqual("beginPath() addPoint((10, 20)) addPoint((20, 20)) "
    210                          "addPoint((20, 10)) addPoint((10, 10)) endPath()",
    211                          repr(tpen))
    212 
    213     def test_roundTrip1(self):
    214         spen = _TestSegmentPen()
    215         pen = SegmentToPointPen(PointToSegmentPen(spen))
    216         pen.moveTo((10, 10))
    217         pen.lineTo((10, 20))
    218         pen.lineTo((20, 20))
    219         pen.closePath()
    220         self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(spen))
    221 
    222     def test_roundTrip2(self):
    223         spen = _TestSegmentPen()
    224         pen = SegmentToPointPen(PointToSegmentPen(spen))
    225         pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
    226         pen.closePath()
    227         pen.addComponent('base', [1, 0, 0, 1, 0, 0])
    228         self.assertEqual("10 20 20 20 20 10 10 10 None qcurveto closepath "
    229                          "'base' [1, 0, 0, 1, 0, 0] addcomponent",
    230                          repr(spen))
    231 
    232 
    233 class TestGuessSmoothPointPen(unittest.TestCase):
    234 
    235     def test_guessSmooth_exact(self):
    236         tpen = _TestPointPen()
    237         pen = GuessSmoothPointPen(tpen)
    238         pen.beginPath(identifier="foo")
    239         pen.addPoint((0, 100), segmentType="curve")
    240         pen.addPoint((0, 200))
    241         pen.addPoint((400, 200), identifier='bar')
    242         pen.addPoint((400, 100), segmentType="curve")
    243         pen.addPoint((400, 0))
    244         pen.addPoint((0, 0))
    245         pen.endPath()
    246         self.assertEqual("beginPath(identifier='foo') "
    247                          "addPoint((0, 100), segmentType='curve', smooth=True) "
    248                          "addPoint((0, 200)) addPoint((400, 200), identifier='bar') "
    249                          "addPoint((400, 100), segmentType='curve', smooth=True) "
    250                          "addPoint((400, 0)) addPoint((0, 0)) endPath()",
    251                          repr(tpen))
    252 
    253     def test_guessSmooth_almost(self):
    254         tpen = _TestPointPen()
    255         pen = GuessSmoothPointPen(tpen)
    256         pen.beginPath()
    257         pen.addPoint((0, 100), segmentType="curve")
    258         pen.addPoint((1, 200))
    259         pen.addPoint((395, 200))
    260         pen.addPoint((400, 100), segmentType="curve")
    261         pen.addPoint((400, 0))
    262         pen.addPoint((0, 0))
    263         pen.endPath()
    264         self.assertEqual("beginPath() addPoint((0, 100), segmentType='curve', smooth=True) "
    265                          "addPoint((1, 200)) addPoint((395, 200)) "
    266                          "addPoint((400, 100), segmentType='curve', smooth=True) "
    267                          "addPoint((400, 0)) addPoint((0, 0)) endPath()",
    268                          repr(tpen))
    269 
    270     def test_guessSmooth_tangent(self):
    271         tpen = _TestPointPen()
    272         pen = GuessSmoothPointPen(tpen)
    273         pen.beginPath()
    274         pen.addPoint((0, 0), segmentType="move")
    275         pen.addPoint((0, 100), segmentType="line")
    276         pen.addPoint((3, 200))
    277         pen.addPoint((300, 200))
    278         pen.addPoint((400, 200), segmentType="curve")
    279         pen.endPath()
    280         self.assertEqual("beginPath() addPoint((0, 0), segmentType='move') "
    281                          "addPoint((0, 100), segmentType='line', smooth=True) "
    282                          "addPoint((3, 200)) addPoint((300, 200)) "
    283                          "addPoint((400, 200), segmentType='curve') endPath()",
    284                          repr(tpen))
    285 
    286 class TestReverseContourPointPen(unittest.TestCase):
    287 
    288     def test_singlePoint(self):
    289         tpen = _TestPointPen()
    290         pen = ReverseContourPointPen(tpen)
    291         pen.beginPath()
    292         pen.addPoint((0, 0), segmentType="move")
    293         pen.endPath()
    294         self.assertEqual("beginPath() "
    295                          "addPoint((0, 0), segmentType='move') "
    296                          "endPath()",
    297                          repr(tpen))
    298 
    299     def test_line(self):
    300         tpen = _TestPointPen()
    301         pen = ReverseContourPointPen(tpen)
    302         pen.beginPath()
    303         pen.addPoint((0, 0), segmentType="move")
    304         pen.addPoint((0, 100), segmentType="line")
    305         pen.endPath()
    306         self.assertEqual("beginPath() "
    307                          "addPoint((0, 100), segmentType='move') "
    308                          "addPoint((0, 0), segmentType='line') "
    309                          "endPath()",
    310                          repr(tpen))
    311 
    312     def test_triangle(self):
    313         tpen = _TestPointPen()
    314         pen = ReverseContourPointPen(tpen)
    315         pen.beginPath()
    316         pen.addPoint((0, 0), segmentType="line")
    317         pen.addPoint((0, 100), segmentType="line")
    318         pen.addPoint((100, 100), segmentType="line")
    319         pen.endPath()
    320         self.assertEqual("beginPath() "
    321                          "addPoint((0, 0), segmentType='line') "
    322                          "addPoint((100, 100), segmentType='line') "
    323                          "addPoint((0, 100), segmentType='line') "
    324                          "endPath()",
    325                          repr(tpen))
    326 
    327     def test_cubicOpen(self):
    328         tpen = _TestPointPen()
    329         pen = ReverseContourPointPen(tpen)
    330         pen.beginPath()
    331         pen.addPoint((0, 0), segmentType="move")
    332         pen.addPoint((0, 100))
    333         pen.addPoint((100, 200))
    334         pen.addPoint((200, 200), segmentType="curve")
    335         pen.endPath()
    336         self.assertEqual("beginPath() "
    337                          "addPoint((200, 200), segmentType='move') "
    338                          "addPoint((100, 200)) "
    339                          "addPoint((0, 100)) "
    340                          "addPoint((0, 0), segmentType='curve') "
    341                          "endPath()",
    342                          repr(tpen))
    343 
    344     def test_quadOpen(self):
    345         tpen = _TestPointPen()
    346         pen = ReverseContourPointPen(tpen)
    347         pen.beginPath()
    348         pen.addPoint((0, 0), segmentType="move")
    349         pen.addPoint((0, 100))
    350         pen.addPoint((100, 200))
    351         pen.addPoint((200, 200), segmentType="qcurve")
    352         pen.endPath()
    353         self.assertEqual("beginPath() "
    354                          "addPoint((200, 200), segmentType='move') "
    355                          "addPoint((100, 200)) "
    356                          "addPoint((0, 100)) "
    357                          "addPoint((0, 0), segmentType='qcurve') "
    358                          "endPath()",
    359                          repr(tpen))
    360 
    361     def test_cubicClosed(self):
    362         tpen = _TestPointPen()
    363         pen = ReverseContourPointPen(tpen)
    364         pen.beginPath()
    365         pen.addPoint((0, 0), segmentType="line")
    366         pen.addPoint((0, 100))
    367         pen.addPoint((100, 200))
    368         pen.addPoint((200, 200), segmentType="curve")
    369         pen.endPath()
    370         self.assertEqual("beginPath() "
    371                          "addPoint((0, 0), segmentType='curve') "
    372                          "addPoint((200, 200), segmentType='line') "
    373                          "addPoint((100, 200)) "
    374                          "addPoint((0, 100)) "
    375                          "endPath()",
    376                          repr(tpen))
    377 
    378     def test_quadClosedOffCurveStart(self):
    379         tpen = _TestPointPen()
    380         pen = ReverseContourPointPen(tpen)
    381         pen.beginPath()
    382         pen.addPoint((100, 200))
    383         pen.addPoint((200, 200), segmentType="qcurve")
    384         pen.addPoint((0, 0), segmentType="line")
    385         pen.addPoint((0, 100))
    386         pen.endPath()
    387         self.assertEqual("beginPath() "
    388                          "addPoint((100, 200)) "
    389                          "addPoint((0, 100)) "
    390                          "addPoint((0, 0), segmentType='qcurve') "
    391                          "addPoint((200, 200), segmentType='line') "
    392                          "endPath()",
    393                          repr(tpen))
    394 
    395     def test_quadNoOnCurve(self):
    396         tpen = _TestPointPen()
    397         pen = ReverseContourPointPen(tpen)
    398         pen.beginPath(identifier='bar')
    399         pen.addPoint((0, 0))
    400         pen.addPoint((0, 100), identifier='foo', arbitrary='foo')
    401         pen.addPoint((100, 200), arbitrary=123)
    402         pen.addPoint((200, 200))
    403         pen.endPath()
    404         pen.addComponent("base", [1, 0, 0, 1, 0, 0], identifier='foo')
    405         self.assertEqual("beginPath(identifier='bar') "
    406                          "addPoint((0, 0)) "
    407                          "addPoint((200, 200)) "
    408                          "addPoint((100, 200), arbitrary=123) "
    409                          "addPoint((0, 100), identifier='foo', arbitrary='foo') "
    410                          "endPath() "
    411                          "addComponent('base', [1, 0, 0, 1, 0, 0], identifier='foo')",
    412                          repr(tpen))
    413