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
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
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 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
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 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)
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