Ejemplo n.º 1
0
def calculate_pathop(pen1, pen2, operation):
    if USE_SKIA_PATHOPS:
        p1 = Path()
        pen1.replay(p1.getPen())
        if operation == BooleanOp.Simplify:
            # ignore pen2
            p1.simplify(fix_winding=True, keep_starting_points=True)
            d0 = RecordingPen()
            p1.draw(d0)
            return d0.value
        if pen2:
            p2 = Path()
            pen2.replay(p2.getPen())
        builder = OpBuilder(fix_winding=True, keep_starting_points=True)
        builder.add(p1, PathOp.UNION)
        if pen2:
            builder.add(p2, BooleanOp.Skia(operation))
        result = builder.resolve()
        d0 = RecordingPen()
        result.draw(d0)
        return d0.value
    else:
        bg2 = BooleanGlyph()
        if pen2:
            pen2.replay(bg2.getPen())
        bg = BooleanGlyph()
        pen1.replay(bg.getPen())
        bg = bg._booleanMath(BooleanOp.BooleanGlyphMethod(operation), bg2)
        dp = RecordingPen()
        bg.draw(dp)
        return dp.value
Ejemplo n.º 2
0
    def test_resolve(self):
        path1 = Path()
        pen1 = path1.getPen()
        pen1.moveTo((5, -225))
        pen1.lineTo((-225, 7425))
        pen1.lineTo((7425, 7425))
        pen1.lineTo((7425, -225))
        pen1.lineTo((-225, -225))
        pen1.closePath()

        path2 = Path()
        pen2 = path2.getPen()
        pen2.moveTo((5940, 2790))
        pen2.lineTo((5940, 2160))
        pen2.lineTo((5970, 1980))
        pen2.lineTo((5688, 773669888))
        pen2.lineTo((5688, 2160))
        pen2.lineTo((5688, 2430))
        pen2.lineTo((5400, 4590))
        pen2.lineTo((5220, 4590))
        pen2.lineTo((5220, 4920))
        pen2.curveTo((5182.22900390625, 4948.328125),
                     (5160, 4992.78662109375),
                     (5160, 5040.00048828125))
        pen2.lineTo((5940, 2790))
        pen2.closePath()

        builder = OpBuilder(fix_winding=False, keep_starting_points=False)
        builder.add(path1, PathOp.UNION)
        builder.add(path2, PathOp.UNION)
        result = builder.resolve()

        assert list(result.segments) == [
            ("moveTo", ((5316.0, 4590.0),)),
            ("lineTo", ((5220.0, 4590.0),)),
            ("lineTo", ((5220.0, 4866.92333984375),)),
            ("lineTo", ((5316.0, 4590.0),)),
            ("closePath", ()),
            ("moveTo", ((5192.18701171875, 4947.15283203125),)),
            (
                "curveTo",
                (
                    (5171.5654296875, 4973.322265625),
                    (5160.0, 5005.9443359375),
                    (5160.0, 5040.00048828125),
                ),
            ),
            ("lineTo", ((5192.18701171875, 4947.15283203125),)),
            ("closePath", ()),
            ("moveTo", ((5688.0, 7425.0),)),
            ("lineTo", ((-225.0, 7425.0),)),
            ("lineTo", ((5.0, -225.0),)),
            ("lineTo", ((7425.0, -225.0),)),
            ("lineTo", ((7425.0, 7425.0),)),
            ("lineTo", ((5688.0, 7425.0),)),
            ("closePath", ()),
        ]
Ejemplo n.º 3
0
    def test_draw(self):
        path = Path()
        pen = path.getPen()
        pen.moveTo((0, 0))
        pen.lineTo((1.0, 2.0))
        pen.curveTo((3.5, 4), (5, 6), (7, 8))
        pen.qCurveTo((9, 10), (11, 12))
        pen.closePath()

        path2 = Path()
        path.draw(path2.getPen())

        assert path == path2
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
 def __init__(self, subject, clip, **kwargs) -> None:
     super().__init__(**kwargs)
     outpen = SkiaPath()
     xor(
         [self._convert_vmobject_to_skia_path(subject)],
         [self._convert_vmobject_to_skia_path(clip)],
         outpen.getPen(),
     )
     self._convert_skia_path_to_vmobject(outpen)
