コード例 #1
0
def draw(layer, instance, pen):
    pen = PointToSegmentPen(pen)

    for path in layer.paths:
        nodes = list(path.nodes)

        pen.beginPath()
        if nodes:
            if not path.closed:
                node = nodes.pop(0)
                assert node.type == "line", "Open path starts with off-curve points"
                pen.addPoint(tuple(node.position), segmentType="move")
            else:
                # In Glyphs.app, the starting node of a closed contour is always
                # stored at the end of the nodes list.
                nodes.insert(0, nodes.pop())
            for node in nodes:
                node_type = node.type
                if node_type not in ["line", "curve", "qcurve"]:
                    node_type = None
                pen.addPoint(tuple(node.position), segmentType=node_type, smooth=node.smooth)
        pen.endPath();

    for component in layer.components:
        componentLayer = getLayer(component.component, instance)
        transform = component.transform.value
        componentPen = pen.pen
        if transform != DEFAULT_TRANSFORM:
            componentPen = TransformPen(pen.pen, transform)
            xx, xy, yx, yy = transform[:4]
            if xx * yy - xy * yx < 0:
                componentPen = ReverseContourPen(componentPen)
        draw(componentLayer, instance, componentPen)

    return pen.pen
コード例 #2
0
 def test_quad_onlyOffCurvePoints(self):
     pen = _TestSegmentPen()
     ppen = PointToSegmentPen(pen)
     ppen.beginPath()
     ppen.addPoint((10, 10))
     ppen.addPoint((10, 40))
     ppen.addPoint((40, 40))
     ppen.endPath()
     self.assertEqual("10 10 10 40 40 40 None qcurveto closepath", repr(pen))
コード例 #3
0
 def test_closed(self):
     pen = _TestSegmentPen()
     ppen = PointToSegmentPen(pen)
     ppen.beginPath()
     ppen.addPoint((10, 10), "line")
     ppen.addPoint((10, 20), "line")
     ppen.addPoint((20, 20), "line")
     ppen.endPath()
     self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(pen))
コード例 #4
0
 def test_closed_outputImpliedClosingLine(self):
     tpen = _TestSegmentPen()
     ppen = PointToSegmentPen(tpen, outputImpliedClosingLine=True)
     ppen.beginPath()
     ppen.addPoint((10, 10), "line")
     ppen.addPoint((10, 20), "line")
     ppen.addPoint((20, 20), "line")
     ppen.endPath()
     self.assertEqual(
         "10 10 moveto "
         "10 20 lineto "
         "20 20 lineto "
         "10 10 lineto "  # explicit closing line
         "closepath",
         repr(tpen))
コード例 #5
0
 def draw(self, pen):
     """
     Draw the component with **pen**.
     """
     from fontTools.pens.pointPen import PointToSegmentPen
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen)
コード例 #6
0
 def test_roundTrip2(self):
     tpen = _TestPointPen()
     ppen = PointToSegmentPen(SegmentToPointPen(tpen))
     ppen.beginPath()
     ppen.addPoint((0, 651), segmentType="line")
     ppen.addPoint((0, 101), segmentType="line")
     ppen.addPoint((0, 101), segmentType="line")
     ppen.addPoint((0, 651), segmentType="line")
     ppen.endPath()
     self.assertEqual(
         "beginPath() "
         "addPoint((0, 651), segmentType='line') "
         "addPoint((0, 101), segmentType='line') "
         "addPoint((0, 101), segmentType='line') "
         "addPoint((0, 651), segmentType='line') "
         "endPath()", repr(tpen))
コード例 #7
0
 def test_roundTrip1(self):
     spen = _TestSegmentPen()
     pen = SegmentToPointPen(PointToSegmentPen(spen))
     pen.moveTo((10, 10))
     pen.lineTo((10, 20))
     pen.lineTo((20, 20))
     pen.closePath()
     self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(spen))
コード例 #8
0
 def test_roundTrip2(self):
     spen = _TestSegmentPen()
     pen = SegmentToPointPen(PointToSegmentPen(spen))
     pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
     pen.closePath()
     pen.addComponent('base', [1, 0, 0, 1, 0, 0])
     self.assertEqual(
         "10 20 20 20 20 10 10 10 None qcurveto closepath "
         "'base' [1, 0, 0, 1, 0, 0] addcomponent", repr(spen))
