Example #1
0
 def endPath(self) -> None:
     """
     End the current sub path.
     """
     # TrueType contours are always "closed"
     if self._isClosed():
         raise PenError("Contour is already closed.")
     if self._currentContourStartIndex == len(self.points):
         raise PenError("Tried to end an empty contour.")
     self.endPts.append(len(self.points) - 1)
     self._currentContourStartIndex = None
Example #2
0
 def beginPath(self, identifier=None, **kwargs):
     if self._points is not None:
         raise PenError("Path already begun")
     self._points = []
     if identifier is not None:
         kwargs["identifier"] = identifier
     self._outPen.beginPath(**kwargs)
Example #3
0
    def glyph(self, componentFlags: int = 0x4) -> Glyph:
        """
        Returns a :py:class:`~._g_l_y_f.Glyph` object representing the glyph.
        """
        if not self._isClosed():
            raise PenError("Didn't close last contour.")
        components = self._buildComponents(componentFlags)

        glyph = Glyph()
        glyph.coordinates = GlyphCoordinates(self.points)
        glyph.coordinates.toInt()
        glyph.endPtsOfContours = self.endPts
        glyph.flags = array("B", self.types)
        self.init()

        if components:
            # If both components and contours were present, they have by now
            # been decomposed by _buildComponents.
            glyph.components = components
            glyph.numberOfContours = -1
        else:
            glyph.numberOfContours = len(glyph.endPtsOfContours)
            glyph.program = ttProgram.Program()
            glyph.program.fromBytecode(b"")

        return glyph
Example #4
0
 def addComponent(self, glyphName, transform, identifier=None, **kwargs):
     if self.currentContour is not None:
         raise PenError("Components must be added before or after contours")
     self.pen.addComponent(glyphName,
                           transform,
                           identifier=identifier,
                           **kwargs)
	def addPoint(self, pt, segmentType=None, smooth=False, name=None,
				 identifier=None, **kwargs):
		if self._points is None:
			raise PenError("Path not begun")
		if identifier is not None:
			kwargs["identifier"] = identifier
		self._points.append((pt, segmentType, False, name, kwargs))
Example #6
0
 def _qCurveToOne(self, p1, p2):
     if not (self.contours and len(self.contours[-1].points) > 0):
         raise PenError("Contour missing required initial moveTo")
     t1, t2 = FT_CURVE_TAG_CONIC, FT_CURVE_TAG_ON
     contour = self.contours[-1]
     for p, t in ((p1, t1), (p2, t2)):
         contour.points.append(p)
         contour.tags.append(t)
Example #7
0
 def curveTo(self, *pts):
     if not pts:
         raise TypeError("Must pass in at least one point")
     if self.contour is None:
         raise PenError("Contour missing required initial moveTo")
     for pt in pts[:-1]:
         self.contour.append((pt, None))
     self.contour.append((pts[-1], "curve"))
Example #8
0
 def beginPath(self,
               identifier: Optional[str] = None,
               **kwargs: Any) -> None:
     """
     Start a new sub path.
     """
     if not self._isClosed():
         raise PenError("Didn't close previous contour.")
     self._currentContourStartIndex = len(self.points)
Example #9
0
 def addComponent(self,
                  glyphName,
                  transformation,
                  identifier=None,
                  **kwargs):
     if self._points is not None:
         raise PenError("Components must be added before or after contours")
     if identifier is not None:
         kwargs["identifier"] = identifier
     self._outPen.addComponent(glyphName, transformation, **kwargs)
Example #10
0
 def addPoint(self,
              pt,
              segmentType=None,
              smooth=False,
              name=None,
              identifier=None,
              **kwargs):
     if self.currentPath is None:
         raise PenError("Path not begun")
     self.currentPath.append((pt, segmentType, smooth, name, kwargs))
