def _split_super_bezier_segments(self, points): sub_segments = [] # n is the number of control points n = len(points) - 1 if n == 2: # a simple bezier curve segment sub_segments.append(points) elif n > 2: # a "super" bezier; decompose it on_curve, smooth, name, kwargs = points[-1] num_sub_segments = n - 1 for i, sub_points in enumerate( decomposeSuperBezierSegment([pt for pt, _, _, _ in points])): new_segment = [] for point in sub_points[:-1]: new_segment.append((point, False, None, {})) if i == (num_sub_segments - 1): # the last on-curve keeps its original attributes new_segment.append((on_curve, smooth, name, kwargs)) else: # on-curves of sub-segments are always "smooth" new_segment.append((sub_points[-1], True, None, {})) sub_segments.append(new_segment) else: raise AssertionError("expected 2 control points, found: %d" % n) return sub_segments
def _split_super_bezier_segments(self, points): sub_segments = [] # n is the number of control points n = len(points) - 1 if n == 2: # a simple bezier curve segment sub_segments.append(points) elif n > 2: # a "super" bezier; decompose it on_curve, smooth, name, kwargs = points[-1] num_sub_segments = n - 1 for i, sub_points in enumerate(decomposeSuperBezierSegment([ pt for pt, _, _, _ in points])): new_segment = [] for point in sub_points[:-1]: new_segment.append((point, False, None, {})) if i == (num_sub_segments - 1): # the last on-curve keeps its original attributes new_segment.append((on_curve, smooth, name, kwargs)) else: # on-curves of sub-segments are always "smooth" new_segment.append((sub_points[-1], True, None, {})) sub_segments.append(new_segment) else: raise AssertionError( "expected 2 control points, found: %d" % n) return sub_segments
def curveTo(self, *points): self._check_contour_is_open() n = len(points) if n == 3: # this is the most common case, so we special-case it self._curve_to_quadratic(*points) elif n > 3: for segment in decomposeSuperBezierSegment(points): self._curve_to_quadratic(*segment) elif n == 2: self.qCurveTo(*points) elif n == 1: self.lineTo(points[0]) else: raise AssertionError("illegal curve segment point count: %d" % n)
def curveTo(self, *points): self._check_contour_is_open() n = len(points) if n == 3: # this is the most common case, so we special-case it self._curve_to_quadratic(*points) elif n > 3: for segment in decomposeSuperBezierSegment(points): self._curve_to_quadratic(*segment) elif n == 2: self.qCurveTo(*points) elif n == 1: self.lineTo(points[0]) else: raise AssertionError("illegal curve segment point count: %d" % n)
def endPath(self): # Love is... abstracting away FL's madness. path = self.currentPath self.currentPath = None glyph = self.glyph if len(path) == 1 and path[0][3] is not None: # Single point on the contour, it has a name. Make it an anchor. x, y = path[0][0] name = path[0][3] anchor = Anchor(name, roundInt(x), roundInt(y)) glyph.anchors.append(anchor) return firstOnCurveIndex = None for i in range(len(path)): if path[i][1] is not None: firstOnCurveIndex = i break if firstOnCurveIndex is None: # TT special case: on-curve-less contour. FL doesn't support that, # so we insert an implied point at the end. x1, y1 = path[0][0] x2, y2 = path[-1][0] impliedPoint = 0.5 * (x1 + x2), 0.5 * (y1 + y2) path.append((impliedPoint, "qcurve", True, None)) firstOnCurveIndex = 0 path = path[firstOnCurveIndex + 1:] + path[:firstOnCurveIndex + 1] firstPoint, segmentType, smooth, name = path[-1] closed = True if segmentType == "move": path = path[:-1] closed = False # XXX The contour is not closed, but I can't figure out how to # create an open contour in FL. Creating one by hand shows type"0x8011" # for a move node in an open contour, but I'm not able to access # that flag. elif segmentType == "line": # The contour is closed and ends in a lineto, which is redundant # as it's implied by closepath. path = path[:-1] x, y = firstPoint node = Node(nMOVE, Point(roundInt(x), roundInt(y))) if smooth and closed: if segmentType == "line" or path[0][1] == "line": node.alignment = nFIXED else: node.alignment = nSMOOTH glyph.Insert(node, len(glyph)) segment = [] nPoints = len(path) for i in range(nPoints): pt, segmentType, smooth, name = path[i] segment.append(pt) if segmentType is None: continue if segmentType == "curve": if len(segment) < 2: segmentType = "line" elif len(segment) == 2: segmentType = "qcurve" if segmentType == "qcurve": for x, y in segment[:-1]: glyph.Insert(Node(nOFF, Point(roundInt(x), roundInt(y))), len(glyph)) x, y = segment[-1] node = Node(nLINE, Point(roundInt(x), roundInt(y))) glyph.Insert(node, len(glyph)) elif segmentType == "curve": if len(segment) == 3: cubicSegments = [segment] else: from fontTools.pens.basePen import decomposeSuperBezierSegment cubicSegments = decomposeSuperBezierSegment(segment) nSegments = len(cubicSegments) for i in range(nSegments): pt1, pt2, pt3 = cubicSegments[i] x, y = pt3 node = Node(nCURVE, Point(roundInt(x), roundInt(y))) node.points[1].x, node.points[1].y = roundInt(pt1[0]), roundInt(pt1[1]) node.points[2].x, node.points[2].y = roundInt(pt2[0]), roundInt(pt2[1]) if i != nSegments - 1: node.alignment = nSMOOTH glyph.Insert(node, len(self.glyph)) elif segmentType == "line": assert len(segment) == 1, segment x, y = segment[0] node = Node(nLINE, Point(roundInt(x), roundInt(y))) glyph.Insert(node, len(glyph)) else: assert 0, "unsupported curve type (%s)" % segmentType if smooth: if i + 1 < nPoints or closed: # Can't use existing node, as you can't change node attributes # AFTER it's been appended to the glyph. node = glyph[-1] if segmentType == "line" or path[(i+1) % nPoints][1] == "line": # tangent node.alignment = nFIXED else: # curve node.alignment = nSMOOTH segment = [] if closed: # we may have output a node too much node = glyph[-1] if node.type == nLINE and (node.x, node.y) == (roundInt(firstPoint[0]), roundInt(firstPoint[1])): glyph.DeleteNode(len(glyph) - 1)