Example #1
0
    def filter(self, glyph):
        matrix = self.context.matrix
        if matrix == Identity or not (glyph or glyph.components
                                      or glyph.anchors):
            return False  # nothing to do

        modified = self.context.modified
        glyphSet = self.context.glyphSet
        for component in glyph.components:
            base_name = component.baseGlyph
            if base_name in modified:
                continue
            base_glyph = glyphSet[base_name]
            if self.include(base_glyph) and self.filter(base_glyph):
                # base glyph is included but was not transformed yet; we
                # call filter recursively until all the included bases are
                # transformed, or there are no more components
                modified.add(base_name)

        rec = RecordingPen()
        glyph.draw(rec)
        glyph.clearContours()
        glyph.clearComponents()

        outpen = glyph.getPen()
        filterpen = TransformPen(outpen, matrix, modified)
        rec.replay(filterpen)

        # anchors are not drawn through the pen API,
        # must be transformed separately
        for a in glyph.anchors:
            a.x, a.y = matrix.transformPoint((a.x, a.y))

        return True
Example #2
0
def spikeGlyph(aGlyph, segmentLength=20, spikeLength=40, patternFunc=None):
    from fontTools.pens.recordingPen import RecordingPen
    recorder = RecordingPen()
    spikePen = SpikePen(recorder, spikeLength=spikeLength, patternFunc=patternFunc)
    filterPen = FlattenPen(spikePen, approximateSegmentLength=segmentLength, segmentLines=True)
    aGlyph.draw(filterPen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())
    return aGlyph
Example #3
0
def makeShadow(g, extrusionX, extrusionY):
    destPen = RecordingPen()
    myPen = TranslationPen(destPen)
    g.draw(myPen)
    destPen.replay(g.getPen())
    if g.rightMargin%2==0:
        g.rightMargin+=int(extrusionX-20) #20 is compensation for frontWidth in the TranslationPen constructor
    else:
        g.rightMargin+=int(extrusionX-19)
    g.changed()
def thresholdGlyph(aGlyph, threshold=10):
    """
    Convenience function that applies the **ThresholdPen** to a glyph in place.
    """
    from fontTools.pens.recordingPen import RecordingPen
    recorder = RecordingPen()
    filterpen = ThresholdPen(recorder, threshold)
    aGlyph.draw(filterpen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())
    return aGlyph
def thresholdGlyph(aGlyph, threshold=10):
    """
    Convenience function that applies the **ThresholdPen** to a glyph in place.
    """
    from fontTools.pens.recordingPen import RecordingPen
    recorder = RecordingPen()
    filterpen = ThresholdPen(recorder, threshold)
    aGlyph.draw(filterpen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())
    return aGlyph
Example #6
0
def samplingGlyph(aGlyph, steps=10):
    """
    Convenience function that applies the **SamplingPen** pen to a glyph in place.
    """
    if len(aGlyph) == 0:
        return aGlyph
    from fontTools.pens.recordingPen import RecordingPen
    recorder = RecordingPen()
    filterpen = SamplingPen(recorder, steps=steps)
    aGlyph.draw(filterpen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())
    return aGlyph
Example #7
0
def flattenGlyph(aGlyph, threshold=10, segmentLines=True):
    """
    Convenience function that applies the **FlattenPen** pen to a glyph in place.
    """
    if len(aGlyph) == 0:
        return aGlyph
    from fontTools.pens.recordingPen import RecordingPen
    recorder = RecordingPen()
    filterpen = FlattenPen(recorder, approximateSegmentLength=threshold, segmentLines=segmentLines)
    aGlyph.draw(filterpen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())
    return aGlyph
def samplingGlyph(aGlyph, steps=10):
    """
    Convenience function that applies the **SamplingPen** pen to a glyph in place.
    """
    if len(aGlyph) == 0:
        return aGlyph
    from fontTools.pens.recordingPen import RecordingPen
    recorder = RecordingPen()
    filterpen = SamplingPen(recorder, steps=steps)
    aGlyph.draw(filterpen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())
    return aGlyph