コード例 #9
0
 def test_draw_vs_drawpoints(self):
     font = TTFont(sfntVersion="\x00\x01\x00\x00")
     font.importXML(GLYF_TTX)
     glyfTable = font['glyf']
     pen1 = RecordingPen()
     pen2 = RecordingPen()
     glyfTable["glyph00003"].draw(pen1, glyfTable)
     glyfTable["glyph00003"].drawPoints(PointToSegmentPen(pen2), glyfTable)
     self.assertEqual(pen1.value, pen2.value)
コード例 #10
0
ファイル: pen_test.py プロジェクト: simoncozens/glyphsLib
def test_pen_recording_equivalent(datadir):
    font = classes.GSFont(str(datadir.join("PenTest.glyphs")))

    for glyph in font.glyphs:
        for layer in glyph.layers:
            rpen1 = fontTools.pens.recordingPen.RecordingPen()
            rpen2 = fontTools.pens.recordingPen.RecordingPen()
            layer.draw(rpen1)
            layer.drawPoints(PointToSegmentPen(rpen2))
            assert rpen1.value == rpen2.value
コード例 #11
0
 def test_closed_line_overlapping_start_end_points(self):
     # Test case from https://github.com/googlefonts/fontmake/issues/572.
     tpen = _TestSegmentPen()
     ppen = PointToSegmentPen(tpen, outputImpliedClosingLine=False)
     # The last oncurve point on this closed contour is a "line" segment and has
     # same coordinates as the starting point.
     ppen.beginPath()
     ppen.addPoint((0, 651), segmentType="line")
     ppen.addPoint((0, 101), segmentType="line")
     ppen.addPoint((0, 101), segmentType="line")
     ppen.addPoint((0, 651), segmentType="line")
     ppen.endPath()
     # Check that we always output an explicit 'lineTo' segment at the end,
     # regardless of the value of 'outputImpliedClosingLine', to disambiguate
     # the duplicate point from the implied closing line.
     self.assertEqual(
         "0 651 moveto "
         "0 101 lineto "
         "0 101 lineto "
         "0 651 lineto "
         "0 651 lineto "
         "closepath", repr(tpen))
コード例 #12
0
ファイル: ufo.py プロジェクト: huertatipografica/Andada-Pro
def _get_segments(glyph):
    """Get a glyph's segments as extracted by GetSegmentsPen."""

    pen = GetSegmentsPen()
    # glyph.draw(pen)
    # We can't simply draw the glyph with the pen, but we must initialize the
    # PointToSegmentPen explicitly with outputImpliedClosingLine=True.
    # By default PointToSegmentPen does not outputImpliedClosingLine -- unless
    # last and first point on closed contour are duplicated. Because we are
    # converting multiple glyphs at the same time, we want to make sure
    # this function returns the same number of segments, whether or not
    # the last and first point overlap.
    # https://github.com/googlefonts/fontmake/issues/572
    # https://github.com/fonttools/fonttools/pull/1720
    pointPen = PointToSegmentPen(pen, outputImpliedClosingLine=True)
    glyph.drawPoints(pointPen)
    return pen.segments
コード例 #13
0
 def draw(self, pen):
     ppen = PointToSegmentPen(pen)
     startIndex = 0
     points = self.getPoints()
     for endIndex in self.contours:
         lastTag = self.tags[endIndex]
         endIndex += 1
         contourTags = self.tags[startIndex:endIndex]
         contourPoints = points[startIndex:endIndex]
         ppen.beginPath()
         for tag, (x, y) in zip(contourTags, contourPoints):
             if tag == FT_CURVE_TAG_ON:
                 segmentType = segmentTypes[lastTag]
             else:
                 segmentType = None
             ppen.addPoint((x, y), segmentType=segmentType)
             lastTag = tag
         ppen.endPath()
         startIndex = endIndex
コード例 #14
0
    def find_shape_diffs(self):
        """Report differences in glyph shapes, using BooleanOperations."""

        self.build_names()

        area_pen = GlyphAreaPen(None)
        pen = PointToSegmentPen(area_pen)
        mismatched = {}
        for name in self.names:
            glyph_a = Glyph()
            glyph_b = Glyph()
            self.glyph_set_a[name].draw(Qu2CuPen(glyph_a.getPen(), self.glyph_set_a))
            self.glyph_set_b[name].draw(Qu2CuPen(glyph_b.getPen(), self.glyph_set_b))
            booleanOperations.xor(list(glyph_a), list(glyph_b), pen)
            area = abs(area_pen.pop())
            if area:
                mismatched[name] = area

        stats = self.stats["compared"]
        for name, area in mismatched.items():
            stats.append((area, name, self.basepath))
コード例 #15
0
 def getReversePen(self):
     adapterPen = PointToSegmentPen(self.otherPen)
     reversePen = ReverseContourPointPen(adapterPen)
     return SegmentToPointPen(reversePen)