Example #11
0
 def closePath(self):
     if self.contour is None:
         raise PenError("Contour missing required initial moveTo")
     if len(self.contour) > 1 and self.contour[0][0] == self.contour[-1][0]:
         self.contour[0] = self.contour[-1]
         del self.contour[-1]
     else:
         # There's an implied line at the end, replace "move" with "line"
         # for the first point
         pt, tp = self.contour[0]
         if tp == "move":
             self.contour[0] = pt, "line"
     self._flushContour()
     self.contour = None
Example #12
0
    def endPath(self):
        if self.currentPath is None:
            raise PenError("Path not begun.")
        points = self.currentPath
        self.currentPath = None
        if not points:
            return
        if len(points) == 1:
            # Not much more we can do than output a single move segment.
            pt, segmentType, smooth, name, kwargs = points[0]
            segments = [("move", [(pt, smooth, name, kwargs)])]
            self._flushContour(segments)
            return
        segments = []
        if points[0][1] == "move":
            # It's an open contour, insert a "move" segment for the first
            # point and remove that first point from the point list.
            pt, segmentType, smooth, name, kwargs = points[0]
            segments.append(("move", [(pt, smooth, name, kwargs)]))
            points.pop(0)
        else:
            # It's a closed contour. Locate the first on-curve point, and
            # rotate the point list so that it _ends_ with an on-curve
            # point.
            firstOnCurve = None
            for i in range(len(points)):
                segmentType = points[i][1]
                if segmentType is not None:
                    firstOnCurve = i
                    break
            if firstOnCurve is None:
                # Special case for quadratics: a contour with no on-curve
                # points. Add a "None" point. (See also the Pen protocol's
                # qCurveTo() method and fontTools.pens.basePen.py.)
                points.append((None, "qcurve", None, None, None))
            else:
                points = points[firstOnCurve + 1:] + points[:firstOnCurve + 1]

        currentSegment = []
        for pt, segmentType, smooth, name, kwargs in points:
            currentSegment.append((pt, smooth, name, kwargs))
            if segmentType is None:
                continue
            segments.append((segmentType, currentSegment))
            currentSegment = []

        self._flushContour(segments)
Example #13
0
    def _flushContour(self):
        if self._points is None:
            raise PenError("Path not begun")
        points = self._points
        nPoints = len(points)
        if not nPoints:
            return
        if points[0][1] == "move":
            # Open path.
            indices = range(1, nPoints - 1)
        elif nPoints > 1:
            # Closed path. To avoid having to mod the contour index, we
            # simply abuse Python's negative index feature, and start at -1
            indices = range(-1, nPoints - 1)
        else:
            # closed path containing 1 point (!), ignore.
            indices = []
        for i in indices:
            pt, segmentType, _, name, kwargs = points[i]
            if segmentType is None:
                continue
            prev = i - 1
            next = i + 1
            if points[prev][1] is not None and points[next][1] is not None:
                continue
            # At least one of our neighbors is an off-curve point
            pt = points[i][0]
            prevPt = points[prev][0]
            nextPt = points[next][0]
            if pt != prevPt and pt != nextPt:
                dx1, dy1 = pt[0] - prevPt[0], pt[1] - prevPt[1]
                dx2, dy2 = nextPt[0] - pt[0], nextPt[1] - pt[1]
                a1 = math.atan2(dx1, dy1)
                a2 = math.atan2(dx2, dy2)
                if abs(a1 - a2) < 0.05:
                    points[i] = pt, segmentType, True, name, kwargs

        for pt, segmentType, smooth, name, kwargs in points:
            self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
    def addPoint(
        self,
        pt: Tuple[float, float],
        segmentType: Optional[str] = None,
        smooth: bool = False,
        name: Optional[str] = None,
        identifier: Optional[str] = None,
        **kwargs: Any,
    ) -> None:
        """
        Add a point to the current sub path.
        """
        if self._isClosed():
            raise PenError("Can't add a point to a closed contour.")
        if segmentType is None:
            self.types.append(0)  # offcurve
        elif segmentType in ("qcurve", "line"):
            self.types.append(1)  # oncurve
        else:
            # cubic curves are not supported
            raise NotImplementedError

        self.points.append(pt)
