• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2009 Type Supply LLC
2# Author: Tal Leming
3
4from __future__ import print_function, division, absolute_import
5from fontTools.misc.py23 import *
6from fontTools.misc.fixedTools import otRound
7from fontTools.misc.psCharStrings import T2CharString
8from fontTools.pens.basePen import BasePen
9from fontTools.cffLib.specializer import specializeCommands, commandsToProgram
10
11
12def t2c_round(number, tolerance=0.5):
13    if tolerance == 0:
14        return number  # no-op
15    rounded = otRound(number)
16    # return rounded integer if the tolerance >= 0.5, or if the absolute
17    # difference between the original float and the rounded integer is
18    # within the tolerance
19    if tolerance >= .5 or abs(rounded - number) <= tolerance:
20        return rounded
21    else:
22        # else return the value un-rounded
23        return number
24
25def makeRoundFunc(tolerance):
26    if tolerance < 0:
27        raise ValueError("Rounding tolerance must be positive")
28
29    def roundPoint(point):
30        x, y = point
31        return t2c_round(x, tolerance), t2c_round(y, tolerance)
32
33    return roundPoint
34
35
36class T2CharStringPen(BasePen):
37    """Pen to draw Type 2 CharStrings.
38
39    The 'roundTolerance' argument controls the rounding of point coordinates.
40    It is defined as the maximum absolute difference between the original
41    float and the rounded integer value.
42    The default tolerance of 0.5 means that all floats are rounded to integer;
43    a value of 0 disables rounding; values in between will only round floats
44    which are close to their integral part within the tolerated range.
45    """
46
47    def __init__(self, width, glyphSet, roundTolerance=0.5, CFF2=False):
48        super(T2CharStringPen, self).__init__(glyphSet)
49        self.roundPoint = makeRoundFunc(roundTolerance)
50        self._CFF2 = CFF2
51        self._width = width
52        self._commands = []
53        self._p0 = (0,0)
54
55    def _p(self, pt):
56        p0 = self._p0
57        pt = self._p0 = self.roundPoint(pt)
58        return [pt[0]-p0[0], pt[1]-p0[1]]
59
60    def _moveTo(self, pt):
61        self._commands.append(('rmoveto', self._p(pt)))
62
63    def _lineTo(self, pt):
64        self._commands.append(('rlineto', self._p(pt)))
65
66    def _curveToOne(self, pt1, pt2, pt3):
67        _p = self._p
68        self._commands.append(('rrcurveto', _p(pt1)+_p(pt2)+_p(pt3)))
69
70    def _closePath(self):
71        pass
72
73    def _endPath(self):
74        pass
75
76    def getCharString(self, private=None, globalSubrs=None, optimize=True):
77        commands = self._commands
78        if optimize:
79            maxstack = 48 if not self._CFF2 else 513
80            commands = specializeCommands(commands,
81                                          generalizeFirst=False,
82                                          maxstack=maxstack)
83        program = commandsToProgram(commands)
84        if self._width is not None:
85            assert not self._CFF2, "CFF2 does not allow encoding glyph width in CharString."
86            program.insert(0, otRound(self._width))
87        if not self._CFF2:
88            program.append('endchar')
89        charString = T2CharString(
90            program=program, private=private, globalSubrs=globalSubrs)
91        return charString
92