コード例 #16
0
ファイル: basebezierpath.py プロジェクト: oakhope/PageBot
 def draw(self, pen):
     """Draws the contours with **pen**."""
     pointPen = PointToSegmentPen(pen)
     self.drawToPointPen(pointPen)
コード例 #17
0
ファイル: basebezierpath.py プロジェクト: oakhope/PageBot
class BaseBezierPath(BasePen):
    """Base class with same interface as DrawBot Bézier path."""

    contourClass = BaseBezierContour

    def __init__(self, path=None, glyphSet=None):
        """

        >>> path = BaseBezierPath()
        >>> path
        <BaseBezierPath>
        """
        self._contours = []
        #super().__init__(glyphSet)
        BasePen.__init__(self, glyphSet)

    def __repr__(self):
        return "<BaseBezierPath>"

    def _points(self, onCurve=True, offCurve=True):
        """Internal points representation, corresponding to the DrawBot Bézier
        path."""
        points = []

        if not onCurve and not offCurve:
            return points

        for contour in self._contours:
            for segment in contour:
                pts = segment.points

                if not onCurve:
                    pts = pts[:-1]
                elif not offCurve:
                    pts = pts[-1:]
                points.extend([(p.x, p.y) for p in pts])

        return tuple(points)

    def _get_points(self):
        return self._points()

    points = property(_get_points, doc="Return a list of all points.")

    def _get_onCurvePoints(self):
        return self._points(offCurve=False)

    onCurvePoints = property(_get_onCurvePoints, doc="Return a list of all on curve points.")

    def _get_offCurvePoints(self):
        return self._points(onCurve=False)

    offCurvePoints = property(_get_offCurvePoints, doc="Return a list of all off curve points.")

    def _get_contours(self):
        """Internal contour representation, corresponding to the DrawBot Bézier path."""
        contours = []
        for contour in self._contours:
            for segment in contour:
                if segment.instruction == MOVETO:
                    contours.append(BaseBezierContour())
                if segment.instruction == CLOSEPATH:
                    contours[-1].open = False
                if segment.points:
                    contours[-1].append([(p.x, p.y) for p in segment.points])

        if len(contours) >= 2 and len(contours[-1]) == 1 and contours[-1][0] == contours[-2][0]:
            contours.pop()

        return tuple(contours)

    contours = property(_get_contours, doc="Return a list of contours with all point coordinates sorted in segments. A contour object has an `open` attribute.")

    def __len__(self):
        return len(self.contours)

    def __getitem__(self, index):
        return self.contours[index]

    def __iter__(self):
        contours = self.contours
        count = len(contours)
        index = 0
        while index < count:
            contour = contours[index]
            yield contour
            index += 1

    def addSegment(self, instruction, points):
        """Adds a new segment to the current contour."""
        segment = BaseBezierSegment(instruction, points)
        contour = self.getContour()
        contour.append(segment)

    def getContour(self):
        """Gets the current contour if it exists, else make one."""
        if len(self._contours) == 0:
            contour = self.contourClass()
            self._contours.append(contour)
        else:
            contour = self._contours[-1]

        return contour

    def getPoint(self, p, onCurve=True):
        x, y = p
        point = BaseBezierPoint(x, y, onCurve=onCurve)
        return point

    # FontTools PointToSegmentPen routines.

    def beginPath(self, identifier=None):
        """Begin using the path as a point pen and start a new subpath."""
        self._pointToSegmentPen = PointToSegmentPen(self)
        self._pointToSegmentPen.beginPath()

    def addPoint(self, point, segmentType=None, smooth=False, name=None,
            identifier=None, **kwargs):
        """Use the path as a point pen and add a point to the current subpath.
        `beginPath` must have been called prior to adding points with
        `addPoint` calls."""
        if not hasattr(self, "_pointToSegmentPen"):
            msg = "path.beginPath() must be called before the path can be used as a point pen."
            raise PageBotError(msg)
        self._pointToSegmentPen.addPoint(
            point,
            segmentType=segmentType,
            smooth=smooth,
            name=name,
            identifier=identifier,
            **kwargs
        )

    def endPath(self):
        """Ends the current subpath. Calling this method has two distinct
        meanings depending on the context:

        When the Bézier path is used as a segment pen (using `moveTo`,
        `lineTo`, etc.), the current subpath will be finished as an open
        contour.

        When the Bézier path is used as a point pen (using `beginPath`,
        `addPoint` and `endPath`), the path will process all the points added
        with `addPoint`, finishing the current subpath."""
        if hasattr(self, "_pointToSegmentPen"):
            pointToSegmentPen = self._pointToSegmentPen
            del self._pointToSegmentPen
            pointToSegmentPen.endPath()
        else:
            msg = "path.beginPath() must be called before the path can be used as a point pen."
            raise PageBotError(msg)

    def draw(self, pen):
        """Draws the contours with **pen**."""
        pointPen = PointToSegmentPen(pen)
        self.drawToPointPen(pointPen)

    def bounds(self):
        """Returns the bounding box of the path."""
        pen = BoundsPen(self)
        self.draw(pen)
        return pen.bounds

    def drawToPointPen(self, pointPen):
        """Draws the Bézier path into a point pen."""
        contours = self.contours

        for contour in contours:
            contour.drawToPointPen(pointPen)

    def drawToPen(self, pen):
        """Draws the Bézier path into a pen."""
        contours = self.contours

        for contour in contours:
            contour.drawToPen(pen)

    # Drawing.

    def addComponent(self, glyphName, transformation):
        """
        Adds a sub glyph. The 'transformation' argument must be a 6-tuple
        containing an affine transformation, or a Transform object from the
        fontTools.misc.transform module. More precisely: it should be a
        sequence containing 6 numbers.

        A `glyphSet` is required during initialization of the BezierPath
        object.
        """
        raise NotImplementedError

    # Shapes.

    def rect(self, x, y, w, h):
        """Adds a rectangle at position `x`, `y` with a size of `w`, `h`."""
        x1 = x + w
        y1 = y + h
        p0 = (x, y)
        p1 = (x1, y)
        p2 = (x1, y1)
        p3 = (x, y1)
        self.moveTo(p0)
        self.lineTo(p1)
        self.lineTo(p2)
        self.lineTo(p3)
        self.closePath()

    def oval(self, x, y, w, h):
        """Adds an oval at position `x`, `y` with a size of `w`, `h`"""
        # Control point offsets.
        kappa = .5522848
        offsetX = (w / 2) * kappa
        offsetY = (h / 2) * kappa

        # Middle and other extreme points.
        x0 = x + (w / 2)
        y0 = y + (h / 2)
        x1 = x + w
        y1 = y + h

        self.moveTo((x0, y0))

        cp1 = (x, y0 - offsetY)
        cp2 = (x0 - offsetX, y)
        p = (x1, y0)
        self.curveTo(cp1, cp2, p)

        cp1 = (x0 + offsetX, y)
        cp2 = (x1, y0 - offsetY)
        p = (x1, y0)
        self.curveTo(cp1, cp2, p)

        cp1 = (x1, y0 + offsetY)
        cp2 = (x0 + offsetX, y1)
        p = (x0, y1)
        self.curveTo(cp1, cp2, p)

        cp1 = (x0 - offsetX, y1)
        cp2 = (x, y0 + offsetY)
        p = (x, y0)
        self.curveTo(cp1, cp2, p)

    def line(self, point1, point2):
        """Adds a line between two given points."""
        self.moveTo(point1)
        self.lineTo(point2)

    def polygon(self, *points, **kwargs):
        """Draws a polygon with `n` points. Optionally a `close` argument can
        be provided to open or close the path. By default a `polygon` is a
        closed path."""
        self.moveTo(points[0])

        for point in points[1:]:
            self.lineTo(point)

        # TODO: optionally close.

    def arc(self, center, radius, startAngle, endAngle, clockwise):
        """Arc with `center` and a given `radius`, from `startAngle` to
        `endAngle`, going clockwise if `clockwise` is True and counter
        clockwise if `clockwise` is False."""
        raise NotImplementedError

    def arcTo(self, point1, point2, radius):
        """Arc defined by a circle inscribed inside the angle specified by
        three points: the current point, `point1`, and `point2`. The arc is
        drawn between the two points of the circle that are tangent to the two
        legs of the angle."""
        raise NotImplementedError

    # Text.

    def text(self, txt, offset=None, font=_FALLBACKFONT, fontSize=10,
            align=None):
        """Draws a `txt` with a `font` and `fontSize` at an `offset` in the
        Bézier path. If a font path is given the font will be installed and
        used directly.

        * Optionally an alignment can be set. Possible `align` values are:
          `"left"`, `"center"` and `"right"`.
        * The default alignment is `left`.
        * Optionally `txt` can be a `FormattedString`.

        """
        raise NotImplementedError

    def textBox(self, txt, box, font=None, fontSize=10, align=None,
            hyphenation=None):
        """
        Draws a `txt` with a `font` and `fontSize` in a `box` in the Bézier path.
        If a font path is given the font will be installed and used directly.

        Optionally an alignment can be set.

        * Possible `align` values are: `"left"`, `"center"` and `"right"`.
        * The default alignment is `left`.
        * Optionally `hyphenation` can be provided.
        * Optionally `txt` can be a `FormattedString`.
        * Optionally `box` can be a `BezierPath`.

        """
        raise NotImplementedError

    # Path operations.
    # These are specific for a DrawBot path, dropping from interface.

    #def getNSBezierPath(self):
    def getBezierPath(self):
        """Returns the equivalent of an NSBezierPath."""

    #def setNSBezierPath(self, path):
    def setBezierPath(self, path):
        """Sets the equivalent of an NSBezierPath."""

    def traceImage(self, path, threshold=.2, blur=None, invert=False, turd=2,
            tolerance=0.2, offset=None):
        """Converts a given image to a vector outline. Optionally some tracing
        options can be provided:

        * `threshold`: the threshold used to bitmap an image
        * `blur`: the image can be blurred
        * `invert`: invert to the image
        * `turd`: the size of small turd that can be ignored
        * `tolerance`: the precision tolerance of the vector outline
        * `offset`: add the traced vector outline with an offset to the BezierPath
        """
        # TODO: use potrace, see drawBot.context.tools.TraceImage.

    def pointInside(self, xy):
        """Checks if a point `x`, `y` is inside a path."""
        raise NotImplementedError

    def controlPointBounds(self):
        """Returns the bounding box of the path including the offcurve
        points."""
        raise NotImplementedError

    def optimizePath(self):
        raise NotImplementedError

    def copy(self):
        """Copy the Bézier path."""
        raise NotImplementedError

    def reverse(self):
        """Reverse the path direction."""
        raise NotImplementedError

    def appendPath(self, otherPath):
        """Append a path."""
        raise NotImplementedError

    # transformations
    # NOTE: currently handled within context.

    def translate(self, x=0, y=0):
        """Translates the path with a given offset."""
        self.transform((1, 0, 0, 1, x, y))

    def rotate(self, angle, center=(0, 0)):
        """Rotates the path around the `center` point (which is the origin by
        default) with a given angle in degrees."""
        angle = math.radians(angle)
        c = math.cos(angle)
        s = math.sin(angle)
        self.transform((c, s, -s, c, 0, 0), center)

    def scale(self, x=1, y=None, center=(0, 0)):
        """Scales the path with a given `x` (horizontal scale) and `y`
        (vertical scale).

        If only 1 argument is provided a proportional scale is applied.

        The center of scaling can optionally be set via the `center` keyword
        argument. By default this is the origin."""
        if y is None:
            y = x
        self.transform((x, 0, 0, y, 0, 0), center)

    def skew(self, angle1, angle2=0, center=(0, 0)):
        """Skews the path with given `angle1` and `angle2`. If only one argument
        is provided a proportional skew is applied. The center of skewing can
        optionally be set via the `center` keyword argument. By default this is
        the origin."""
        angle1 = math.radians(angle1)
        angle2 = math.radians(angle2)
        self.transform((1, math.tan(angle2), math.tan(angle1), 1, 0, 0), center)

    def transform(self, transformMatrix, center=(0, 0)):
        """Transforms a path with a transform matrix (xy, xx, yy, yx, x, y)."""

    # Boolean operations.

    def _contoursForBooleanOperations(self):
        # contours are temporary objects
        # redirect drawToPointPen to drawPoints
        contours = self.contours
        for contour in contours:
            contour.drawPoints = contour.drawToPointPen
            if contour.open:
                raise PageBotError("open contours are not supported during boolean operations")
        return contours


    def union(self, other):
        """Returns the union between two Bézier paths."""
        assert isinstance(other, self.__class__)
        contours = self._contoursForBooleanOperations() + other._contoursForBooleanOperations()
        result = self.__class__()
        booleanOperations.union(contours, result)
        return result

    def removeOverlap(self):
        """Remove all overlaps in a Bézier path."""
        contours = self._contoursForBooleanOperations()
        result = self.__class__()
        booleanOperations.union(contours, result)
        # TODO:
        #self.setNSBezierPath(result.getNSBezierPath())
        #self.setBezierPath(result.getBezierPath())
        return self

    def difference(self, other):
        """Returns the difference between two Bézier paths.
        """
        subjectContours = self._contoursForBooleanOperations()
        clipContours = other._contoursForBooleanOperations()
        result = self.__class__()
        booleanOperations.difference(subjectContours, clipContours, result)
        return result

    def intersection(self, other):
        """Returns the intersection between two Bézier paths."""
        assert isinstance(other, self.__class__)
        subjectContours = self._contoursForBooleanOperations()
        clipContours = other._contoursForBooleanOperations()
        result = self.__class__()
        booleanOperations.intersection(subjectContours, clipContours, result)
        return result

    def xor(self, other):
        """Returns the xor between two Bézier paths."""
        assert isinstance(other, self.__class__)
        subjectContours = self._contoursForBooleanOperations()
        clipContours = other._contoursForBooleanOperations()
        result = self.__class__()
        booleanOperations.xor(subjectContours, clipContours, result)
        return result

    def intersectionPoints(self, other=None):
        """
        Returns a list of intersection points as `x`, `y` tuples. Optionaly
        provides another path object to find intersection points.
        """
        contours = self._contoursForBooleanOperations()
        if other is not None:
            assert isinstance(other, self.__class__)
            contours += other._contoursForBooleanOperations()
        return booleanOperations.getIntersections(contours)

    def expandStroke(self, width, lineCap="round", lineJoin="round",
            miterLimit=10):
        """Returns a new Bézier path with an expanded stroke around the original path,
        with a given `width`. Note: the new path will not contain the original path.

        The following optional arguments are available with respect to line caps and joins:
        * `lineCap`: Possible values are `"butt"`, `"square"` or `"round"`
        * `lineJoin`: Possible values are `"bevel"`, `"miter"` or `"round"`
        * `miterLimit`: The miter limit to use for `"miter"` lineJoin option
        """
        # TODO: find cross-platform alternative to Quartz.CGPathCreateCopyByStrokingPath.

    #

    def __add__(self, otherPath):
        #new = self.copy()
        #new.appendPath(otherPath)
        #return new
        pass

    def __iadd__(self, other):
        self.appendPath(other)
        return self

    def __mod__(self, other):
        return self.difference(other)

    __rmod__ = __mod__

    def __imod__(self, other):
        #result = self.difference(other)
        #self.setBezierPath(result.getBezierPath())
        #return self
        pass

    def __or__(self, other):
        return self.union(other)

    __ror__ = __or__

    def __ior__(self, other):
        #result = self.union(other)
        #self.setBezierPath(result.getBezierPath())
        #return self
        pass

    def __and__(self, other):
        return self.intersection(other)

    __rand__ = __and__

    def __iand__(self, other):
        #result = self.intersection(other)
        #self.setBezierPath(result.getBezierPath())
        #return self
        pass

    def __xor__(self, other):
        return self.xor(other)

    __rxor__ = __xor__

    def __ixor__(self, other):
        #result = self.xor(other)
        #self.setBezierPath(result.getBezierPath())
        #return self
        pass
