prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  [T2CharStringPen] Rounding coordinates?
  Currently, [T2CharStringPen](https://github.com/fonttools/fonttools/blob/fcd697a328b439165b3e9d04904eb73f065b6838/Lib/fontTools/pens/t2CharStringPen.py#L78) always rounds glyph coordinates and casts to int, but it does not seem a good idea. I think we should remove this behavior, or make it optional.
  
  cf. TTGlyphPen (#526)
  
  </issue>
  <code>
  [start of Lib/fontTools/pens/t2CharStringPen.py]
  1 # Copyright (c) 2009 Type Supply LLC
  2 # Author: Tal Leming
  3 
  4 
  5 from __future__ import print_function, division, absolute_import
  6 
  7 from fontTools.misc.py23 import *
  8 from fontTools.misc.psCharStrings import T2CharString
  9 from fontTools.pens.basePen import BasePen
  10 
  11 
  12 def roundInt(v):
  13     return int(round(v))
  14 
  15 
  16 def roundIntPoint(point):
  17     x, y = point
  18     return roundInt(x), roundInt(y)
  19 
  20 
  21 class RelativeCoordinatePen(BasePen):
  22 
  23     def __init__(self, glyphSet):
  24         BasePen.__init__(self, glyphSet)
  25         self._lastX = None
  26         self._lastY = None
  27         self._heldAbsoluteMove = None
  28 
  29     def _makePointRelative(self, pt):
  30         absX, absY = pt
  31         absX = absX
  32         absY = absY
  33         # no points have been added
  34         # so no conversion is needed
  35         if self._lastX is None:
  36             relX, relY = absX, absY
  37         # otherwise calculate the relative coordinates
  38         else:
  39             relX = absX - self._lastX
  40             relY = absY - self._lastY
  41         # store the absolute coordinates
  42         self._lastX = absX
  43         self._lastY = absY
  44         # now return the relative coordinates
  45         return relX, relY
  46 
  47     def _moveTo(self, pt):
  48         self._heldAbsoluteMove = pt
  49 
  50     def _releaseHeldMove(self):
  51         if self._heldAbsoluteMove is not None:
  52             pt = self._makePointRelative(self._heldAbsoluteMove)
  53             self._relativeMoveTo(pt)
  54             self._heldAbsoluteMove = None
  55 
  56     def _relativeMoveTo(self, pt):
  57         raise NotImplementedError
  58 
  59     def _lineTo(self, pt):
  60         self._releaseHeldMove()
  61         pt = self._makePointRelative(pt)
  62         self._relativeLineTo(pt)
  63 
  64     def _relativeLineTo(self, pt):
  65         raise NotImplementedError
  66 
  67     def _curveToOne(self, pt1, pt2, pt3):
  68         self._releaseHeldMove()
  69         pt1 = self._makePointRelative(pt1)
  70         pt2 = self._makePointRelative(pt2)
  71         pt3 = self._makePointRelative(pt3)
  72         self._relativeCurveToOne(pt1, pt2, pt3)
  73 
  74     def _relativeCurveToOne(self, pt1, pt2, pt3):
  75         raise NotImplementedError
  76 
  77 
  78 class T2CharStringPen(RelativeCoordinatePen):
  79 
  80     def __init__(self, width, glyphSet):
  81         RelativeCoordinatePen.__init__(self, glyphSet)
  82         self._heldMove = None
  83         self._program = []
  84         if width is not None:
  85             self._program.append(roundInt(width))
  86 
  87     def _moveTo(self, pt):
  88         RelativeCoordinatePen._moveTo(self, roundIntPoint(pt))
  89 
  90     def _relativeMoveTo(self, pt):
  91         pt = roundIntPoint(pt)
  92         x, y = pt
  93         self._heldMove = [x, y, "rmoveto"]
  94 
  95     def _storeHeldMove(self):
  96         if self._heldMove is not None:
  97             self._program.extend(self._heldMove)
  98             self._heldMove = None
  99 
  100     def _lineTo(self, pt):
  101         RelativeCoordinatePen._lineTo(self, roundIntPoint(pt))
  102 
  103     def _relativeLineTo(self, pt):
  104         self._storeHeldMove()
  105         pt = roundIntPoint(pt)
  106         x, y = pt
  107         self._program.extend([x, y, "rlineto"])
  108 
  109     def _curveToOne(self, pt1, pt2, pt3):
  110         RelativeCoordinatePen._curveToOne(self, roundIntPoint(pt1), roundIntPoint(pt2), roundIntPoint(pt3))
  111 
  112     def _relativeCurveToOne(self, pt1, pt2, pt3):
  113         self._storeHeldMove()
  114         pt1 = roundIntPoint(pt1)
  115         pt2 = roundIntPoint(pt2)
  116         pt3 = roundIntPoint(pt3)
  117         x1, y1 = pt1
  118         x2, y2 = pt2
  119         x3, y3 = pt3
  120         self._program.extend([x1, y1, x2, y2, x3, y3, "rrcurveto"])
  121 
  122     def _closePath(self):
  123         pass
  124 
  125     def _endPath(self):
  126         pass
  127 
  128     def getCharString(self, private=None, globalSubrs=None):
  129         program = self._program + ["endchar"]
  130         charString = T2CharString(program=program, private=private, globalSubrs=globalSubrs)
  131         return charString
  132
  [end of Lib/fontTools/pens/t2CharStringPen.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py
  --- a/Lib/fontTools/pens/t2CharStringPen.py
  +++ b/Lib/fontTools/pens/t2CharStringPen.py
  @@ -9,15 +9,6 @@
   from fontTools.pens.basePen import BasePen
   
   
  -def roundInt(v):
  -    return int(round(v))
  -
  -
  -def roundIntPoint(point):
  -    x, y = point
  -    return roundInt(x), roundInt(y)
  -
  -
   class RelativeCoordinatePen(BasePen):
   
       def __init__(self, glyphSet):
  @@ -75,20 +66,45 @@
           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):
   
  -    def __init__(self, width, glyphSet):
  +    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(roundInt(width))
  +            self._program.append(round(width))
   
       def _moveTo(self, pt):
  -        RelativeCoordinatePen._moveTo(self, roundIntPoint(pt))
  +        RelativeCoordinatePen._moveTo(self, self.roundPoint(pt))
   
       def _relativeMoveTo(self, pt):
  -        pt = roundIntPoint(pt)
  +        pt = self.roundPoint(pt)
           x, y = pt
           self._heldMove = [x, y, "rmoveto"]
   
  @@ -98,22 +114,25 @@
               self._heldMove = None
   
       def _lineTo(self, pt):
  -        RelativeCoordinatePen._lineTo(self, roundIntPoint(pt))
  +        RelativeCoordinatePen._lineTo(self, self.roundPoint(pt))
   
       def _relativeLineTo(self, pt):
           self._storeHeldMove()
  -        pt = roundIntPoint(pt)
  +        pt = self.roundPoint(pt)
           x, y = pt
           self._program.extend([x, y, "rlineto"])
   
       def _curveToOne(self, pt1, pt2, pt3):
  -        RelativeCoordinatePen._curveToOne(self, roundIntPoint(pt1), roundIntPoint(pt2), roundIntPoint(pt3))
  +        RelativeCoordinatePen._curveToOne(self,
  +                                          self.roundPoint(pt1),
  +                                          self.roundPoint(pt2),
  +                                          self.roundPoint(pt3))
   
       def _relativeCurveToOne(self, pt1, pt2, pt3):
           self._storeHeldMove()
  -        pt1 = roundIntPoint(pt1)
  -        pt2 = roundIntPoint(pt2)
  -        pt3 = roundIntPoint(pt3)
  +        pt1 = self.roundPoint(pt1)
  +        pt2 = self.roundPoint(pt2)
  +        pt3 = self.roundPoint(pt3)
           x1, y1 = pt1
           x2, y2 = pt2
           x3, y3 = pt3
  @@ -127,5 +146,6 @@
   
       def getCharString(self, private=None, globalSubrs=None):
           program = self._program + ["endchar"]
  -        charString = T2CharString(program=program, private=private, globalSubrs=globalSubrs)
  +        charString = T2CharString(
  +            program=program, private=private, globalSubrs=globalSubrs)
           return charString
  
verification_info: {"golden_diff": "diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py\n--- a/Lib/fontTools/pens/t2CharStringPen.py\n+++ b/Lib/fontTools/pens/t2CharStringPen.py\n@@ -9,15 +9,6 @@\n from fontTools.pens.basePen import BasePen\n \n \n-def roundInt(v):\n-    return int(round(v))\n-\n-\n-def roundIntPoint(point):\n-    x, y = point\n-    return roundInt(x), roundInt(y)\n-\n-\n class RelativeCoordinatePen(BasePen):\n \n     def __init__(self, glyphSet):\n@@ -75,20 +66,45 @@\n         raise NotImplementedError\n \n \n+def makeRoundFunc(tolerance):\n+    if tolerance < 0:\n+        raise ValueError(\"Rounding tolerance must be positive\")\n+\n+    def _round(number):\n+        if tolerance == 0:\n+            return number  # no-op\n+        rounded = round(number)\n+        # return rounded integer if the tolerance >= 0.5, or if the absolute\n+        # difference between the original float and the rounded integer is\n+        # within the tolerance\n+        if tolerance >= .5 or abs(rounded - number) <= tolerance:\n+            return rounded\n+        else:\n+            # else return the value un-rounded\n+            return number\n+\n+    def roundPoint(point):\n+        x, y = point\n+        return _round(x), _round(y)\n+\n+    return roundPoint\n+\n+\n class T2CharStringPen(RelativeCoordinatePen):\n \n-    def __init__(self, width, glyphSet):\n+    def __init__(self, width, glyphSet, roundTolerance=0.5):\n         RelativeCoordinatePen.__init__(self, glyphSet)\n+        self.roundPoint = makeRoundFunc(roundTolerance)\n         self._heldMove = None\n         self._program = []\n         if width is not None:\n-            self._program.append(roundInt(width))\n+            self._program.append(round(width))\n \n     def _moveTo(self, pt):\n-        RelativeCoordinatePen._moveTo(self, roundIntPoint(pt))\n+        RelativeCoordinatePen._moveTo(self, self.roundPoint(pt))\n \n     def _relativeMoveTo(self, pt):\n-        pt = roundIntPoint(pt)\n+        pt = self.roundPoint(pt)\n         x, y = pt\n         self._heldMove = [x, y, \"rmoveto\"]\n \n@@ -98,22 +114,25 @@\n             self._heldMove = None\n \n     def _lineTo(self, pt):\n-        RelativeCoordinatePen._lineTo(self, roundIntPoint(pt))\n+        RelativeCoordinatePen._lineTo(self, self.roundPoint(pt))\n \n     def _relativeLineTo(self, pt):\n         self._storeHeldMove()\n-        pt = roundIntPoint(pt)\n+        pt = self.roundPoint(pt)\n         x, y = pt\n         self._program.extend([x, y, \"rlineto\"])\n \n     def _curveToOne(self, pt1, pt2, pt3):\n-        RelativeCoordinatePen._curveToOne(self, roundIntPoint(pt1), roundIntPoint(pt2), roundIntPoint(pt3))\n+        RelativeCoordinatePen._curveToOne(self,\n+                                          self.roundPoint(pt1),\n+                                          self.roundPoint(pt2),\n+                                          self.roundPoint(pt3))\n \n     def _relativeCurveToOne(self, pt1, pt2, pt3):\n         self._storeHeldMove()\n-        pt1 = roundIntPoint(pt1)\n-        pt2 = roundIntPoint(pt2)\n-        pt3 = roundIntPoint(pt3)\n+        pt1 = self.roundPoint(pt1)\n+        pt2 = self.roundPoint(pt2)\n+        pt3 = self.roundPoint(pt3)\n         x1, y1 = pt1\n         x2, y2 = pt2\n         x3, y3 = pt3\n@@ -127,5 +146,6 @@\n \n     def getCharString(self, private=None, globalSubrs=None):\n         program = self._program + [\"endchar\"]\n-        charString = T2CharString(program=program, private=private, globalSubrs=globalSubrs)\n+        charString = T2CharString(\n+            program=program, private=private, globalSubrs=globalSubrs)\n         return charString\n", "issue": "[T2CharStringPen] Rounding coordinates?\nCurrently, [T2CharStringPen](https://github.com/fonttools/fonttools/blob/fcd697a328b439165b3e9d04904eb73f065b6838/Lib/fontTools/pens/t2CharStringPen.py#L78) always rounds glyph coordinates and casts to int, but it does not seem a good idea. I think we should remove this behavior, or make it optional.\r\n\r\ncf. TTGlyphPen (#526)\n", "before_files": [{"content": "# Copyright (c) 2009 Type Supply LLC\n# Author: Tal Leming\n\n\nfrom __future__ import print_function, division, absolute_import\n\nfrom fontTools.misc.py23 import *\nfrom fontTools.misc.psCharStrings import T2CharString\nfrom fontTools.pens.basePen import BasePen\n\n\ndef roundInt(v):\n    return int(round(v))\n\n\ndef roundIntPoint(point):\n    x, y = point\n    return roundInt(x), roundInt(y)\n\n\nclass RelativeCoordinatePen(BasePen):\n\n    def __init__(self, glyphSet):\n        BasePen.__init__(self, glyphSet)\n        self._lastX = None\n        self._lastY = None\n        self._heldAbsoluteMove = None\n\n    def _makePointRelative(self, pt):\n        absX, absY = pt\n        absX = absX\n        absY = absY\n        # no points have been added\n        # so no conversion is needed\n        if self._lastX is None:\n            relX, relY = absX, absY\n        # otherwise calculate the relative coordinates\n        else:\n            relX = absX - self._lastX\n            relY = absY - self._lastY\n        # store the absolute coordinates\n        self._lastX = absX\n        self._lastY = absY\n        # now return the relative coordinates\n        return relX, relY\n\n    def _moveTo(self, pt):\n        self._heldAbsoluteMove = pt\n\n    def _releaseHeldMove(self):\n        if self._heldAbsoluteMove is not None:\n            pt = self._makePointRelative(self._heldAbsoluteMove)\n            self._relativeMoveTo(pt)\n            self._heldAbsoluteMove = None\n\n    def _relativeMoveTo(self, pt):\n        raise NotImplementedError\n\n    def _lineTo(self, pt):\n        self._releaseHeldMove()\n        pt = self._makePointRelative(pt)\n        self._relativeLineTo(pt)\n\n    def _relativeLineTo(self, pt):\n        raise NotImplementedError\n\n    def _curveToOne(self, pt1, pt2, pt3):\n        self._releaseHeldMove()\n        pt1 = self._makePointRelative(pt1)\n        pt2 = self._makePointRelative(pt2)\n        pt3 = self._makePointRelative(pt3)\n        self._relativeCurveToOne(pt1, pt2, pt3)\n\n    def _relativeCurveToOne(self, pt1, pt2, pt3):\n        raise NotImplementedError\n\n\nclass T2CharStringPen(RelativeCoordinatePen):\n\n    def __init__(self, width, glyphSet):\n        RelativeCoordinatePen.__init__(self, glyphSet)\n        self._heldMove = None\n        self._program = []\n        if width is not None:\n            self._program.append(roundInt(width))\n\n    def _moveTo(self, pt):\n        RelativeCoordinatePen._moveTo(self, roundIntPoint(pt))\n\n    def _relativeMoveTo(self, pt):\n        pt = roundIntPoint(pt)\n        x, y = pt\n        self._heldMove = [x, y, \"rmoveto\"]\n\n    def _storeHeldMove(self):\n        if self._heldMove is not None:\n            self._program.extend(self._heldMove)\n            self._heldMove = None\n\n    def _lineTo(self, pt):\n        RelativeCoordinatePen._lineTo(self, roundIntPoint(pt))\n\n    def _relativeLineTo(self, pt):\n        self._storeHeldMove()\n        pt = roundIntPoint(pt)\n        x, y = pt\n        self._program.extend([x, y, \"rlineto\"])\n\n    def _curveToOne(self, pt1, pt2, pt3):\n        RelativeCoordinatePen._curveToOne(self, roundIntPoint(pt1), roundIntPoint(pt2), roundIntPoint(pt3))\n\n    def _relativeCurveToOne(self, pt1, pt2, pt3):\n        self._storeHeldMove()\n        pt1 = roundIntPoint(pt1)\n        pt2 = roundIntPoint(pt2)\n        pt3 = roundIntPoint(pt3)\n        x1, y1 = pt1\n        x2, y2 = pt2\n        x3, y3 = pt3\n        self._program.extend([x1, y1, x2, y2, x3, y3, \"rrcurveto\"])\n\n    def _closePath(self):\n        pass\n\n    def _endPath(self):\n        pass\n\n    def getCharString(self, private=None, globalSubrs=None):\n        program = self._program + [\"endchar\"]\n        charString = T2CharString(program=program, private=private, globalSubrs=globalSubrs)\n        return charString\n", "path": "Lib/fontTools/pens/t2CharStringPen.py"}]}