for p in selected_points(c): if direction != "h" and direction != "v" and direction != "hv": return False if direction == "h" or direction == "hv": guide = fontforge.contour() # place an horizontal guideline using point position guide.moveTo(-1000, p.y) guide.lineTo(2000, p.y) guides += guide if direction == "v" or direction == "hv": guide = fontforge.contour() # place a vertical guideline using point position guide.moveTo(p.x, -2000) guide.lineTo(p.x, 3000) guides += guide f.guide = guides glyph.layers[glyph.activeLayer] = l # Is there a better way to refresh the screen? print "done" return True if fontforge.hasUserInterface(): menuText_h = "Horizontal guidelines" menuText_v = "Vertical guidelines" menuText_hv = "Hor/Vert guidelines" fontforge.registerMenuItem(lambda r, g: add_guides(g, "h"), None, None, "Glyph", None, menuText_h) fontforge.registerMenuItem(lambda r, g: add_guides(g, "v"), None, None, "Glyph", None, menuText_v) fontforge.registerMenuItem(lambda r, g: add_guides(g, "hv"), None, None, "Glyph", None, menuText_hv)
import sys, os, fontforge sys.path.append(os.path.join(os.environ['HOME'], 'src/pysilfont/scripts')) import samples.demoFunctions from samples.demoFunctions import functionList, callFunctions #from samples.demoCallFunctions import callFunctions def toolMenuFunction(functionGroup,font) : reload (samples.demoFunctions) callFunctions(functionGroup,font) funcList=functionList() for functionGroup in funcList : menuType = funcList[functionGroup][0] fontforge.registerMenuItem(toolMenuFunction,None,functionGroup,menuType,None,functionGroup); print functionGroup, " registered" ''' This script needs to be called from one of the folders that FontForge looks in for scripts to run when it is started. With current versions of FontForge, one is Home/.config/fontforge/python. You may need to turn on showing hidden files (ctrl-H in Nautilus) before you can see the .config folder. Within there create a one-line python script, say call sampledemo.py containing a call to this script, eg: execfile("/home/david/src/pysilfont/scripts/samples/demoAddToMenu.py") Due to the reload(samples.demoFunctions) line above, changes functions defined in demoFunctions.py are dynamic, ie FontForge does not have to be restarted (as would be the case if the functions were called directly from the tools menu. Functions can even be added dynamically to the function groups. If new function groups are defined, FontForge does have to be restarted to add them to the tools menu.
gentime = time.time() base_font_face = '''@font-face { font-family: '{fontname}'; src: url('{fontname}.eot?{gentime}'); src: url('{fontname}.eot?{gentime}#iefix') format('embedded-opentype'), url('{fontname}.svg?{gentime}#{fontname}') format('svg'), url('{fontname}.woff?{gentime}') format('woff'), url('{fontname}.ttf?{gentime}') format('truetype'); font-weight: normal; font-style: normal; }''' if 0xe000 in font:#dont bother if not icon based font! f = open(font_file_name + ".html",'w') htmlbase = "<style>"+base_font_face+"\n.fontdemo:before { display: inline; font-size: 40px; font-family: {fontname}; font-style: normal; }\n" htmlbase = htmlbase.replace("{fontname}", font.fontname) htmlbase = htmlbase.replace("{gentime}", "%d" % gentime) f.write(htmlbase) f.write(css+'</style>') f.write(html+'</table>') f.close(); #write css file too f = open(font_file_name + ".css",'w') f.write(base_font_face.replace("{fontname}", font.fontname).replace("{gentime}", "%d" % gentime)) fontforge.postNotice("Finished", "Files have been output") fontforge.registerMenuItem(GenerateFonts,None,None,"Font",None,"Generate Webfonts");
def nextStep(junk,glyph): """Roll a font forward""" myFont = fontforge.activeFont() snapShot(myFont) fontforge.logWarning("All done!") def newBackgroundLayerDialog(junk,glyph): layerName = fontforge.askString("New Background Layer","Layer name?",myDate) newBackgroundLayer(layerName) def newBackgroundLayer(layerName): myFont = fontforge.activeFont() myFont.layers.add(layerName,0,0) def finalContours(junk,glyph): glyph = fontforge.activeGlyph() glyph.round() glyph.addExtrema("all") glyph.simplify() glyph.simplify() glyph.correctDirection() print "Finalised %s" % glyph.glyphname if fontforge.hasUserInterface(): keyShortcut="Ctl+Shft+n" fontforge.registerMenuItem(nextStep,None,None,("Font","Glyph"),keyShortcut,"Next Step"); fontforge.registerMenuItem(newBackgroundLayerDialog,None,None,("Font","Glyph"),None,"New BG Layer"); keyShortcut="Ctl+Shft+p" fontforge.registerMenuItem(finalContours,None,None,("Font","Glyph"),keyShortcut,"Finalise Contours");
#!/usr/bin/env python import fontforge from subprocess import Popen, PIPE print "FontForge open python scripts folder extension registered" def folder(dummy, font): subprocess.Popen(['xdg-open', '/home/nico/.config/fontforge/python/'], shell=False, stdin=subprocess.PIPE) if fontforge.hasUserInterface(): UIelement = ("Font", "Glyph") keyShortcut = "" menuText = "Open python scripts folder" fontforge.registerMenuItem(folder, None, None, UIelement, keyShortcut, menuText)
f['v'].right_side_bearing = diferencia if 'w' in f: f['w'].left_side_bearing = diferencia f['w'].right_side_bearing = diferencia if 'y' in f: f['y'].left_side_bearing = diferencia f['y'].right_side_bearing = diferencia if 'x' in f: f['x'].left_side_bearing = 0 f['x'].right_side_bearing = 0 diferencia = int( ( (nWidth * xProportion ) * params['xBoth'] / 100 ) - f['x'].width ) / 2 f['x'].left_side_bearing = diferencia f['x'].right_side_bearing = diferencia if 'z' in f: f['z'].left_side_bearing = 0 f['z'].right_side_bearing = 0 diferencia = int( ( (nWidth * zProportion ) * params['zBoth'] / 100 ) - f['z'].width ) / 2 f['z'].left_side_bearing = diferencia f['z'].right_side_bearing = diferencia print "done"; if fontforge.hasUserInterface(): keyShortcut = None menuText = "Spacing macro" fontforge.registerMenuItem(spacing, None, None, "Font", keyShortcut, menuText)
def genSNBsamp(thisFont): """Generate a OTF of this font with points rounded to integers""" systemFontsDir = '/Users/Pathum/src/github.com/mooniak/stick-no-bills-font/tests/fonts/' thisOTF = thisFont.fontname + ".otf" thisSystemFont = systemFontsDir + thisOTF thisFont.generate(thisOTF,flags=("round", "dummy-dsig", "PfEd-comments", "PfEd-colors", "PfEd-lookups", "PfEd-guidelines", "PfEd-background")) fontforge.logWarning(" Generated " + thisOTF) if os.path.exists(systemFontsDir): shutil.copy(thisOTF, thisSystemFont) if os.path.isfile(thisSystemFont): fontforge.logWarning(" Installed " + thisOTF) def writeDoc(document,fileName): outfile = open(fileName, "w") outfile.write(document) outfile.close() def genSNB(junk,glyph): """Roll a font forward""" myFont = fontforge.activeFont() genSNBsamp(myFont) fontforge.logWarning("All done!") if fontforge.hasUserInterface(): keyShortcut="Ctl+Shft+n" fontforge.registerMenuItem(genSNB,None,None,("Font","Glyph"),keyShortcut,"Generate SNB");
sys.argv.append('') from ipython_view import * import gdraw def runShell(data = None, glyphOrFont = None): """Run an ipython shell in a gtk-widget with the current fontforge module in the namespace""" FONT = "monospace 10" W = gtk.Window() W.set_resizable(True) W.set_size_request(750,550) W.set_title("FontForge Interactive Python Shell") S = gtk.ScrolledWindow() S.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) V = IPythonView() V.updateNamespace({'fontforge': fontforge}) #important V.modify_font(pango.FontDescription(FONT)) V.set_wrap_mode(gtk.WRAP_CHAR) S.add(V) W.add(S) W.show_all() W.connect('delete_event',lambda x,y:False) # W.connect('destroy',lambda x:gtk.main_quit()) # Start gtk loop here! gdraw.gtkrunner.sniffwindow(W) gdraw.gtkrunner.start() if fontforge.hasUserInterface(): fontforge.registerMenuItem(runShell, None, None, ("Font","Glyph"), None, "Interactive Python Shell");
flags = ("opentype", "dummy-dsig", "round", "short-post") out = "" def Preview(re, obj): if type(obj).__name__ == "font": font = obj else: font = obj.font global out if not out: if font.layers[1].is_quadratic: out = tempfile.NamedTemporaryFile(suffix=".ttf").name else: out = tempfile.NamedTemporaryFile(suffix=".otf").name font.generate(out, flags=flags) if not re: cmd = "%s %s" %(viewer, out) subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) fontforge.registerMenuItem(Preview, None, None, ("Font", "Glyph"), "P", "Preview", "Preview font") fontforge.registerMenuItem(Preview, None, True, ("Font", "Glyph"), "R", "Preview", "Re-view font")
(0 = curve becomes a line, 100 = control points converge)""") if value == None: return value else: return float(value)/100.0 def fit_selected_segments (registerobject, glyph): l = glyph.layers[glyph.activeLayer] selection = map (selected_segments, l) try: v = ask_user_fit () except: fontforge.postError("Bad Value", "Input was not a number") return if v == None: return for contour in selection: for s in filter (is_curve_seg, contour): fit_segment (s, v) glyph.layers[glyph.activeLayer] = l print 'done' return fit_selected_segments if fontforge.hasUserInterface(): keyShortcut = None menuText = "Fit curve" fontforge.registerMenuItem(fit_curve(), None, None, \ "Glyph", keyShortcut, menuText)
import fontforge """ Custon Python Scripting for FontForge to force all points to extrema for every glyph within the font without notification. Is useful for a face where the glyphs have been drawn and then needs to ensure linting. Unsure whether registerMenuItem is correct as developed and used by the File > Execute Script method to allow for alteration on the fly. """ def extremaForce(): font = fontforge.activeFont() for glyph in font.glyphs(): noExtremaErrors = False while noExtremaErrors == False: errors = glyph.validate(True) noExtremaErrors = True if e & 0x20 == 0x20: noExtremaErrors = False glyph.addExtrema('only_good_rm') if noExtremaErrors == False: glyph.simplify(0, 'setstarttoextrema') fontforge.registerMenuItem(extremaForce, None, None, "Font", None, "SubMenu", "Force Glyphs to Extrema")
print "\nProcessing double diacritics..." for n in xrange(0, len(two_d)): f[two_d[n]].build() print "Building: %s" % f[two_d[n]] print "\nProcessing triple diacritics..." for n in xrange(0, len(three_d)): f[three_d[n]].build() print "Building: %s" % f[three_d[n]] if fontforge.hasUserInterface(): print "Loaded progressive refresh script." fontforge.registerMenuItem(refresh_compound_glyphs, None, None , "Font", None, "Refresh All Compound Glyphs") else: if len(argv) == 2: inputfile = argv[1] outputfile = inputfile elif len(argv) == 3: script, inputfile, outputfile = argv else: print "Usage: progressive_accents.py inputfile [outputfile]" exit(1) font = fontforge.open(inputfile) # print "Opening %s..." % fontfile refresh_compound_glyphs(None, font)
evaleddict.update(constant) for k, v in getDict(name, fontOrGlyph).items(): # adding locals() for font and glyph attribute access evaleddict[k] = eval(v, evaleddict, locals()) return evaleddict # for functions that can be called with either a font or a glyph, this returns the font def getFont(fontOrGlyph): return fontOrGlyph if isinstance(fontOrGlyph, ff.font) else fontOrGlyph.font ## registering the menu items ff.registerMenuItem(viewParameters, None, None, ("Font", "Glyph"), None, "MetaForge", "View/edit parameters...") ff.registerMenuItem(updateAllGlyphs, None, None, "Font", None, "MetaForge", "Update all glyph parametrizations") ff.registerMenuItem(viewPoints, None, None, "Glyph", None, "MetaForge", "View/edit parametrized points...") ff.registerMenuItem(parametrizePoints, None, None, "Glyph", None, "MetaForge", "Parametrize selected points...") ff.registerMenuItem(deparametrizePoints, None, None, "Glyph", None, "MetaForge", "Deparametrize selected points") ff.registerMenuItem(askImport, None, None, ("Font", "Glyph"), None, "MetaForge", "Import parametrizations...") ff.registerMenuItem(askExport, None, None, ("Font", "Glyph"), None, "MetaForge", "Export parametrizations...") ff.registerImportExport(importParameters, exportParameters, None, "MetaForge Parametrization", "mfg")
# Set PostScript Style Name (FamilyName-Style) newFont.weight = "Regular" newFont.fontname = newFontName.replace(' ', '') + '-' + newFont.weight # Set PostScript Family Name (Family Name) newFont.familyname = newFontName # Set PostScript Full Name (Family Name Style) newFont.fullname = newFontName + ' ' + font.weight # let folks know what we did message = "Simplepolated %s: %s%%/%s%% %s/%s" % ( newFont.fullname, int(100 - interpolationamount[x] * 100), int(interpolationamount[x] * 100), source1.fontname, source2.fontname) note(message) # Only enable Tool menu item if 2 characters are selected def shouldWeAppear(registerobject, font): fonts = fontforge.fonts() if len(fonts) == 2: return True else: return False # Register this PlugIn in the Tools menu if fontforge.hasUserInterface(): # keyShortcut="Ctl+Shft+n" keyShortcut = None menuText = "Simplepolate Fonts" fontforge.registerMenuItem(simplepolateFonts, shouldWeAppear, None, "Font", keyShortcut, menuText)
except: import_successful = False fontforge.postError( "Cannot import Python module glyphcomponents", "This script is made to work with the SFDs residing in top_level/SFD and the module in top_level/tools." ) if import_successful: try: reload(glyphcomponents) glyphComponents = glyphcomponents.glyphComponents if type(object).__name__ == "font": for glyph in object.selection.byGlyphs: if glyph.glyphname in glyphComponents.keys(): composeAccented(glyph, glyphComponents) else: continue else: if object.glyphname in glyphComponents.keys(): composeAccented(object, glyphComponents) else: logWarning('This glyph has no entry in the dictionary') except BaseException as e: fontforge.postError("Building glyphs failed", str(e)) fontforge.registerMenuItem(buildAccentedGlyphs, None, None, ("Font", "Glyph"), None, "Glyphbuilder")
import fontforge """ Custon Python Scripting for FontForge to move all points to Integral Coordinates for every glyph within the font without notification. Is useful for a face where the glyphs have been drawn and then needs to ensure linting. Unsure whether registerMenuItem is correct as developed and used by the File > Execute Script method to allow for alteration on the fly. """def integralPoints(): font = fontforge.activeFont() for glyph in font.glyphs(): noRoundErrors = False while noRoundErrors == False: errors = glyph.validate(True) noRoundErrors = True if errors & 0x80000 == 0x80000: noRoundErrors = False glyph.round() fontforge.registerMenuItem(integralPoints,None,None,"Font", None, "SubMenu", "Force Glyphs to Integral Points");
acc = 0 for c in begin_state: for i, p in enumerate(c): Locs[(math.floor(p.x), math.floor(p.y))] = { "contour": c, "idx": i, "total_idx": acc, "on": p.on_curve } acc += 1 new_hoi = fontforge.layer() for i, c in enumerate(hoi_paths): for p in c: p.name = '' new_hoi += c start = c[0] l = (math.floor(start.x), math.floor(start.y)) if l in Locs: info = Locs[l] new_hoi[i][0].name = str(info["total_idx"]) glyph.layers["HOI paths"] = new_hoi fontforge.registerMenuItem(autoNameGlyphPoints, None, None, "Glyph", None, "_Higher-Order Interpolation", "Attempt to automatically _name points")
pindex = p[1] point = contour[pindex] cpoint = [] # control points if direct in [0, -1]: # previous control try: if not contour[pindex-1].on_curve: cpoint.append((cindex, pindex-1)) except IndexError: pass if direct in [0, 1]: try: if not contour[pindex+1].on_curve: cpoint.append((cindex, pindex+1)) except IndexError: pass for c in cpoint: layer[c[0]][c[1]].x = point.x layer[c[0]][c[1]].y = point.y glyph.layers[glyph.activeLayer] = layer if fontforge.hasUserInterface(): fontforge.registerMenuItem(retractcontrol, None, -1, "Glyph", RETRACT_PREV_CP_KEY, "Points", "Retract Prev CP"); fontforge.registerMenuItem(retractcontrol, None, 1, "Glyph", RETRACT_NEXT_CP_KEY, "Points", "Retract Next CP"); fontforge.registerMenuItem(retractcontrol, None, 0, "Glyph", RETRACT_BOTH_CP_KEY, "Points", "Retract Both CP");
glif.left_side_bearing = -20 else: glif.useRefsMetrics(base, True) elif base == 'space.stack': glif.useRefsMetrics(base, False) glif.left_side_bearing = 30 glif.right_side_bearing = 30 else: glif.useRefsMetrics(base, True) def buildAccentedGlyphs(junk,object): # """Get the names of the selected glyphs in a font. # # Call the composing function for every glyph that has an entry # in the dictionary.""" # componentsByFontname(font, font.fontname) if type(object).__name__ == "font": for glyph in object.selection.byGlyphs: if glyph.glyphname in glyphComponents.keys(): composeAccented(glyph) else: continue else: if object.glyphname in glyphComponents.keys(): composeAccented(object) else: logWarning('This glyph has no entry in the dictionary') fontforge.registerMenuItem(buildAccentedGlyphs,None,None,("Font","Glyph"),None,"Glyphbuilder");
boundingbox = newglyph.boundingBox() elif boundingbox[3]-boundingbox[1] < 1000: print("smaller than 1000") while boundingbox[3]-boundingbox[1] < 1000: newglyph.transform(scaleup) boundingbox = newglyph.boundingBox() boundingbox = newglyph.boundingBox() #fontforge.postNotice("Info", "y1 = %f, y2 = %f" % (boundingbox[1], boundingbox[3])) print("y1 = %f, y2 = %f" % (boundingbox[1], boundingbox[3])) if min(boundingbox[1], boundingbox[3]) > -200: print("need to move down") while min(boundingbox[1], boundingbox[3]) > -200: newglyph.transform(translatedown) boundingbox = newglyph.boundingBox() elif max(boundingbox[1], boundingbox[3]) > 1000: while max(boundingbox[1], boundingbox[3]) > 1000: print("need to move up") newglyph.transform(translateup) boundingbox = newglyph.boundingBox() newglyph.right_side_bearing = 0 newglyph.left_side_bearing = 0 except Exception as e: fontforge.postError("Error", "Something went wrong!\n\n"+str(e)) #newglyph.clear() font.removeGlyph(iconbase) fontforge.registerMenuItem(importIcon,None,None,"Font",None,"Import Icon");
thisOTF = thisFont.fontname + ".otf" thisSystemFont = systemFontsDir + thisOTF thisFont.generate(thisOTF, flags=("round", "dummy-dsig", "PfEd-comments", "PfEd-colors", "PfEd-lookups", "PfEd-guidelines", "PfEd-background")) fontforge.logWarning(" Generated " + thisOTF) if os.path.exists(systemFontsDir): shutil.copy(thisOTF, thisSystemFont) if os.path.isfile(thisSystemFont): fontforge.logWarning(" Installed " + thisOTF) def writeDoc(document, fileName): outfile = open(fileName, "w") outfile.write(document) outfile.close() def genAyanna(junk, glyph): """Roll a font forward""" myFont = fontforge.activeFont() genAyannasamp(myFont) fontforge.logWarning("All done!") if fontforge.hasUserInterface(): keyShortcut = "Ctl+Shft+n" fontforge.registerMenuItem(genAyanna, None, None, ("Font", "Glyph"), keyShortcut, "Generate Ayanna")
g.correctDirection(); g.canonicalStart(); g.canonicalContours(); testname = 'test-' + time.strftime("%H-%M-%m-%d-%Y") + "-" + ft.fontname; ft.familyname = testname; ft.fullname = testname; ft.fontname = testname; # uniqueID # Fullname ft.appendSFNTName('English (US)', 'Version', testname); ft.appendSFNTName('English (US)', 'Descriptor', 'this is just a test version generated on' + time.strftime("%H-%M-%m-%d-%Y")); ft.appendSFNTName('English (US)', 'Compatible Full', testname ); ft.appendSFNTName('English (US)', 'Preferred Family', testname ); ft.appendSFNTName('English (US)', 'Fullname', testname ); target=testname +'.otf'; ft.generate(target, flags=('opentype','dummy-dsig','apple','no-hints','no-flex'),layer=aLayer) shutil.copy(target,fDir+target); ft.close(); os.remove(target); os.remove('temp.sfd'); print target + " generated and installed in ~/.fonts/ "; if fontforge.hasUserInterface(): #keyShortcut="" UIelement = ("Font","Glyph") menuText = "Generate test otf - drop in font folder" fontforge.registerMenuItem(genTestOTF,None,None,UIelement,keyShortcut,menuText);
if glyph.glyphname in doneglyphs: print("Warning: skipping {}".format(repr(glyph)), file=sys.stderr) else: glyph.width += 300 glyph.export(tempf, pixelsize=font.em) vbdiff = viewBox_diff(glyph, tempf) im = svg_to_PILImage(tempf) (should_exit, should_prev) = pop_window_for_image(font, glyph, im, imaccent, vbdiff, vbdiffa) os.remove(tempf) glyph.width -= 300 if should_exit: return False elif should_prev and i != 0: return process_glyph(i - 1, glyphs[i - 1]) elif should_prev: return process_glyph(0, glyphs[0]) elif i + 1 == len(glyphs): return True else: return process_glyph(i + 1, glyphs[i + 1]) return True process_glyph(0, glyphs[0]) fontforge.registerMenuItem(main, None, None, "Font", None, "QuickAnchors")
guide.moveTo (-1000, p.y) guide.lineTo (2000, p.y) guides += guide if direction == 'v' or direction == 'hv': guide = fontforge.contour() #place a vertical guideline using point position guide.moveTo (p.x, -2000) guide.lineTo (p.x, 3000) guides += guide f.guide = guides glyph.layers[glyph.activeLayer]=l #Is there a better way to refresh the screen? print 'done' return True if fontforge.hasUserInterface(): menuText_h = "Horizontal guidelines" menuText_v = "Vertical guidelines" menuText_hv = "Hor/Vert guidelines" fontforge.registerMenuItem(lambda r, g: add_guides (g, 'h') ,\ None, None, \ "Glyph", None, menuText_h) fontforge.registerMenuItem(lambda r, g: add_guides (g, 'v') ,\ None, None, \ "Glyph", None, menuText_v) fontforge.registerMenuItem(lambda r, g: add_guides (g, 'hv') ,\ None, None, \ "Glyph", None, menuText_hv)
glyphname = str(name) + '.'+ str(x+1) g = font.createChar(-1, glyphname) g.preserveLayerAsUndo(1) # interpolate the top layer g.layers[1] = source1.layers[1].interpolateNewLayer(source2.layers[1], interpolationamount[x]) # interpolate the width g.width = source1.width + (source2.width - source1.width)*interpolationamount[x] # interpolate the vwidth g.vwidth = source1.vwidth + (source2.vwidth - source1.vwidth)*interpolationamount[x] # let folks know what we did message = "Simplepolator created '%s' that is %s%% '%s' and %s%% '%s'" % (glyphname, int(100-interpolationamount[x]*100), source1.glyphname, int(interpolationamount[x]*100), source2.glyphname) note(message) # Only enable Tool menu item if 2 characters are selected def shouldWeAppear(registerobject, font): font = fontforge.activeFont() glyphs = [] for g in font.selection.byGlyphs: glyphs.append(g) if len(glyphs) == 2: return True else: return False # Register this PlugIn in the Tools menu if fontforge.hasUserInterface(): # keyShortcut="Ctl+Shft+n" keyShortcut = None menuText = "Simplepolate" fontforge.registerMenuItem(simplepolate,shouldWeAppear,None,"Font",keyShortcut,menuText);
wmax, hmax, wpct, hpct = get_max_size(afont) wavg, havg = get_avg_size(afont) em = afont.ascent + afont.descent msg = ('The max width and height of the selected glyphs are:\n' 'Points:\t%d\t%d\nScale Factor to 100%%:\t%.3f\t %.3f%%') msg = msg % (wmax, hmax, wpct, hpct) msg += "\nEm:\t%d" % em msg += '\nThe average width and height of the selected glyphs are:\n' msg += 'Points:' msg += str(wavg) msg += '\t' msg += str(havg) fontforge.postError('Max Demension', msg) if fontforge.hasUserInterface(): fontforge.registerMenuItem(GetSelectedBound, None, None, "Font", None, "Metrics", "Max Selected Size"); fontforge.registerMenuItem(CenterHeight, None, None, "Font", None, "Metrics", "Center in Height"); fontforge.registerMenuItem(CenterGlyph, None, None, "Font", None, "Metrics", "Center in Glyph"); fontforge.registerMenuItem(YOffset, None, None, "Font", None, "Metrics", "Y Offset"); fontforge.registerMenuItem(BoundToSquare, None, None, "Font", None, "Metrics", "Bounding to Square"); fontforge.registerMenuItem(ScaleToEm, None, None, "Font", None, "Transform", "Scale to Em"); fontforge.registerMenuItem(ScaleToSquare, None, None, "Font", None, "Transform", "Scale to Sqare"); fontforge.registerMenuItem(ScaleToSquare, None, None, "Font", None, "Transform", "Auto Adjust");
#!/usr/bin/python import os, sys, time, fontforge print "FontForge fea export extension registered" font = fontforge.activeFont() def feaexport(dummy, font): name = font.fontname + "-" + font.weight + "-" + "exported" + time.strftime( "-%Y-%m-%d-%H-%M") + ".fea" font.generateFeatureFile(name) msg = "Exported OpenType code to " + name + ".\n" + "Check copyright and license before reusing any smart font code." fontforge.logWarning(msg) if fontforge.hasUserInterface(): keyShortcut = "" menuText = "Export OpenType to FEA file" fontforge.registerMenuItem(feaexport, None, None, "Font", keyShortcut, menuText)
wavg, havg = get_avg_size(afont) em = afont.ascent + afont.descent msg = ('The max width and height of the selected glyphs are:\n' 'Points:\t%d\t%d\nScale Factor to 100%%:\t%.3f\t %.3f%%') msg = msg % (wmax, hmax, wpct, hpct) msg += "\nEm:\t%d" % em msg += '\nThe average width and height of the selected glyphs are:\n' msg += 'Points:' msg += str(wavg) msg += '\t' msg += str(havg) fontforge.postError('Max Demension', msg) if fontforge.hasUserInterface(): fontforge.registerMenuItem(GetSelectedBound, None, None, "Font", None, "Metrics", "Max Selected Size") fontforge.registerMenuItem(CenterHeight, None, None, "Font", None, "Metrics", "Center in Height") fontforge.registerMenuItem(CenterGlyph, None, None, "Font", None, "Metrics", "Center in Glyph") fontforge.registerMenuItem(YOffset, None, None, "Font", None, "Metrics", "Y Offset") fontforge.registerMenuItem(BoundToSquare, None, None, "Font", None, "Metrics", "Bounding to Square") fontforge.registerMenuItem(ScaleToEm, None, None, "Font", None, "Transform", "Scale to Em") fontforge.registerMenuItem(ScaleToSquare, None, None, "Font", None, "Transform", "Scale to Sqare") fontforge.registerMenuItem(ScaleToSquare, None, None, "Font", None, "Transform", "Auto Adjust")
# =============== important for fontforge!!!! ================= # set proper sys.path for sub modules def setup_syspath(): import sys import os # setup module path for local modules of Fontforge scripts. # put local module in 'modules' subdirectory: ~/.FontForge/python/modules x = sys._getframe().f_code.co_filename modpath = os.path.join(os.path.dirname(x), 'modules') if modpath not in sys.path: sys.path.append(modpath) setup_syspath() import gdraw import gtk import fontforge from kmetricwin import KMetricWin def DemoGtk(junk, afont): '''Show max height and width of the selected glyphs.''' w = KMetricWin(junk, afont) # Start gtk loop here! gdraw.gtkrunner.start() if fontforge.hasUserInterface(): fontforge.registerMenuItem(DemoGtk, None, None, "Font", None, "Metrics", "Kerning Metrics");
# TODO copy foreground to new layer! XXX # myFont.layer def nextStep(junk, glyph): """Roll a font forward""" myFont = fontforge.activeFont() snapShot(myFont) fontforge.logWarning("All done!") def finalContours(junk, glyph): glyph = fontforge.activeGlyph() glyph.round(100) glyph.addExtrema("all") glyph.simplify() glyph.simplify() glyph.correctDirection() print "Finalised %s" % glyph.glyphname if fontforge.hasUserInterface(): keyShortcut = "Ctl+Shft+n" fontforge.registerMenuItem(nextStep, None, None, ("Font", "Glyph"), keyShortcut, "Generate Sample") keyShortcut = "Ctl+Shft+p" fontforge.registerMenuItem(finalContours, None, None, ("Font", "Glyph"), keyShortcut, "Finalise Contours")
c = point_list_to_contour(points, contour.closed, contour.is_quadratic) new_layer += c glyph.layers[glyph.activeLayer] = new_layer def points_are_selected(glyph): layer = glyph.layers[glyph.activeLayer] if layer.is_quadratic: return False # Quadratics are not currently supported. for contour in layer: for point in contour: if point.selected: return True return False fontforge.registerMenuItem((lambda _, glyph: make_pin(glyph)), (lambda _, glyph: points_are_selected(glyph)), None, "Glyph", "None", "Turn points into pins") #-------------------------------------------------------------------------- def reautohint(f): for g in f.glyphs(): if not g.manualHints: g.autoHint() fontforge.registerMenuItem((lambda _, glyph: glyph.autoHint()), (lambda _, glyph: not glyph.manualHints), None, "Glyph", "None", "Autohint") fontforge.registerMenuItem((lambda _, font: reautohint(font)),
def genAbhayasamp(thisFont): """Generate a OTF of this font with points rounded to integers""" systemFontsDir = '/Users/Pathum/src/github.com/mooniak/abhaya-libre-font/tests/fonts/' thisOTF = thisFont.fontname + ".otf" thisSystemFont = systemFontsDir + thisOTF thisFont.generate(thisOTF,flags=("round", "dummy-dsig", "PfEd-comments", "PfEd-colors", "PfEd-lookups", "PfEd-guidelines", "PfEd-background")) fontforge.logWarning(" Generated " + thisOTF) if os.path.exists(systemFontsDir): shutil.copy(thisOTF, thisSystemFont) if os.path.isfile(thisSystemFont): fontforge.logWarning(" Installed " + thisOTF) def writeDoc(document,fileName): outfile = open(fileName, "w") outfile.write(document) outfile.close() def genAbhaya(junk,glyph): """Roll a font forward""" myFont = fontforge.activeFont() genAbhayasamp(myFont) fontforge.logWarning("All done!") if fontforge.hasUserInterface(): keyShortcut="Ctl+Shft+n" fontforge.registerMenuItem(genAbhaya,None,None,("Font","Glyph"),keyShortcut,"Generate Abahay");
def genFont(thisFont): """Generate a OTF of this font with points rounded to integers""" systemFontsDir = "/Users/Pathum/src/pathumego/ayanna-narrow/tamil/" thisOTF = thisFont.fontname + ".otf" thisSystemFont = systemFontsDir + thisOTF thisFont.generate(thisOTF, flags=("round", "dummy-dsig", "PfEd-comments", "PfEd-colors", "PfEd-lookups", "PfEd-guidelines", "PfEd-background")) fontforge.logWarning(" Generated " + thisOTF) if os.path.exists(systemFontsDir): shutil.copy(thisOTF, thisSystemFont) if os.path.isfile(thisSystemFont): fontforge.logWarning(" Installed " + thisOTF) def genMast(junk, glyph): """Generate Masters""" myFont = fontforge.activeFont() geMaster(myFont) fontforge.logWarning("All done!") if fontforge.hasUserInterface(): keyShortcut = "Ctl+Shft+n" fontforge.registerMenuItem(genMast, None, None, ("Font", "Glyph"), keyShortcut, "Generate OTF from layers ") keyShortcut = "Ctl+Shft+p"
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import fontforge import os from os.path import exists from imp import reload def build_file_exists(bitbucket, font): build_file_name = font.fontname + "_build.py" return exists(build_file_name) def load_build(bitbucket, font): module_name = font.fontname + "_build" module = __import__(module_name) reload(module) module.build_glyphs(bitbucket, font) fontforge.registerMenuItem(load_build, build_file_exists, None, "Font", "None", "Build glyphs")
furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import fontforge #-------------------------------------------------------------------------- def clear_persistent(font): font.persistent = None def has_persistent(font): return font.persistent not in (None, {}) fontforge.registerMenuItem((lambda _, font: clear_persistent(font)), (lambda _, font: has_persistent(font)), None, 'Font', 'None', 'Clear persistent data') #--------------------------------------------------------------------------
def feature_file_exists(bitbucket, font): feature_file_name = font.fontname + "_main.fea" return exists(feature_file_name) def read_features(bitbucket, font): feature_file_name = font.fontname + "_main.fea" font.mergeFeature(feature_file_name) # merge_pair_positioning_subtables(font) # FIX/TODO: This seems to trigger a metrics view bug in FontForge. # # I need to investigate and report. Meanwhile, put the call in # # make-fonts.py, or just not bother. def erase_and_read_features(bitbucket, font): all_lookups = font.gpos_lookups + font.gsub_lookups for lookup in all_lookups: if font.getLookupInfo(lookup)[0] != "gpos_mark2base": font.removeLookup(lookup) read_features(bitbucket, font) font.buildOrReplaceAALTFeatures() fontforge.registerMenuItem( erase_and_read_features, feature_file_exists, None, "Font", "None", "Delete lookups and read _main feature file" ) # --------------------------------------------------------------------------
module_name = fontname + "_build" module = __import__(module_name) reload(module) module.build_glyphs(None, font) def load_build(font): load_build_by_fontname(font, font.fontname) def load_build_thoroughly(font): spacing_by_anchors.clear_cached_data(font) load_build(font) load_build(font) load_build(font) tools.reautohint(font) fontforge.registerMenuItem( (lambda _, font: load_build(font)), (lambda _, font: build_file_exists(font)), None, "Font", "None", "Build glyphs" ) fontforge.registerMenuItem( (lambda _, font: load_build_thoroughly(font)), (lambda _, font: build_file_exists(font)), None, "Font", "None", "Thoroughly build glyphs", )
def kern_cache_has_contents(font): return (font.temporary != None and 'kern-cache' in font.temporary and font.temporary['kern-cache'] != None and font.temporary['kern-cache'] != {}) def clear_cached_data(font): clear_kern_cache(font) font_db.db_remove(font) def cached_data_exists(font): return kern_cache_has_contents(font) or font_db.db_exists(font) fontforge.registerMenuItem((lambda _, glyph: set_spacing_anchors_read_only(glyph, True)), (lambda _, glyph: selected_spacing_anchors(glyph) != []), None, 'Glyph', 'None', 'Make spacing anchors kerning-only') fontforge.registerMenuItem((lambda _, glyph: set_spacing_anchors_read_only(glyph, False)), (lambda _, glyph: selected_spacing_anchors(glyph) != []), None, 'Glyph', 'None', 'Make spacing anchors non-kerning-only') fontforge.registerMenuItem((lambda _, glyph: flip_spacing_anchors_left_right(glyph)), (lambda _, glyph: selected_spacing_anchors(glyph) != []), None, 'Glyph', 'None', 'Flip spacing anchors left-right') fontforge.registerMenuItem(spread_spacing_anchors, glyph_has_spacing_anchors, 5, 'Glyph', 'None', 'Spread spacing anchors by 5')
# Returns false iff no glyph is selected # (needed for enabling in tools menu). @staticmethod def are_glyphs_selected(junk, font): font = fontforge.activeFont() for glyph in font.selection.byGlyphs: return True return False if __name__ == '__main__': if fontforge.hasUserInterface(): # Register the tools in the tools menu of FontForge: fontforge.registerMenuItem(Curvatura.modify_glyphs, Curvatura.are_glyphs_selected, "harmonize", "Font", None, "Curvatura", "Harmonize") fontforge.registerMenuItem(Curvatura.modify_glyphs, Curvatura.are_glyphs_selected, "harmonizehandles", "Font", None, "Curvatura", "Harmonize handles") fontforge.registerMenuItem(Curvatura.modify_glyphs, Curvatura.are_glyphs_selected, "tunnify", "Font", None, "Curvatura", "Tunnify (balance)") fontforge.registerMenuItem(Curvatura.modify_glyphs, Curvatura.are_glyphs_selected, "inflection", "Font", None, "Curvatura", "Add points of inflection") fontforge.registerMenuItem(Curvatura.modify_contours, None, "harmonize", "Glyph", None, "Curvatura",
def register_menu(): fontforge.registerMenuItem(export_sfd, None, None, ("Glyph", "Font"), None, "Export Glyph SFD...")
s += "|> close" return s #-------------------------------------------------------------------------- def load_glyph_data_from_program(glyph, program): module_name = 'program_generated_glyph_data' pipe = subprocess.Popen([program, glyph.glyphname], stdout=subprocess.PIPE).stdout module = imp.load_module(module_name, pipe, "<stdin>", ('', '', imp.PY_SOURCE)) def load_program_glyph_data(glyph): program = './' + glyph.font.fontname + '_glyph_update' load_glyph_data_from_program(glyph, program) fontforge.registerMenuItem((lambda _, glyph: load_program_glyph_data(glyph)), None, None, 'Glyph', 'None', 'Load glyph data from program') #-------------------------------------------------------------------------- def print_caml_contours(glyph): for c in glyph.layers[glyph.activeLayer]: print(caml_path(c)) fontforge.registerMenuItem((lambda _, glyph: print_caml_contours(glyph)), None, None, 'Glyph', 'None', 'Output OCaml contours') #--------------------------------------------------------------------------
outname = conf.get('main', 'ap') if os.path.exists(outname) : readAP(font, outname) mergeAPInfo(font) myFfFont = FfFont(font) buildGraphite(conf, None, myFfFont, outputfont) else : buildGraphite(conf, None, None, outputfont) writecfg(conf, cfg) os.chdir(cwd) def loadFont(font) : if getcfg(font) : if 'initScriptString' not in font.persistent : font.persistent['initScriptString'] = None if not font.temporary : font.temporary = {} font.temporary['generateFontPostHook'] = doGenerate if fontforge.hasUserInterface() : fontforge.hooks['loadFontHook'] = loadFont fontforge.registerMenuItem(loadConfig, None, None, "Font", None, "Graide", "Load configuration") fontforge.registerMenuItem(editConfig, None, None, "Font", None, "Graide", "Edit configuration") else: f = fontforge.open(os.path.abspath(sys.argv[1])) cfg = getcfg(f) if not cfg : print "No configuration, can't build" sys.exit(1) conf = RawConfigParser() conf.read(cfg) f.generate(conf.get('main', 'font'), flags = ('opentype',)) sys.exit(0)
svgTtxFileName = os.path.join(tmpDirName, font.fontname + '.S_V_G_.ttx') fil = open(svgTtxFileName, 'a') fil.write(SvgOutput) fil.close() #modify base ttx file tree = ET.parse(baseTtxFileName) root = tree.getroot() svgTtxOnlyFileName = font.fontname + '.S_V_G_.ttx' ET.SubElement(root, 'SVG', attrib={'src': svgTtxOnlyFileName}) tree.write(baseTtxFileName, encoding="UTF-8", xml_declaration=True, method="xml") #fontTools NEW color font newmTTFont = TTFont() newmTTFont.importXML(baseTtxFileName) colorFontFileName = os.path.join(orgDirName, font.fontname + 'Color.ttf') newmTTFont.save(colorFontFileName) # cleaning fontforge.open(orgSfdName) tempFont.close() shutil.rmtree(tmpDirName) window.layCounter = 0 fontforge.registerMenuItem(SVGinOTlayComp, None, None, "Font", None, "SVGinOpenType", "layersCompositor 0.21")