Ejemplo n.º 6
0
 def __init__(self, *vmobjects: VMobject, **kwargs) -> None:
     if len(vmobjects) < 2:
         raise ValueError("At least 2 mobjects needed for Union.")
     super().__init__(**kwargs)
     paths = []
     for vmobject in vmobjects:
         paths.append(self._convert_vmobject_to_skia_path(vmobject))
     outpen = SkiaPath()
     union(paths, outpen.getPen())
     self._convert_skia_path_to_vmobject(outpen)
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
 def test_last_implicit_lineTo(self):
     # https://github.com/fonttools/skia-pathops/issues/6
     path = Path()
     pen = path.getPen()
     pen.moveTo((100, 100))
     pen.lineTo((100, 200))
     pen.closePath()
     assert list(path.segments) == [
         ('moveTo', ((100.0, 100.0),)),
         ('lineTo', ((100.0, 200.0),)),
         # ('lineTo', ((100.0, 100.0),)),
         ('closePath', ())]
Ejemplo n.º 9
0
def test_intersection(subject_path, clip_path, expected):
    sub = Path()
    for verb, pts in subject_path:
        sub.add(verb, *pts)
    clip = Path()
    for verb, pts in clip_path:
        clip.add(verb, *pts)
    result = Path()

    intersection([sub], [clip], result.getPen())

    assert list(result) == expected
Ejemplo n.º 10
0
    def test_add(self):
        path = Path()
        pen = path.getPen()
        pen.moveTo((5, -225))
        pen.lineTo((-225, 7425))
        pen.lineTo((7425, 7425))
        pen.lineTo((7425, -225))
        pen.lineTo((-225, -225))
        pen.closePath()

        builder = OpBuilder()
        builder.add(path, PathOp.UNION)
Ejemplo n.º 11
0
    def test_pen_addComponent_decomposed_from_glyphSet(self):
        a = Path()
        a.moveTo(0, 0)
        a.lineTo(1, 0)
        a.lineTo(1, 1)
        a.lineTo(0, 1)
        a.close()
        glyphSet = {"a": a}

        b = Path()
        pen = b.getPen(glyphSet=glyphSet)
        pen.addComponent("a", (2, 0, 0, 2, 10, 10))
        glyphSet["b"] = b

        assert list(b) == [
            (PathVerb.MOVE, ((10, 10),)),
            (PathVerb.LINE, ((12, 10),)),
            (PathVerb.LINE, ((12, 12),)),
            (PathVerb.LINE, ((10, 12),)),
            (PathVerb.CLOSE, ()),
        ]

        c = Path()
        pen = c.getPen(glyphSet=glyphSet)
        pen.addComponent("a", (1, 0, 0, 1, 2, 2))
        pen.addComponent("b", (1, 0, 0, 1, -10, -10))
        glyphSet["c"] = c

        assert list(c) == [
            (PathVerb.MOVE, ((2, 2),)),
            (PathVerb.LINE, ((3, 2),)),
            (PathVerb.LINE, ((3, 3),)),
            (PathVerb.LINE, ((2, 3),)),
            (PathVerb.CLOSE, ()),
            (PathVerb.MOVE, ((0, 0),)),
            (PathVerb.LINE, ((2, 0),)),
            (PathVerb.LINE, ((2, 2),)),
            (PathVerb.LINE, ((0, 2),)),
            (PathVerb.CLOSE, ()),
        ]
