blob: d8e0338dc708b2daa1032db2bd6a8ea8a5581347 [file] [log] [blame]
# 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