[qu2cuPen] Process multiple qCurveTo's at a time

This commit is contained in:
Behdad Esfahbod 2023-02-17 15:02:06 -07:00
parent 8c88184413
commit 1a10b05c99
2 changed files with 36 additions and 21 deletions

View File

@ -13,12 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from fontTools.qu2cu import quadratic_to_curves
from fontTools.pens.filterPen import FilterPen
from fontTools.qu2cu import quadratics_to_curves
from fontTools.pens.filterPen import ContourFilterPen
from fontTools.pens.reverseContourPen import ReverseContourPen
class Qu2CuPen(FilterPen):
class Qu2CuPen(ContourFilterPen):
"""A filter pen to convert quadratic bezier splines to cubic curves
using the FontTools SegmentPen protocol.
@ -45,21 +45,30 @@ class Qu2CuPen(FilterPen):
self.max_err = max_err
self.stats = stats
def _quadratic_to_curve(self, points):
quadratics = (self.current_pt,) + points
curves = quadratic_to_curves(quadratics, self.max_err)
def _quadratics_to_curve(self, q):
curves = quadratics_to_curves(q, self.max_err)
if self.stats is not None:
n = str(len(curves))
self.stats[n] = self.stats.get(n, 0) + 1
if len(quadratics) <= len(curves) * 3:
super().qCurveTo(*quadratics)
else:
for curve in curves:
self.curveTo(*curve[1:])
for curve in curves:
if len(curve) == 4:
yield ("curveTo", curve[1:])
else:
yield ("qCurveTo", curve[1:])
def qCurveTo(self, *points):
n = len(points)
if n <= 3 or points[-1] is None:
super().qCurveTo(*points)
else:
self._quadratic_to_curve(points)
def filterContour(self, contour):
quadratics = []
currentPt = None
newContour = []
for op,args in contour:
if op == 'qCurveTo' and len(args) > 2 and args[-1] is not None:
quadratics.append((currentPt,) + args)
else:
if quadratics:
newContour.extend(self._quadratics_to_curve(quadratics))
quadratics = []
newContour.append((op, args))
currentPt = args[-1] if args else None
if quadratics:
newContour.extend(self._quadratics_to_curve(quadratics))
return newContour

View File

@ -171,9 +171,9 @@ def quadratics_to_curves(pp, tolerance=0.5):
q = [pp[0][0]]
for p in pp:
assert q[-1] == p[0]
q.extend(spline_to_curves(q)[1:])
q.extend(add_implicit_on_curves(p)[1:])
q = add_implicit_on_curves(q)
curves = spline_to_curves(q, tolerance)
if not is_complex:
curves = [tuple((c.real, c.imag) for c in curve) for curve in curves]
@ -210,7 +210,10 @@ def spline_to_curves(q, tolerance=0.5):
for j in range(0, i):
# Fit elevated_quadratics[j:i] into one cubic
curve, ts = merge_curves(elevated_quadratics[j:i])
try:
curve, ts = merge_curves(elevated_quadratics[j:i])
except ZeroDivisionError:
continue
# Now reconstruct the segments from the fitted curve
reconstructed_iter = splitCubicAtTC(*curve, *ts)
@ -265,7 +268,10 @@ def spline_to_curves(q, tolerance=0.5):
curves = []
j = 0
for i in reversed(splits):
curves.append(merge_curves(elevated_quadratics[j:i])[0])
if j + 1 == i:
curves.append(q[j:j+3])
else:
curves.append(merge_curves(elevated_quadratics[j:i])[0])
j = i
return curves