| # Copyright (c) 2009 Type Supply LLC |
| # Author: Tal Leming |
| |
| from __future__ import print_function, division, absolute_import |
| from fontTools.misc.py23 import * |
| from fontTools.misc.fixedTools import otRound |
| from fontTools.misc.psCharStrings import T2CharString |
| from fontTools.pens.basePen import BasePen |
| from fontTools.cffLib.specializer import specializeCommands, commandsToProgram |
| |
| |
| def t2c_round(number, tolerance=0.5): |
| if tolerance == 0: |
| return number # no-op |
| rounded = otRound(number) |
| # return rounded integer if the tolerance >= 0.5, or if the absolute |
| # difference between the original float and the rounded integer is |
| # within the tolerance |
| if tolerance >= .5 or abs(rounded - number) <= tolerance: |
| return rounded |
| else: |
| # else return the value un-rounded |
| return number |
| |
| def makeRoundFunc(tolerance): |
| if tolerance < 0: |
| raise ValueError("Rounding tolerance must be positive") |
| |
| def roundPoint(point): |
| x, y = point |
| return t2c_round(x, tolerance), t2c_round(y, tolerance) |
| |
| return roundPoint |
| |
| |
| class T2CharStringPen(BasePen): |
| """Pen to draw Type 2 CharStrings. |
| |
| The 'roundTolerance' argument controls the rounding of point coordinates. |
| It is defined as the maximum absolute difference between the original |
| float and the rounded integer value. |
| The default tolerance of 0.5 means that all floats are rounded to integer; |
| a value of 0 disables rounding; values in between will only round floats |
| which are close to their integral part within the tolerated range. |
| """ |
| |
| def __init__(self, width, glyphSet, roundTolerance=0.5, CFF2=False): |
| super(T2CharStringPen, self).__init__(glyphSet) |
| self.roundPoint = makeRoundFunc(roundTolerance) |
| self._CFF2 = CFF2 |
| self._width = width |
| self._commands = [] |
| self._p0 = (0,0) |
| |
| def _p(self, pt): |
| p0 = self._p0 |
| pt = self._p0 = self.roundPoint(pt) |
| return [pt[0]-p0[0], pt[1]-p0[1]] |
| |
| def _moveTo(self, pt): |
| self._commands.append(('rmoveto', self._p(pt))) |
| |
| def _lineTo(self, pt): |
| self._commands.append(('rlineto', self._p(pt))) |
| |
| def _curveToOne(self, pt1, pt2, pt3): |
| _p = self._p |
| self._commands.append(('rrcurveto', _p(pt1)+_p(pt2)+_p(pt3))) |
| |
| def _closePath(self): |
| pass |
| |
| def _endPath(self): |
| pass |
| |
| def getCharString(self, private=None, globalSubrs=None, optimize=True): |
| commands = self._commands |
| if optimize: |
| maxstack = 48 if not self._CFF2 else 513 |
| commands = specializeCommands(commands, |
| generalizeFirst=False, |
| maxstack=maxstack) |
| program = commandsToProgram(commands) |
| if self._width is not None: |
| assert not self._CFF2, "CFF2 does not allow encoding glyph width in CharString." |
| program.insert(0, otRound(self._width)) |
| if not self._CFF2: |
| program.append('endchar') |
| charString = T2CharString( |
| program=program, private=private, globalSubrs=globalSubrs) |
| return charString |