Ejemplo n.º 12
0
    def __init__(self, *vmobjects, **kwargs) -> None:
        if len(vmobjects) < 2:
            raise ValueError("At least 2 mobjects needed for Intersection.")

        super().__init__(**kwargs)
        outpen = SkiaPath()
        intersection(
            [self._convert_vmobject_to_skia_path(vmobjects[0])],
            [self._convert_vmobject_to_skia_path(vmobjects[1])],
            outpen.getPen(),
        )
        new_outpen = outpen
        for _i in range(2, len(vmobjects)):
            new_outpen = SkiaPath()
            intersection(
                [outpen],
                [self._convert_vmobject_to_skia_path(vmobjects[_i])],
                new_outpen.getPen(),
            )
            outpen = new_outpen

        self._convert_skia_path_to_vmobject(outpen)
Ejemplo n.º 13
0
    def test_allow_open_contour(self):
        path = Path()
        pen = path.getPen()
        pen.moveTo((0, 0))
        # pen.endPath() is implicit here
        pen.moveTo((1, 0))
        pen.lineTo((1, 1))
        pen.curveTo((2, 2), (3, 3), (4, 4))
        pen.endPath()

        assert list(path.segments) == [
            ('moveTo', ((0.0, 0.0),)),
            ('endPath', ()),
            ('moveTo', ((1.0, 0.0),)),
            ('lineTo', ((1.0, 1.0),)),
            ('curveTo', ((2.0, 2.0), (3.0, 3.0), (4.0, 4.0))),
            ('endPath', ()),
        ]
Ejemplo n.º 14
0
    def test_decompose_join_quadratic_segments(self):
        path = Path()
        pen = path.getPen()
        pen.moveTo((0, 0))
        pen.qCurveTo((1, 1), (2, 2), (3, 3))
        pen.closePath()

        items = list(path)
        assert len(items) == 4
        # the TrueType quadratic spline with N off-curves is stored internally
        # as N atomic quadratic Bezier segments
        assert items[1][0] == PathVerb.QUAD
        assert items[1][1] == ((1.0, 1.0), (1.5, 1.5))
        assert items[2][0] == PathVerb.QUAD
        assert items[2][1] == ((2.0, 2.0), (3.0, 3.0))

        # when drawn back onto a SegmentPen, the implicit on-curves are omitted
        assert list(path.segments) == [
            ('moveTo', ((0.0, 0.0),)),
            ('qCurveTo', ((1.0, 1.0), (2.0, 2.0), (3.0, 3.0))),
            ('closePath', ())]