コード例 #18
0
 def test_roundTrip1(self):
     tpen = _TestPointPen()
     ppen = PointToSegmentPen(SegmentToPointPen(tpen))
     ppen.beginPath()
     ppen.addPoint((10, 10), "line")
     ppen.addPoint((10, 20))
     ppen.addPoint((20, 20))
     ppen.addPoint((20, 40), "curve")
     ppen.endPath()
     self.assertEqual(
         "beginPath() addPoint((10, 10), segmentType='line') addPoint((10, 20)) "
         "addPoint((20, 20)) addPoint((20, 40), segmentType='curve') endPath()",
         repr(tpen))
コード例 #19
0
class BezierPath(BasePen):
    def __init__(self, path=None, glyphSet=None):
        super().__init__(glyphSet)
        if path is None:
            path = skia.Path()
        self.path = path

    def _moveTo(self, pt):
        self.path.moveTo(*pt)

    def _lineTo(self, pt):
        self.path.lineTo(*pt)

    def _curveToOne(self, pt1, pt2, pt3):
        x1, y1 = pt1
        x2, y2 = pt2
        x3, y3 = pt3
        self.path.cubicTo(x1, y1, x2, y2, x3, y3)

    def _qCurveToOne(self, pt1, pt2):
        x1, y1 = pt1
        x2, y2 = pt2
        self.path.quadTo(x1, y1, x2, y2)

    def _closePath(self):
        self.path.close()

    def beginPath(self, identifier=None):
        self._pointToSegmentPen = PointToSegmentPen(self)
        self._pointToSegmentPen.beginPath()

    def addPoint(self,
                 point,
                 segmentType=None,
                 smooth=False,
                 name=None,
                 identifier=None,
                 **kwargs):
        if not hasattr(self, "_pointToSegmentPen"):
            raise AttributeError(
                "path.beginPath() must be called before the path can be used as a point pen"
            )
        self._pointToSegmentPen.addPoint(point,
                                         segmentType=segmentType,
                                         smooth=smooth,
                                         name=name,
                                         identifier=identifier,
                                         **kwargs)

    def endPath(self):
        if hasattr(self, "_pointToSegmentPen"):
            # We are drawing as a point pen
            pointToSegmentPen = self._pointToSegmentPen
            del self._pointToSegmentPen
            pointToSegmentPen.endPath()

    def arc(self, center, radius, startAngle, endAngle, clockwise):
        cx, cy = center
        diameter = radius * 2
        rect = (cx - radius, cy - radius, diameter, diameter)
        sweepAngle = (endAngle - startAngle) % 360
        if clockwise:
            sweepAngle -= 360
        self.path.arcTo(rect, startAngle, sweepAngle, False)

    def arcTo(self, point1, point2, radius):
        self.path.arcTo(point1, point2, radius)

    def rect(self, x, y, w, h):
        self.path.addRect((x, y, w, h))

    def oval(self, x, y, w, h):
        self.path.addOval((x, y, w, h))

    def line(self, pt1, pt2):
        points = [(x, y) for x, y in [pt1, pt2]]
        self.path.addPoly(points, False)

    def polygon(self, firstPoint, *points, close=True):
        points = [(x, y) for x, y in (firstPoint, ) + points]
        self.path.addPoly(points, close)

    def pointInside(self, point):
        x, y = point
        return self.path.contains(x, y)

    def bounds(self):
        if self.path.countVerbs() == 0:
            return None
        return tuple(self.path.computeTightBounds())

    def controlPointBounds(self):
        if self.path.countVerbs() == 0:
            return None
        return tuple(self.path.getBounds())

    def reverse(self):
        path = skia.Path()
        path.reverseAddPath(self.path)
        self.path = path

    def appendPath(self, other):
        self.path.addPath(other.path)

    def copy(self):
        path = skia.Path(self.path)
        return BezierPath(path=path)

    def translate(self, x, y):
        self.path.offset(x, y)

    def scale(self, x, y=None, center=(0, 0)):
        if y is None:
            y = x
        self.transform((x, 0, 0, y, 0, 0), center=center)

    def rotate(self, angle, center=(0, 0)):
        t = Transform()
        t = t.rotate(math.radians(angle))
        self.transform(t, center=center)

    def skew(self, x, y=0, center=(0, 0)):
        t = Transform()
        t = t.skew(math.radians(x), math.radians(y))
        self.transform(t, center=center)

    def transform(self, transform, center=(0, 0)):
        cx, cy = center
        t = Transform()
        t = t.translate(cx, cy)
        t = t.transform(transform)
        t = t.translate(-cx, -cy)
        matrix = skia.Matrix()
        matrix.setAffine(t)
        self.path.transform(matrix)

    def drawToPen(self, pen):
        it = skia.Path.Iter(self.path, False)
        needEndPath = False
        for verb, points in it:
            penVerb, startIndex, numPoints = _pathVerbsToPenMethod.get(
                verb, (None, None, None))
            if penVerb is None:
                continue
            assert len(points) == numPoints, (verb, numPoints, len(points))
            if penVerb == "conicTo":
                # We should only call _convertConicToCubicDirty()
                # if it.conicWeight() == sqrt(2)/2, but skia-python doesn't
                # give the correct value.
                # https://github.com/kyamagu/skia-python/issues/116
                # if abs(it.conicWeight() - 0.707...) > 1e-10:
                #     logging.warning("unsupported conic form (weight != sqrt(2)/2): conic to cubic conversion will be bad")
                # TODO: we should fall back to skia.Path.ConvertConicToQuads(),
                # but that call is currently also not working.
                pen.curveTo(*_convertConicToCubicDirty(*points))
            elif penVerb == "closePath":
                needEndPath = False
                pen.closePath()
            else:
                if penVerb == "moveTo":
                    if needEndPath:
                        pen.endPath()
                    needEndPath = True
                pointArgs = ((x, y) for x, y in points[startIndex:])
                getattr(pen, penVerb)(*pointArgs)
        if needEndPath:
            pen.endPath()

    def drawToPointPen(self, pen):
        self.drawToPen(SegmentToPointPen(pen))

    def text(self, txt, offset=None, font=None, fontSize=10, align=None):
        if not txt:
            return
        textStyle = TextStyle(font=font, fontSize=fontSize)
        glyphsInfo = textStyle.shape(txt)
        textStyle.alignGlyphPositions(glyphsInfo, align)
        gids = sorted(set(glyphsInfo.gids))
        paths = [textStyle.skFont.getPath(gid) for gid in gids]
        for path in paths:
            path.transform(FLIP_MATRIX)
        paths = dict(zip(gids, paths))
        x, y = (0, 0) if offset is None else offset
        for gid, pos in zip(glyphsInfo.gids, glyphsInfo.positions):
            path = paths[gid]
            self.path.addPath(path, pos[0] + x, pos[1] + y)

    def _doPathOp(self, other, operator):
        from pathops import Path, op
        path1 = Path()
        path2 = Path()
        self.drawToPen(path1.getPen())
        other.drawToPen(path2.getPen())
        result = op(
            path1,
            path2,
            operator,
            fix_winding=True,
            keep_starting_points=True,
        )
        resultPath = BezierPath()
        result.draw(resultPath)
        return resultPath

    def union(self, other):
        from pathops import PathOp
        return self._doPathOp(other, PathOp.UNION)

    def intersection(self, other):
        from pathops import PathOp
        return self._doPathOp(other, PathOp.INTERSECTION)

    def difference(self, other):
        from pathops import PathOp
        return self._doPathOp(other, PathOp.DIFFERENCE)

    def xor(self, other):
        from pathops import PathOp
        return self._doPathOp(other, PathOp.XOR)

    def removeOverlap(self):
        from pathops import Path
        path = Path()
        self.drawToPen(path.getPen())
        path.simplify(
            fix_winding=True,
            keep_starting_points=False,
        )
        resultPath = BezierPath()
        path.draw(resultPath)
        self.path = resultPath.path

    __mod__ = difference

    def __imod__(self, other):
        result = self.difference(other)
        self.path = result.path
        return self

    __or__ = union

    def __ior__(self, other):
        result = self.union(other)
        self.path = result.path
        return self

    __and__ = intersection

    def __iand__(self, other):
        result = self.intersection(other)
        self.path = result.path
        return self

    __xor__ = xor

    def __ixor__(self, other):
        result = self.xor(other)
        self.path = result.path
        return self
