| # 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.psCharStrings import T2CharString |
| from fontTools.pens.basePen import BasePen |
| |
| |
| class RelativeCoordinatePen(BasePen): |
| |
| def __init__(self, glyphSet): |
| BasePen.__init__(self, glyphSet) |
| self._lastX = None |
| self._lastY = None |
| self._heldAbsoluteMove = None |
| |
| def _makePointRelative(self, pt): |
| absX, absY = pt |
| absX = absX |
| absY = absY |
| # no points have been added |
| # so no conversion is needed |
| if self._lastX is None: |
| relX, relY = absX, absY |
| # otherwise calculate the relative coordinates |
| else: |
| relX = absX - self._lastX |
| relY = absY - self._lastY |
| # store the absolute coordinates |
| self._lastX = absX |
| self._lastY = absY |
| # now return the relative coordinates |
| return relX, relY |
| |
| def _moveTo(self, pt): |
| self._heldAbsoluteMove = pt |
| |
| def _releaseHeldMove(self): |
| if self._heldAbsoluteMove is not None: |
| pt = self._makePointRelative(self._heldAbsoluteMove) |
| self._relativeMoveTo(pt) |
| self._heldAbsoluteMove = None |
| |
| def _relativeMoveTo(self, pt): |
| raise NotImplementedError |
| |
| def _lineTo(self, pt): |
| self._releaseHeldMove() |
| pt = self._makePointRelative(pt) |
| self._relativeLineTo(pt) |
| |
| def _relativeLineTo(self, pt): |
| raise NotImplementedError |
| |
| def _curveToOne(self, pt1, pt2, pt3): |
| self._releaseHeldMove() |
| pt1 = self._makePointRelative(pt1) |
| pt2 = self._makePointRelative(pt2) |
| pt3 = self._makePointRelative(pt3) |
| self._relativeCurveToOne(pt1, pt2, pt3) |
| |
| def _relativeCurveToOne(self, pt1, pt2, pt3): |
| raise NotImplementedError |
| |
| |
| def makeRoundFunc(tolerance): |
| if tolerance < 0: |
| raise ValueError("Rounding tolerance must be positive") |
| |
| def _round(number): |
| if tolerance == 0: |
| return number # no-op |
| rounded = round(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 roundPoint(point): |
| x, y = point |
| return _round(x), _round(y) |
| |
| return roundPoint |
| |
| |
| class T2CharStringPen(RelativeCoordinatePen): |
| """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): |
| RelativeCoordinatePen.__init__(self, glyphSet) |
| self.roundPoint = makeRoundFunc(roundTolerance) |
| self._heldMove = None |
| self._program = [] |
| if width is not None: |
| self._program.append(round(width)) |
| |
| def _moveTo(self, pt): |
| RelativeCoordinatePen._moveTo(self, self.roundPoint(pt)) |
| |
| def _relativeMoveTo(self, pt): |
| pt = self.roundPoint(pt) |
| x, y = pt |
| self._heldMove = [x, y, "rmoveto"] |
| |
| def _storeHeldMove(self): |
| if self._heldMove is not None: |
| self._program.extend(self._heldMove) |
| self._heldMove = None |
| |
| def _lineTo(self, pt): |
| RelativeCoordinatePen._lineTo(self, self.roundPoint(pt)) |
| |
| def _relativeLineTo(self, pt): |
| self._storeHeldMove() |
| pt = self.roundPoint(pt) |
| x, y = pt |
| self._program.extend([x, y, "rlineto"]) |
| |
| def _curveToOne(self, pt1, pt2, pt3): |
| RelativeCoordinatePen._curveToOne(self, |
| self.roundPoint(pt1), |
| self.roundPoint(pt2), |
| self.roundPoint(pt3)) |
| |
| def _relativeCurveToOne(self, pt1, pt2, pt3): |
| self._storeHeldMove() |
| pt1 = self.roundPoint(pt1) |
| pt2 = self.roundPoint(pt2) |
| pt3 = self.roundPoint(pt3) |
| x1, y1 = pt1 |
| x2, y2 = pt2 |
| x3, y3 = pt3 |
| self._program.extend([x1, y1, x2, y2, x3, y3, "rrcurveto"]) |
| |
| def _closePath(self): |
| pass |
| |
| def _endPath(self): |
| pass |
| |
| def getCharString(self, private=None, globalSubrs=None): |
| program = self._program + ["endchar"] |
| charString = T2CharString( |
| program=program, private=private, globalSubrs=globalSubrs) |
| return charString |