Example #15
0
 def _flushContour(self, segments):
     if not segments:
         raise PenError("Must have at least one segment.")
     pen = self.pen
     if segments[0][0] == "move":
         # It's an open path.
         closed = False
         points = segments[0][1]
         if len(points) != 1:
             raise PenError(
                 f"Illegal move segment point count: {len(points)}")
         movePt, _, _, _ = points[0]
         del segments[0]
     else:
         # It's a closed path, do a moveTo to the last
         # point of the last segment.
         closed = True
         segmentType, points = segments[-1]
         movePt, _, _, _ = points[-1]
     if movePt is None:
         # quad special case: a contour with no on-curve points contains
         # one "qcurve" segment that ends with a point that's None. We
         # must not output a moveTo() in that case.
         pass
     else:
         pen.moveTo(movePt)
     outputImpliedClosingLine = self.outputImpliedClosingLine
     nSegments = len(segments)
     lastPt = movePt
     for i in range(nSegments):
         segmentType, points = segments[i]
         points = [pt for pt, _, _, _ in points]
         if segmentType == "line":
             if len(points) != 1:
                 raise PenError(
                     f"Illegal line segment point count: {len(points)}")
             pt = points[0]
             # For closed contours, a 'lineTo' is always implied from the last oncurve
             # point to the starting point, thus we can omit it when the last and
             # starting point don't overlap.
             # However, when the last oncurve point is a "line" segment and has same
             # coordinates as the starting point of a closed contour, we need to output
             # the closing 'lineTo' explicitly (regardless of the value of the
             # 'outputImpliedClosingLine' option) in order to disambiguate this case from
             # the implied closing 'lineTo', otherwise the duplicate point would be lost.
             # See https://github.com/googlefonts/fontmake/issues/572.
             if (i + 1 != nSegments or outputImpliedClosingLine
                     or not closed or pt == lastPt):
                 pen.lineTo(pt)
                 lastPt = pt
         elif segmentType == "curve":
             pen.curveTo(*points)
             lastPt = points[-1]
         elif segmentType == "qcurve":
             pen.qCurveTo(*points)
             lastPt = points[-1]
         else:
             raise PenError(f"Illegal segmentType: {segmentType}")
     if closed:
         pen.closePath()
     else:
         pen.endPath()
Example #16
0
 def moveTo(self, pt: Tuple[float, float]) -> None:
     if not self._isClosed():
         raise PenError('"move"-type point must begin a new contour.')
     self._addPoint(pt, 1)
Example #17
0
 def lineTo(self, pt):
     if self.contour is None:
         raise PenError("Contour missing required initial moveTo")
     self.contour.append((pt, "line"))
Example #18
0
 def _lineTo(self, pt):
     if not (self.contours and len(self.contours[-1].points) > 0):
         raise PenError("Contour missing required initial moveTo")
     contour = self.contours[-1]
     contour.points.append(pt)
     contour.tags.append(FT_CURVE_TAG_ON)
Example #19
0
 def beginPath(self, identifier=None, **kwargs):
     if self.currentPath is not None:
         raise PenError("Path already begun.")
     self.currentPath = []
Example #20
0
 def endPath(self):
     if self.contour is None:
         raise PenError("Contour missing required initial moveTo")
     self._flushContour()
     self.contour = None
Example #21
0
 def endPath(self):
     if self.currentContour is None:
         raise PenError("Path not begun")
     self._flushContour()
     self.currentContour = None
Example #22
0
 def beginPath(self, identifier=None, **kwargs):
     if self.currentContour is not None:
         raise PenError("Path already begun")
     self.currentContour = []
     self.currentContourIdentifier = identifier
     self.onCurve = []
Example #23
0
 def addComponent(self, glyphName, transform):
     if self.contour is not None:
         raise PenError("Components must be added before or after contours")
     self.pen.addComponent(glyphName, transform)