コード例 #20
0
ファイル: utils.py プロジェクト: verbosus/fonttools
 def getPointPen(self):
     """Return a PointPen adapter that can 'draw' on this glyph."""
     return PointToSegmentPen(self._pen)
コード例 #21
0
 def beginPath(self, identifier=None):
     self._pointToSegmentPen = PointToSegmentPen(self)
     self._pointToSegmentPen.beginPath()
コード例 #22
0
ファイル: objects.py プロジェクト: BlackFoundryCom/rcjk-tools
 def draw(self, pen):
     self.drawPoints(PointToSegmentPen(pen))
コード例 #23
0
ファイル: basebezierpath.py プロジェクト: oakhope/PageBot
 def beginPath(self, identifier=None):
     """Begin using the path as a point pen and start a new subpath."""
     self._pointToSegmentPen = PointToSegmentPen(self)
     self._pointToSegmentPen.beginPath()
コード例 #24
0
ファイル: mathGlyph.py プロジェクト: roboDocs/ScaleFast
 def draw(self, pen):
     """draw self using pen"""
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen)
コード例 #25
0
 def draw(self, pen, filterRedundantPoints=False):
     """draw self using pen"""
     from fontTools.pens.pointPen import PointToSegmentPen
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen, filterRedundantPoints=filterRedundantPoints)
コード例 #26
0
 def draw(self, pen: AbstractPen) -> None:
     """Draws contour into given pen."""
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen)
コード例 #27
0
ファイル: utils.py プロジェクト: verbosus/fonttools
 def draw(self, pen):
     """Use another SegmentPen to replay the glyph's outline commands,
     indirectly through an adapter.
     """
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen)
コード例 #28
0
 def draw(self, pen: AbstractPen) -> None:
     """Draws glyph into given pen."""
     # TODO: Document pen interface more or link to somewhere.
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen)
コード例 #29
0
 def draw(self, pen):
     pointPen = PointToSegmentPen(pen)
     self.drawPoints(pointPen)
コード例 #30
0
 def test_quad(self):
     pen = _TestSegmentPen()
     ppen = PointToSegmentPen(pen)
     ppen.beginPath(identifier='foo')
     ppen.addPoint((10, 10), "line")
     ppen.addPoint((10, 40))
     ppen.addPoint((40, 40))
     ppen.addPoint((10, 40), "qcurve")
     ppen.endPath()
     self.assertEqual("10 10 moveto 10 40 40 40 10 40 qcurveto closepath",
                      repr(pen))