Example #9
0
def thresholdAttrGlyph(aGlyph, threshold=0.5):
    """
    Like fontPens.thresholdPen.thresholdGlyph, but preserves some glyph- and
    point-level attributes that are not preserved by that method.
    """
    # preserve glyph-level attributes
    attrnames = ['anchors']
    attrs = {
        k: getattr(aGlyph, k, None)
        for k in attrnames if hasattr(aGlyph, k)
    }  # noqa: E501

    # filter with ThresholdPen into recording pen
    recorder = RecordingPen()
    filterpen = ThresholdPen(recorder, threshold)
    aGlyph.draw(filterpen)
    aGlyph.clear()
    recorder.replay(aGlyph.getPen())

    # restore glyph-level attributes
    for k, v in attrs.items():
        setattr(aGlyph, k, v)

    return aGlyph
path = BezierPath()
path.moveTo((100, 100))
path.curveTo((200, 100), (300, 200), (300, 300))
path.lineTo((100, 300))
path.closePath()
path.oval(0, 0, 200, 90)
path.moveTo((250, 250))
path.arc((250, 250), 200, 0, 120, False)
# path.skew() will trigger bad results
# https://github.com/justvanrossum/drawbot-skia/issues/7
# path.skew(10, 20)

fill(None)
stroke(0)
strokeWidth(2)
drawPath(path)

pen = RecordingPen()
path.drawToPen(pen)

path = BezierPath()
pen.replay(path)

stroke(None)
fill(0, 0.3)
drawPath(path)