Ejemplo n.º 15
0
def build(instance, opts):
    font = instance.parent
    master = font.masters[0]

    fea, marks = makeFeatures(instance, master)

    glyphOrder = []
    advanceWidths = {}
    characterMap = {}
    charStrings = {}
    for glyph in font.glyphs:
        if not glyph.export:
            continue
        name = glyph.name

        glyphOrder.append(name)
        if glyph.unicode:
            characterMap[int(glyph.unicode, 16)] = name

        layer = getLayer(glyph, instance)
        width = 0 if name in marks else layer.width
        path = Path()
        draw(layer, instance, path.getPen())
        path.simplify(fix_winding=True, keep_starting_points=True)
        pen = T2CharStringPen(width, None)
        path.draw(pen)
        charStrings[name] = pen.getCharString(optimize=False)
        advanceWidths[name] = width

    # XXX
    glyphOrder.pop(glyphOrder.index(".notdef"))
    glyphOrder.pop(glyphOrder.index("space"))
    glyphOrder.insert(0, ".notdef")
    glyphOrder.insert(1, "space")

    version = float(opts.version)
    vendor = font.customParameters["vendorID"]
    names = {
        "copyright": font.copyright,
        "familyName": instance.familyName,
        "styleName": instance.name,
        "uniqueFontIdentifier": f"{version:.03f};{vendor};{instance.fontName}",
        "fullName": instance.fullName,
        "version": f"Version {version:.03f}",
        "psName": instance.fontName,
        "manufacturer": font.manufacturer,
        "designer": font.designer,
        "description": font.customParameters["description"],
        "vendorURL": font.manufacturerURL,
        "designerURL": font.designerURL,
        "licenseDescription": font.customParameters["license"],
        "licenseInfoURL": font.customParameters["licenseURL"],
        "sampleText": font.customParameters["sampleText"],
    }

    date = int(font.date.timestamp()) - epoch_diff
    fb = FontBuilder(font.upm, isTTF=False)
    fb.updateHead(fontRevision=version, created=date, modified=date)
    fb.setupGlyphOrder(glyphOrder)
    fb.setupCharacterMap(characterMap)
    fb.setupNameTable(names, mac=False)
    fb.setupHorizontalHeader(ascent=master.ascender, descent=master.descender,
                             lineGap=master.customParameters["typoLineGap"])

    privateDict = {
        "BlueValues": [],
        "OtherBlues": [],
        "StemSnapH": master.horizontalStems,
        "StemSnapV": master.verticalStems,
        "StdHW": master.horizontalStems[0],
        "StdVW": master.verticalStems[0],
    }
    for zone in sorted(master.alignmentZones):
        pos = zone.position
        size = zone.size
        vals = privateDict["BlueValues"] if pos == 0 or size >= 0 else privateDict["OtherBlues"]
        vals.extend(sorted((pos, pos + size)))

    fontInfo = {
        "FullName": names["fullName"],
        "Notice": names["copyright"].replace("©", "\(c\)"),
        "version": f"{version:07.03f}",
        "Weight": instance.name,
    }
    fb.setupCFF(names["psName"], fontInfo, charStrings, privateDict)

    metrics = {}
    for i, (name, width) in enumerate(advanceWidths.items()):
        bounds = charStrings[name].calcBounds(None) or [0]
        metrics[name] = (width, bounds[0])
    fb.setupHorizontalMetrics(metrics)

    codePages = [CODEPAGE_RANGES[v] for v in font.customParameters["codePageRanges"]]
    fb.setupOS2(version=4, sTypoAscender=master.ascender,
                sTypoDescender=master.descender,
                sTypoLineGap=master.customParameters["typoLineGap"],
                usWinAscent=master.ascender, usWinDescent=-master.descender,
                sxHeight=master.xHeight, sCapHeight=master.capHeight,
                achVendID=vendor,
                fsType=calcBits(font.customParameters["fsType"], 0, 16),
                fsSelection=calcFsSelection(instance),
                ulUnicodeRange1=calcBits(font.customParameters["unicodeRanges"], 0, 32),
                ulCodePageRange1=calcBits(codePages, 0, 32))

    ut = int(master.customParameters["underlineThickness"])
    up = int(master.customParameters["underlinePosition"])
    fb.setupPost(underlineThickness=ut, underlinePosition=up + ut//2)

    fb.addOpenTypeFeatures(fea)

    cidinfo = f"""
FontName	({names["psName"]})
FamilyName	({names["familyName"]})
Weight	({fontInfo["Weight"]})
version	({fontInfo["version"]})
Notice	({fontInfo["Notice"]})
Registry	Adobe
Ordering	Identity
Supplement	0
"""

    cidmap = f"mergefonts {instance.fontName}\n" \
            + "\n".join([f"{i} {n}" for i, n in enumerate(glyphOrder)])

    return fb.font, cidinfo, cidmap
Ejemplo n.º 16
0
 def test_raise_open_contour_error(self):
     path = Path()
     pen = path.getPen(allow_open_paths=False)
     pen.moveTo((0, 0))
     with pytest.raises(OpenPathError):
         pen.endPath()
Ejemplo n.º 17
0
 def test_getPen(self):
     path = Path()
     pen = path.getPen()
     assert isinstance(pen, PathPen)
     assert id(pen) != id(path.getPen())
Ejemplo n.º 18
0
 def test_pen_addComponent_missing_required_glyphSet(self):
     path = Path()
     pen = path.getPen()
     with pytest.raises(TypeError, match="Missing required glyphSet"):
         pen.addComponent("a", (1, 0, 0, 1, 0, 0))