ppen = RecordingPointPen()
path.drawToPointPen(ppen)
Example #11
0
def build(instance, isTTF, version):
    font = instance.parent
    source = font.masters[0]

    fea, marks = makeFeatures(instance, source)

    source.blueValues = []
    source.otherBlues = []

    for zone in sorted(source.alignmentZones):
        pos = zone.position
        size = zone.size
        vals = sorted((pos, pos + size))
        if pos == 0 or size >= 0:
            source.blueValues.extend(vals)
        else:
            source.otherBlues.extend(vals)

    fontinfo = f"""
        FontName {instance.fontName}
        OrigEmSqUnits {font.upm}
        DominantV {source.verticalStems}
        DominantH {source.horizontalStems}
        BaselineOvershoot {source.blueValues[0]}
        BaselineYCoord {source.blueValues[1]}
        LcHeight {source.blueValues[2]}
        LcOvershoot {source.blueValues[3] - source.blueValues[2]}
        CapHeight {source.blueValues[4]}
        CapOvershoot {source.blueValues[5] - source.blueValues[4]}
        AscenderHeight {source.blueValues[6]}
        AscenderOvershoot {source.blueValues[7] - source.blueValues[6]}
        Baseline5 {source.otherBlues[1]}
        Baseline5Overshoot {source.otherBlues[0] - source.otherBlues[1]}

        FlexOK true
        BlueFuzz 1
    """

    characterMap = {}
    glyphs = {}
    metrics = {}
    layerSet = {g.name: g.layers[source.id] for g in font.glyphs}

    if isTTF:
        from fontTools.pens.cu2quPen import Cu2QuPen
        from fontTools.pens.recordingPen import RecordingPen

        for glyph in font.glyphs:
            layer = glyph.layers[source.id]
            pen = RecordingPen()
            layer.draw(pen)
            layer.paths = []
            layer.components = []
            pen.replay(Cu2QuPen(layer.getPen(), 1.0, reverse_direction=True))

    for glyph in font.glyphs:
        if not glyph.export and not isTTF:
            continue
        name = glyph.name

        for code in glyph.unicodes:
            characterMap[int(code, 16)] = name

        layer = glyph.layers[source.id]
        width = 0 if name in marks else layer.width

        pen = BoundsPen(layerSet)
        layer.draw(pen)
        metrics[name] = (width, pen.bounds[0] if pen.bounds else 0)

        if isTTF:
            from fontTools.pens.ttGlyphPen import TTGlyphPen

            pen = TTGlyphPen(layerSet)
            if layer.paths:
                # Decompose and remove overlaps.
                path = Path()
                layer.draw(DecomposePathPen(path, layerSet))
                path.simplify(fix_winding=True, keep_starting_points=True)
                path.draw(pen)
            else:
                # Composite-only glyph, no need to decompose.
                layer.draw(FlattenComponentsPen(pen, layerSet))
            glyphs[name] = pen.glyph()
        else:
            from fontTools.pens.t2CharStringPen import T2CharStringPen

            # Draw glyph and remove overlaps.
            path = Path()
            layer.draw(DecomposePathPen(path, layerSet))
            path.simplify(fix_winding=True, keep_starting_points=True)

            # Build CharString.
            pen = T2CharStringPen(width, None)
            path.draw(pen)
            glyphs[name] = pen.getCharString()

    vendor = font.customParameters["vendorID"]
    names = {
        "copyright": font.copyright,
        "familyName": instance.familyName,
        "styleName": instance.name,
        "uniqueFontIdentifier": f"{version};{vendor};{instance.fontName}",
        "fullName": instance.fullName,
        "version": f"Version {version}",
        "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=isTTF)
    fb.updateHead(fontRevision=version, created=date, modified=date)
    fb.setupGlyphOrder(font.glyphOrder)
    fb.setupCharacterMap(characterMap)
    fb.setupNameTable(names, mac=False)
    fb.setupHorizontalHeader(
        ascent=source.ascender,
        descent=source.descender,
        lineGap=source.customParameters["typoLineGap"],
    )

    if isTTF:
        fb.setupGlyf(glyphs)
    else:
        privateDict = {
            "BlueValues": source.blueValues,
            "OtherBlues": source.otherBlues,
            "StemSnapH": source.horizontalStems,
            "StemSnapV": source.verticalStems,
            "StdHW": source.horizontalStems[0],
            "StdVW": source.verticalStems[0],
        }

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

        fb.setupCFF(names["psName"], fontInfo, glyphs, privateDict)

    fb.setupHorizontalMetrics(metrics)

    codePages = [CODEPAGE_RANGES[v] for v in font.customParameters["codePageRanges"]]
    fb.setupOS2(
        version=4,
        sTypoAscender=source.ascender,
        sTypoDescender=source.descender,
        sTypoLineGap=source.customParameters["typoLineGap"],
        usWinAscent=source.ascender,
        usWinDescent=-source.descender,
        sxHeight=source.xHeight,
        sCapHeight=source.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),
    )

    underlineThickness = int(source.customParameters["underlineThickness"])
    underlinePosition = int(source.customParameters["underlinePosition"])
    fb.setupPost(
        keepGlyphNames=False,
        underlineThickness=underlineThickness,
        underlinePosition=underlinePosition + underlineThickness // 2,
    )

    fb.font["meta"] = meta = newTable("meta")
    meta.data = {"dlng": "Arab", "slng": "Arab"}

    fb.addOpenTypeFeatures(fea)

    if isTTF:
        from fontTools.ttLib.tables import ttProgram

        fb.setupDummyDSIG()

        fb.font["gasp"] = gasp = newTable("gasp")
        gasp.gaspRange = {0xFFFF: 15}

        fb.font["prep"] = prep = newTable("prep")
        prep.program = ttProgram.Program()
        assembly = ["PUSHW[]", "511", "SCANCTRL[]", "PUSHB[]", "4", "SCANTYPE[]"]
        prep.program.fromAssembly(assembly)
    else:
        from cffsubr import subroutinize

        subroutinize(fb.font)

    return fb.font