#Needs: fonts/Ambrosia.sfd #Test the fontforge module (but not its types) import sys, fontforge foo = fontforge.getPrefs("DetectDiagonalStems") fontforge.setPrefs("DetectDiagonalStems",~foo) fontforge.loadPrefs() # fontforge.savePrefs() fontforge.defaultOtherSubrs() # fontforge.readOtherSubrsFile() foo = fontforge.hasSpiro() # fontforge.loadEncodingFile() # fontforge.loadNamelist() # fontforge.loadNamelistDir() # fontforge.preloadCidmap() fontforge.printSetup("lpr") if (fontforge.unicodeFromName("A")!=65) or (fontforge.unicodeFromName("uni030D")!=0x30D): raise ValueError("Wrong return from unicodeFromName") foo = fontforge.version() ambrosia = sys.argv[1] fonts = fontforge.fonts() if ( len(fonts)!=0 ) : raise ValueError("Wrong return from fontforge.fonts")
def execute(tool, fn, argspec, chain = None): # Function to handle parameter parsing, font and file opening etc in command-line scripts # Supports opening (and saving) fonts using FontForge (FF), PysilFont UFO (UFO) or fontTools (FT) # Special handling for: # -d variation on -h to print extra info about defaults # -q quiet mode - suppresses progress messages and sets screen logging to errors only # -l opens log file and also creates a logger function to write to the log file # -p other parameters. Includes backup settings and loglevel/scrlevel settings for logger # for UFOlib scripts, also includes all outparams keys and ufometadata settings # infont and returnfont are used when chaining calls to execute together, passing ifont on without writing to disk params = chain["params"] if chain else parameters() logger = chain["logger"] if chain else params.logger # paramset has already created a basic logger argv = chain["argv"] if chain else sys.argv if tool == "FF": import fontforge if fontforge.hasUserInterface(): return # Execute is for command-line use fontforge.loadPrefs() fontforge.setPrefs("PreserveTables", "DSIG,Feat,Glat,Gloc,LTSH,Silf,Sill,Silt,VDMX,hdmx") ## Perhaps should be a parameter and check for existing values elif tool == "UFO": from silfont.ufo import Ufont elif tool == "FT": from fontTools import ttLib elif tool == "" or tool is None: tool = None else: logger.log("Invalid tool in call to execute()", "X") return basemodule = sys.modules[fn.__module__] poptions = {} poptions['prog'] = splitfn(argv[0])[1] poptions['description'] = basemodule.__doc__ poptions['formatter_class'] = argparse.RawDescriptionHelpFormatter epilog = "For more help see https://github.com/silnrsi/pysilfont/blob/master/docs/scripts.md#" + poptions['prog'] + "\n\n" poptions['epilog'] = epilog + "Version: " + params.sets['default']['version'] + "\n" + params.sets['default']['copyright'] parser = argparse.ArgumentParser(**poptions) parser._optionals.title = "other arguments" # Add standard arguments standardargs = [ ('-d', '--defaults', {'help': 'Display help with info on default values', 'action': 'store_true'}, {}), ('-q', '--quiet', {'help': 'Quiet mode - only display errors', 'action': 'store_true'}, {}), ('-l', '--log', {'help': 'Log file'}, {'type': 'outfile'}), ('-p', '--params', {'help': 'Other parameters', 'action': 'append'}, {'type': 'optiondict'})] standardargsindex = ['defaults', 'quiet', 'log', 'params'] suppliedargs = [] for a in argspec: argn = a[:-2][-1] # [:-2] will give either 1 or 2, the last of which is the full argument name if argn[0:2] == "--": argn = argn[2:] # Will start with -- for options suppliedargs.append(argn) for i, arg in enumerate(standardargsindex): if arg not in suppliedargs: argspec.append(standardargs[i]) # Special handling for "-d" to print default value info with help text defhelp = False if "-d" in argv: defhelp = True pos = argv.index("-d") argv[pos] = "-h" # Set back to -h for argparse to recognise deffiles = [] defother = [] quiet = True if "-q" in argv else False if quiet: logger.scrlevel = "E" # Process the supplied argument specs, add args to parser, store other info in arginfo arginfo = [] logdef = None for a in argspec: # Process all but last tuple entry as argparse arguments nonkwds = a[:-2] kwds = a[-2] parser.add_argument(*nonkwds, **kwds) # Create dict of framework keywords using argument name argn = nonkwds[-1] # Find the argument name from first 1 or 2 tuple entries if argn[0:2] == "--": argn = argn[2:] # Will start with -- for options ainfo=a[-1] ainfo['name']=argn if argn == 'log': logdef = ainfo['def'] if 'def' in ainfo else None arginfo.append(ainfo) if defhelp: arg = nonkwds[0] if 'def' in ainfo: deffiles.append([arg, ainfo['def']]) elif 'default' in kwds: defother.append([arg, kwds['default']]) # if -d specified, change the help epilog to info about argument defaults if defhelp: if not (deffiles or defother): deftext = "No defaults for parameters/options" else: deftext = "Defaults for parameters/options - see user docs for details\n" if deffiles: deftext = deftext + "\n Font/file names\n" for (param, defv) in deffiles: deftext = deftext + ' {:<20}{}\n'.format(param, defv) if defother: deftext = deftext + "\n Other parameters\n" for (param, defv) in defother: deftext = deftext + ' {:<20}{}\n'.format(param, defv) parser.epilog = deftext + "\n\n" + parser.epilog # Parse the command-line arguments. If errors or -h used, procedure will exit here args = parser.parse_args(argv[1:]) # Process the first positional parameter to get defaults for file names fppval = getattr(args, arginfo[0]['name']) if fppval is None: fppval = "" # For scripts that can be run with no positional parameters (fppath, fpbase, fpext) = splitfn(fppval) # First pos param use for defaulting # Process parameters if chain: execparams = params.sets["main"] args.params = {} # clparams not used when chaining else: # Read config file from disk if it exists configname = os.path.join(fppath, "pysilfont.cfg") if os.path.exists(configname): params.addset("config file", configname, configfile=configname) else: params.addset("config file") # Create empty set if not quiet and "scrlevel" in params.sets["config file"]: logger.scrlevel = params.sets["config file"]["scrlevel"] # Process command-line parameters clparams = {} if 'params' in args.__dict__: if args.params is not None: for param in args.params: x = param.split("=", 1) if len(x) != 2: logger.log("params must be of the form 'param=value'", "S") if x[1] == "\\t": x[1] = "\t" # Special handling for tab characters clparams[x[0]] = x[1] args.params = clparams params.addset("command line", "command line", inputdict=clparams) if not quiet and "scrlevel" in params.sets["command line"]: logger.scrlevel = params.sets["command line"]["scrlevel"] # Create main set of parameters based on defaults then update with config file values and command line values params.addset("main", copyset="default") params.sets["main"].updatewith("config file") params.sets["main"].updatewith("command line") execparams = params.sets["main"] # Set up logging if chain: setattr(args, 'logger', logger) args.logfile = logger.logfile else: logfile = None if 'log' in args.__dict__: logname = args.log if args.log else "" if logdef is not None: (path, base, ext) = splitfn(logname) (dpath, dbase, dext) = splitfn(logdef) if not path: if base and ext: # If both specified then use cwd, ie no path path = "" else: path = (fppath if dpath is "" else os.path.join(fppath, dpath)) if not base: if dbase == "": base = fpbase elif dbase[0] == "_": # Append to font name if starts with _ base = fpbase + dbase else: base = dbase if not ext and dext: ext = dext logname = os.path.join(path, base+ext) if logname == "": logfile = None else: (logname, logpath, exists) = fullpath(logname) if not exists: logger.log("Log file directory " + logpath + " does not exist", "S") if not quiet: logger.log('Opening log file for output: ' + logname, "P") try: logfile = open(logname, "w") except Exception as e: print e sys.exit(1) args.log = logfile # Set up logger details logger.loglevel = execparams['loglevel'].upper() if not quiet: logger.scrlevel = execparams['scrlevel'].upper() logger.logfile = logfile setattr(args, 'logger', logger) # Process the argument values returned from argparse outfont = None infontlist = [] for c, ainfo in enumerate(arginfo): aval = getattr(args, ainfo['name']) if ainfo['name'] in ('params', 'log'): continue # params and log already processed atype = None adef = None if 'type' in ainfo: atype = ainfo['type'] if atype not in ('infont', 'outfont', 'infile', 'outfile', 'incsv', 'filename', 'optiondict'): logger.log("Invalid type of " + atype + " supplied in argspec", "X") if atype != 'optiondict': # All other types are file types, so adef must be set, even if just to "" adef = ainfo['def'] if 'def' in ainfo else "" if adef is None and aval is None: # If def explicitly set to None then this is optional setattr(args, ainfo['name'], None) continue if c == 0: if aval is None : logger.log("Invalid first positional parameter spec", "X") if aval[-1] in ("\\","/"): aval = aval[0:-1] # Remove trailing slashes else: #Handle defaults for all but first positional parameter if adef is not None: if not aval: aval = "" if aval == "" and adef == "": # Only valid for output font parameter if atype != "outfont": logger.log("No value suppiled for " + ainfo['name'], "S") ## Not sure why this needs to fail - we need to cope with other optional file or filename parameters (apath, abase, aext) = splitfn(aval) (dpath, dbase, dext) = splitfn(adef) # dpath should be None if not apath: if abase and aext: # If both specified then use cwd, ie no path apath = "" else: apath = fppath if not abase: if dbase == "": abase = fpbase elif dbase[0] == "_": # Append to font name if starts with _ abase = fpbase + dbase else: abase = dbase if not aext: if dext: aext = dext elif (atype == 'outfont' or atype == 'infont'): aext = fpext aval = os.path.join(apath, abase+aext) # Open files/fonts if atype == 'infont': if tool is None: logger.log("Can't specify a font without a font tool", "X") infontlist.append((ainfo['name'], aval)) # Build list of fonts to open when other args processed elif atype == 'infile': if not quiet: logger.log('Opening file for input: '+aval, "P") try: aval = open(aval, "r") except Exception as e: print e sys.exit(1) elif atype == 'incsv': if not quiet: logger.log('Opening file for input: '+aval, "P") aval = csvreader(aval) elif atype == 'outfile': (aval, path, exists) = fullpath(aval) if not exists: logger.log("Output file directory " + path + " does not exist", "S") if not quiet: logger.log('Opening file for output: ' + aval, "P") try: aval = codecs.open(aval, 'w', 'utf-8') except Exception as e: print e sys.exit(1) elif atype == 'outfont': if tool is None: logger.log("Can't specify a font without a font tool", "X") outfont = aval outfontpath = apath outfontbase = abase outfontext = aext elif atype == 'optiondict': # Turn multiple options in the form ['opt1=a', 'opt2=b'] into a dictionary avaldict={} if aval is not None: for option in aval: x = option.split("=", 1) if len(x) != 2: logger.log("options must be of the form 'param=value'", "S") if x[1] == "\\t": x[1] = "\t" # Special handling for tab characters avaldict[x[0]] = x[1] aval = avaldict setattr(args, ainfo['name'], aval) # Open fonts - needs to be done after processing other arguments so logger and params are defined for name, aval in infontlist: if chain and name == 'ifont': aval = chain["font"] else: if tool == "FF" : aval = fontforge.open(aval) if tool == "UFO": aval = Ufont(aval, params=params) if tool == "FT" : aval = ttLib.TTFont(aval) setattr(args, name, aval) # Assign the font object to args attribute # All arguments processed, now call the main function setattr(args, "paramsobj", params) setattr(args, "cmdlineargs", argv) newfont = fn(args) # If an output font is expected and one is returned, output the font if outfont and newfont is not None: if chain: # return font to be handled by chain() return newfont else: # Backup the font if output is overwriting original input font if outfont == infontlist[0][1]: backupdir = os.path.join(outfontpath, execparams['backupdir']) backupmax = int(execparams['backupkeep']) backup = str2bool(execparams['backup']) if backup: if not os.path.isdir(backupdir): # Create backup directory if not present try: os.mkdir(backupdir) except Exception as e: print e sys.exit(1) backupbase = os.path.join(backupdir, outfontbase+outfontext) # Work out backup name based on existing backups nums = sorted([int(i[len(backupbase)+1-len(i):-1]) for i in glob(backupbase+".*~")]) # Extract list of backup numbers from existing backups newnum = max(nums)+1 if nums else 1 backupname = backupbase+"."+str(newnum)+"~" # Backup the font newfont.logger.log("Backing up input font to "+backupname, "P") shutil.copytree(outfont, backupname) # Purge old backups for i in range(0, len(nums) - backupmax + 1): backupname = backupbase+"."+str(nums[i])+"~" newfont.logger.log("Purging old backup "+backupname, "I") shutil.rmtree(backupname) else: newfont.logger.log("No font backup done due to backup parameter setting", "W") # Output the font if tool == "FF": if not quiet: logger.log("Saving font to " + outfont, "P") if outfontext.lower() == ".ufo" or outfontext.lower() == '.ttf': newfont.generate(outfont) else: newfont.save(outfont) elif tool == "FT": if not quiet: logger.log("Saving font to " + outfont, "P") newfont.save(outfont) else: # Must be Pyslifont Ufont newfont.write(outfont) if logfile: logfile.close()
def execute(tool, fn, argspec, chain = None): # Function to handle parameter parsing, font and file opening etc in command-line scripts # Supports opening (and saving) fonts using FontForge (FF), PysilFont UFO (UFO) or fontTools (FT) # Special handling for: # -d variation on -h to print extra info about defaults # -q quiet mode - only output a single line with count of errors (if there are any) # -l opens log file and also creates a logger function to write to the log file # -p other parameters. Includes backup settings and loglevel/scrlevel settings for logger # for UFOlib scripts, also includes all outparams keys and ufometadata settings chainfirst = False if chain == "first": # If first call to execute has this set, only do the final return part of chaining chainfirst = True chain = None params = chain["params"] if chain else parameters() logger = chain["logger"] if chain else params.logger # paramset has already created a basic logger argv = chain["argv"] if chain else sys.argv if tool == "FF": import fontforge if fontforge.hasUserInterface(): return # Execute is for command-line use fontforge.loadPrefs() fontforge.setPrefs("PreserveTables", "DSIG,Feat,Glat,Gloc,LTSH,Silf,Sill,Silt,VDMX,hdmx") ## Perhaps should be a parameter and check for existing values elif tool == "UFO": from silfont.ufo import Ufont elif tool == "FT": from fontTools import ttLib elif tool == "" or tool is None: tool = None else: logger.log("Invalid tool in call to execute()", "X") return basemodule = sys.modules[fn.__module__] poptions = {} poptions['prog'] = splitfn(argv[0])[1] poptions['description'] = basemodule.__doc__ poptions['formatter_class'] = argparse.RawDescriptionHelpFormatter epilog = "For more help see https://github.com/silnrsi/pysilfont/blob/master/docs/scripts.md#" + poptions['prog'] + "\n\n" poptions['epilog'] = epilog + "Version: " + params.sets['default']['version'] + "\n" + params.sets['default']['copyright'] parser = argparse.ArgumentParser(**poptions) parser._optionals.title = "other arguments" # Add standard arguments standardargs = [ ('-d', '--defaults', {'help': 'Display help with info on default values', 'action': 'store_true'}, {}), ('-q', '--quiet', {'help': 'Quiet mode - only display errors', 'action': 'store_true'}, {}), ('-l', '--log', {'help': 'Log file'}, {'type': 'outfile'}), ('-p', '--params', {'help': 'Other parameters - see parameters.md for details', 'action': 'append'}, {'type': 'optiondict'})] standardargsindex = ['defaults', 'quiet', 'log', 'params'] suppliedargs = [] for a in argspec: argn = a[:-2][-1] # [:-2] will give either 1 or 2, the last of which is the full argument name if argn[0:2] == "--": argn = argn[2:] # Will start with -- for options suppliedargs.append(argn) for i, arg in enumerate(standardargsindex): if arg not in suppliedargs: argspec.append(standardargs[i]) # Special handling for "-d" to print default value info with help text defhelp = False if "-d" in argv: defhelp = True pos = argv.index("-d") argv[pos] = "-h" # Set back to -h for argparse to recognise deffiles = [] defother = [] quiet = True if "-q" in argv else False if quiet: logger.scrlevel = "S" # Process the supplied argument specs, add args to parser, store other info in arginfo arginfo = [] logdef = None for a in argspec: # Process all but last tuple entry as argparse arguments nonkwds = a[:-2] kwds = a[-2] parser.add_argument(*nonkwds, **kwds) # Create dict of framework keywords using argument name argn = nonkwds[-1] # Find the argument name from first 1 or 2 tuple entries if argn[0:2] == "--": argn = argn[2:] # Will start with -- for options ainfo=a[-1] ainfo['name']=argn if argn == 'log': logdef = ainfo['def'] if 'def' in ainfo else None arginfo.append(ainfo) if defhelp: arg = nonkwds[0] if 'def' in ainfo: deffiles.append([arg, ainfo['def']]) elif 'default' in kwds: defother.append([arg, kwds['default']]) # if -d specified, change the help epilog to info about argument defaults if defhelp: if not (deffiles or defother): deftext = "No defaults for parameters/options" else: deftext = "Defaults for parameters/options - see user docs for details\n" if deffiles: deftext = deftext + "\n Font/file names\n" for (param, defv) in deffiles: deftext = deftext + ' {:<20}{}\n'.format(param, defv) if defother: deftext = deftext + "\n Other parameters\n" for (param, defv) in defother: deftext = deftext + ' {:<20}{}\n'.format(param, defv) parser.epilog = deftext + "\n\n" + parser.epilog # Parse the command-line arguments. If errors or -h used, procedure will exit here args = parser.parse_args(argv[1:]) # Process the first positional parameter to get defaults for file names fppval = getattr(args, arginfo[0]['name']) if fppval is None: fppval = "" # For scripts that can be run with no positional parameters (fppath, fpbase, fpext) = splitfn(fppval) # First pos param use for defaulting # Process parameters if chain: execparams = params.sets["main"] args.params = {} # clparams not used when chaining else: # Read config file from disk if it exists configname = os.path.join(fppath, "pysilfont.cfg") if os.path.exists(configname): params.addset("config file", configname, configfile=configname) else: params.addset("config file") # Create empty set if not quiet and "scrlevel" in params.sets["config file"]: logger.scrlevel = params.sets["config file"]["scrlevel"] # Process command-line parameters clparams = {} if 'params' in args.__dict__: if args.params is not None: for param in args.params: x = param.split("=", 1) if len(x) != 2: logger.log("params must be of the form 'param=value'", "S") if x[1] == "\\t": x[1] = "\t" # Special handling for tab characters clparams[x[0]] = x[1] args.params = clparams params.addset("command line", "command line", inputdict=clparams) if not quiet and "scrlevel" in params.sets["command line"]: logger.scrlevel = params.sets["command line"]["scrlevel"] # Create main set of parameters based on defaults then update with config file values and command line values params.addset("main", copyset="default") params.sets["main"].updatewith("config file") params.sets["main"].updatewith("command line") execparams = params.sets["main"] # Set up logging if chain: setattr(args, 'logger', logger) args.logfile = logger.logfile else: logfile = None logname = args.log if 'log' in args.__dict__ and args.log is not None else "" if 'log' in args.__dict__: if logdef is not None: (path, base, ext) = splitfn(logname) (dpath, dbase, dext) = splitfn(logdef) if not path: if base and ext: # If both specified then use cwd, ie no path path = "" else: path = (fppath if dpath is "" else os.path.join(fppath, dpath)) if not base: if dbase == "": base = fpbase elif dbase[0] == "_": # Append to font name if starts with _ base = fpbase + dbase else: base = dbase if not ext and dext: ext = dext logname = os.path.join(path, base+ext) if logname == "": logfile = None else: (logname, logpath, exists) = fullpath(logname) if not exists: logger.log("Log file directory " + logpath + " does not exist", "S") logger.log('Opening log file for output: ' + logname, "P") try: logfile = io.open(logname, "w", encoding="utf-8") except Exception as e: print(e) sys.exit(1) args.log = logfile # Set up logger details logger.loglevel = execparams['loglevel'].upper() if not quiet: logger.scrlevel = execparams['scrlevel'].upper() logger.logfile = logfile setattr(args, 'logger', logger) # Process the argument values returned from argparse outfont = None infontlist = [] for c, ainfo in enumerate(arginfo): aval = getattr(args, ainfo['name']) if ainfo['name'] in ('params', 'log'): continue # params and log already processed atype = None adef = None if 'type' in ainfo: atype = ainfo['type'] if atype not in ('infont', 'outfont', 'infile', 'outfile', 'incsv', 'filename', 'optiondict'): logger.log("Invalid type of " + atype + " supplied in argspec", "X") if atype != 'optiondict': # All other types are file types, so adef must be set, even if just to "" adef = ainfo['def'] if 'def' in ainfo else "" if adef is None and aval is None: # If def explicitly set to None then this is optional setattr(args, ainfo['name'], None) continue if c == 0: if aval is None : logger.log("Invalid first positional parameter spec", "X") if aval[-1] in ("\\","/"): aval = aval[0:-1] # Remove trailing slashes else: #Handle defaults for all but first positional parameter if adef is not None: if not aval: aval = "" if aval == "" and adef == "": # Only valid for output font parameter if atype != "outfont": logger.log("No value suppiled for " + ainfo['name'], "S") ## Not sure why this needs to fail - we need to cope with other optional file or filename parameters (apath, abase, aext) = splitfn(aval) (dpath, dbase, dext) = splitfn(adef) # dpath should be None if not apath: if abase and aext: # If both specified then use cwd, ie no path apath = "" else: apath = fppath if not abase: if dbase == "": abase = fpbase elif dbase[0] == "_": # Append to font name if starts with _ abase = fpbase + dbase else: abase = dbase if not aext: if dext: aext = dext elif (atype == 'outfont' or atype == 'infont'): aext = fpext aval = os.path.join(apath, abase+aext) # Open files/fonts if atype == 'infont': if tool is None: logger.log("Can't specify a font without a font tool", "X") infontlist.append((ainfo['name'], aval)) # Build list of fonts to open when other args processed elif atype == 'infile': logger.log('Opening file for input: '+aval, "P") try: aval = io.open(aval, "r", encoding="utf-8") except Exception as e: print(e) sys.exit(1) elif atype == 'incsv': logger.log('Opening file for input: '+aval, "P") aval = csvreader(aval) elif atype == 'outfile': (aval, path, exists) = fullpath(aval) if not exists: logger.log("Output file directory " + path + " does not exist", "S") logger.log('Opening file for output: ' + aval, "P") try: aval = io.open(aval, 'w', encoding="utf-8") except Exception as e: print(e) sys.exit(1) elif atype == 'outfont': if tool is None: logger.log("Can't specify a font without a font tool", "X") outfont = aval outfontpath = apath outfontbase = abase outfontext = aext elif atype == 'optiondict': # Turn multiple options in the form ['opt1=a', 'opt2=b'] into a dictionary avaldict={} if aval is not None: for option in aval: x = option.split("=", 1) if len(x) != 2: logger.log("options must be of the form 'param=value'", "S") if x[1] == "\\t": x[1] = "\t" # Special handling for tab characters avaldict[x[0]] = x[1] aval = avaldict setattr(args, ainfo['name'], aval) # Open fonts - needs to be done after processing other arguments so logger and params are defined for name, aval in infontlist: if chain and name == 'ifont': aval = chain["font"] else: if tool == "FF" : aval = fontforge.open(aval) if tool == "UFO": aval = Ufont(aval, params=params) if tool == "FT" : aval = ttLib.TTFont(aval) setattr(args, name, aval) # Assign the font object to args attribute # All arguments processed, now call the main function setattr(args, "paramsobj", params) setattr(args, "cmdlineargs", argv) newfont = fn(args) # If an output font is expected and one is returned, output the font if chainfirst: chain = True # Special handling for first call of chaining if newfont: if chain: # return font to be handled by chain() return (args, newfont) else: if outfont: # Backup the font if output is overwriting original input font if outfont == infontlist[0][1]: backupdir = os.path.join(outfontpath, execparams['backupdir']) backupmax = int(execparams['backupkeep']) backup = str2bool(execparams['backup']) if backup: if not os.path.isdir(backupdir): # Create backup directory if not present try: os.mkdir(backupdir) except Exception as e: print(e) sys.exit(1) backupbase = os.path.join(backupdir, outfontbase+outfontext) # Work out backup name based on existing backups nums = sorted([int(i[len(backupbase)+1-len(i):-1]) for i in glob(backupbase+".*~")]) # Extract list of backup numbers from existing backups newnum = max(nums)+1 if nums else 1 backupname = backupbase+"."+str(newnum)+"~" # Backup the font newfont.logger.log("Backing up input font to "+backupname, "P") shutil.copytree(outfont, backupname) # Purge old backups for i in range(0, len(nums) - backupmax + 1): backupname = backupbase+"."+str(nums[i])+"~" newfont.logger.log("Purging old backup "+backupname, "I") shutil.rmtree(backupname) else: newfont.logger.log("No font backup done due to backup parameter setting", "W") # Output the font if tool == "FF": logger.log("Saving font to " + outfont, "P") if outfontext.lower() == ".ufo" or outfontext.lower() == '.ttf': newfont.generate(outfont) else: newfont.save(outfont) elif tool == "FT": logger.log("Saving font to " + outfont, "P") newfont.save(outfont) else: # Must be Pyslifont Ufont newfont.write(outfont) else: logger.log("Font returned to execute() but no output font is specified in arg spec", "X") elif chain: # ) When chaining return just args - the font can be accessed by args.ifont return (args, None) # ) assuming that the script has not changed the input font if logger.errorcount or logger.warningcount: message = "Command completed with " + str(logger.errorcount) + " errors and " + str(logger.warningcount) + " warnings" if logger.scrlevel in ("S", "E") and logname is not "": if logger.scrlevel == "S" or logger.warningcount: message = message + " - see " + logname if logger.errorcount: if quiet: logger.raisescrlevel("E") logger.log(message, "E") logger.resetscrlevel() else: logger.log(message, "P") if logger.scrlevel == "P" and logger.warningcount: logger.log("See log file for warning messages or rerun with '-p scrlevel=w'", "P") else: logger.log("Command completed with no warnings", "P") return (args, newfont)
def execute(tool, fn, argspec) : # Function to handle parameter parsing, font and file opening etc in command-line scripts # Supports opening (and saving) fonts using FontForge (FF) or PysilFont UFOlib (PSFU) # Special handling for: # -d variation on -h to print extra info about defaults # -q quiet mode - suppresses progress messages and sets screen logging to errors only # -l opens log file and also creates a logger function to write to the log file # -p includes loglevel and scrlevel settings for logger # for UFOlib scripts, also includes all font.outparams keys except for attribOrder # -v for UFOlib scripts this sets font.outparams(UFOversion) logger = loggerobj() # Basic screen logger at this stage ff = False psfu = False if tool == "FF" : ff=True import fontforge if fontforge.hasUserInterface() : return # Execute is for command-line use fontforge.loadPrefs() elif tool == "PSFU" : psfu = True from UFOlib import Ufont elif tool == "" or tool is None : tool = None else : logger.log( "Invalid tool in call to execute()", "X") return basemodule = sys.modules[fn.__module__] poptions = {} poptions['prog'] = _splitfn(sys.argv[0])[1] poptions['description'] = basemodule.__doc__ poptions['formatter_class'] = argparse.RawDescriptionHelpFormatter if hasattr(basemodule, '__version__') : poptions['epilog'] = "Version: " + basemodule.__version__ parser = argparse.ArgumentParser(**poptions) # Add standard arguments standardargs = [ ('-d','--defaults', {'help': 'Display help with info on default values', 'action': 'store_true'}, {}), ('-q','--quiet',{'help': 'Quiet mode - only display errors', 'action': 'store_true'}, {})] standardargsindex = ['defaults','quiet'] if psfu: standardargs.extend([ ('-v','--version',{'help': 'UFO version to output'},{}), ('-p','--params',{'help': 'Other font parameters','action': 'append'}, {'type': 'optiondict'})]) standardargsindex.extend(['version','params']) suppliedargs = [] for a in argspec : argn = a[:-2][-1] # [:-2] will give either 1 or 2, the last of which is the full argument name if argn[0:2] == "--" : argn = argn[2:] # Will start with -- for options suppliedargs.append(argn) for i,arg in enumerate(standardargsindex) : if arg not in suppliedargs: argspec.append(standardargs[i]) # Special handling for "-d" to print default value info with help text defhelp = False if "-d" in sys.argv: defhelp = True pos = sys.argv.index("-d") sys.argv[pos] = "-h" # Set back to -h for argparse to recognise deffiles=[] defother=[] quiet = True if "-q" in sys.argv else False # Process the supplied argument specs, add args to parser, store other info in arginfo arginfo = [] logdef = None for a in argspec : # Process all but last tuple entry as argparse arguments nonkwds = a[:-2] kwds = a[-2] parser.add_argument(*nonkwds, **kwds) # Create dict of framework keywords using argument name argn = nonkwds[-1] # Find the argument name from first 1 or 2 tuple entries if argn[0:2] == "--" : argn = argn[2:] # Will start with -- for options ainfo=a[-1] ainfo['name']=argn if argn == 'log' : logdef = ainfo['def'] if 'def' in ainfo else None arginfo.append(ainfo) if defhelp: arg = nonkwds[0] if 'def' in ainfo: deffiles.append([arg,ainfo['def']]) elif 'default' in kwds: defother.append([arg,kwds['default']]) # if -d specified, change the help epilog to info about argument defaults if defhelp: if not (deffiles or defother): deftext = "No defaults for parameters/options" else: deftext = "Defaults for parameters/options - see user docs for details\n" if deffiles: deftext = deftext + "\n Font/file names\n" for (param,defv) in deffiles: deftext = deftext + ' {:<20}{}\n'.format(param,defv) if defother: deftext = deftext + "\n Other parameters\n" for (param,defv) in defother: deftext = deftext + ' {:<20}{}\n'.format(param,defv) parser.epilog = deftext # Parse the command-line arguments. If errors or -h used, procedure will exit here args = parser.parse_args() # Process the first positional parameter to get defaults for file names fppval = getattr(args,arginfo[0]['name']) if fppval is None : fppval = "" # For scripts that can be run with no positional parameters (fppath,fpbase,fpext)=_splitfn(fppval) # First pos param use for defaulting # Process command-line parameters and config file clparams = {} if 'params' in args.__dict__ : if args.params is not None : for param in args.params : x = param.split("=",1) if len(x) <> 2 : logger.log( "params must be of the form 'param=value'", "S") if x[1] == "\\t" : x[1] = "\t" # Special handling for tab characters clparams[x[0]] = x[1] if psfu and 'version' in args.__dict__: if args.version : clparams["UFOversion"] = args.version args.params = clparams # Read config file from disk if it exists cfgparams = {} configname = os.path.join(fppath, "pysilfont.cfg") if os.path.exists(configname) : config = csvreader(configname, logger = logger, numfields = 2) for param,value in config : cfgparams[param] = value # Create list of parameters for use with logging and backup from base parameters overdidden by any congif file or command line parameters lbparams={} for param in baseparamsindex : lbparams[param] = cfgparams[param] if param in cfgparams else baseparamsindex[param]['value'] if param in clparams : lbparams[param] = clparams[param] # Set up logging logfile = None if 'log' in args.__dict__ : logname = args.log if args.log else "" if logdef is not None : (path,base,ext)=_splitfn(logname) (dpath,dbase,dext)=_splitfn(logdef) # dpath should be None if not path : if base and ext : # If both specified then use cwd, ie no path path = "" else: path=fppath if not base : if dbase == "" : base = fpbase elif dbase[0] == "_" : # Append to font name if starts with _ base = fpbase + dbase else: base = dbase if not ext and dext : ext = dext logname = os.path.join(path,base+ext) if not quiet : logger.log( 'Opening log file for output: '+logname, "P") try : logfile=open(logname,"w") except Exception as e : print e sys.exit(1) args.log = logfile # Set up logger details logger.loglevel = lbparams['loglevel'].upper() logger.scrlevel = "E" if quiet else lbparams['scrlevel'].upper() logger.logfile = logfile setattr(args,'logger',logger) # Process the argument values returned from argparse outfont = None infontlist = [] for c,ainfo in enumerate(arginfo) : aval = getattr(args,ainfo['name']) if ainfo['name'] in ('params', 'log') : continue # params and log already processed atype = None adef = None if 'type' in ainfo : atype = ainfo['type'] if atype <> 'optiondict' : # All other types are file types, so adef must be set, even if just to "" adef = ainfo['def'] if 'def' in ainfo else "" if c == 0 : if aval[-1] in ("\\","/") : aval = aval[0:-1] # Remove trailing slashes else : #Handle defaults for all but first positional parameter if adef is not None: if not aval : aval="" if aval == "" and adef == "" : # Only valid for output font parameter if atype <> "outfont" : logger.log( "No value suppiled for " + ainfo['name'], "S") sysexit() (apath,abase,aext)=_splitfn(aval) (dpath,dbase,dext)=_splitfn(adef) # dpath should be None if not apath : if abase and aext : # If both specified then use cwd, ie no path apath = "" else: apath=fppath if not abase : if dbase == "" : abase = fpbase elif dbase[0] == "_" : # Append to font name if starts with _ abase = fpbase + dbase else: abase = dbase if not aext : if dext : aext = dext elif (atype=='outfont' or atype=='infont') : aext = fpext aval = os.path.join(apath,abase+aext) # Open files/fonts if atype=='infont' : if tool is None: logger.log( "Can't specify a font without a font tool", "X") infontlist.append((ainfo['name'],aval)) # Build list of fonts to open when other args processed elif atype=='infile' : if not quiet : logger.log( 'Opening file for input: '+aval, "P") try : aval=open(aval,"r") except Exception as e : print e sys.exit(1) elif atype=='incsv' : if not quiet : logger.log( 'Opening file for input: '+aval, "P") aval = csvreader(aval) elif atype=='outfile': if not quiet : logger.log( 'Opening file for output: '+aval, "P") try : aval=open(aval,"w") except Exception as e : print e sys.exit(1) elif atype=='outfont' : if tool is None: logger.log("Can't specify a font without a font tool", "X") outfont = aval outfontpath = apath outfontbase = abase outfontext = aext elif atype=='optiondict' : # Turn multiple options in the form ['opt1=a','opt2=b'] into a dictionary avaldict={} if aval is not None: for option in aval: x = option.split("=",1) if len(x) <> 2 : logger.log( "params must be of the form 'param=value'", "S") if x[1] == "\\t" : x[1] = "\t" # Special handling for tab characters avaldict[x[0]] = x[1] aval = avaldict setattr(args,ainfo['name'],aval) # Open fonts - needs to be done after processing other arguments so logger and params are defined for name,aval in infontlist : if ff : aval=fontforge.open(aval) if psfu: aval=Ufont(aval, logger = logger, cfgparams=cfgparams, clparams=clparams) setattr(args,name,aval) # Assign the font object to args attribute # All arguments processed, now call the main function newfont = fn(args) # If an output font is expected and one is returned, output the font if outfont and newfont is not None: # Backup the font if output is overwriting original input font if outfont == infontlist[0][1] : backupdir = os.path.join(outfontpath,lbparams['backupdir']) backupmax = int(lbparams['backupkeep']) backup = str2bool(lbparams['backup']) if backup : if not os.path.isdir(backupdir) : # Create backup directory if not present try: os.mkdir(backupdir) except Exception as e : print e sys.exit(1) backupbase = os.path.join(backupdir,outfontbase+outfontext) # Work out backup name based on existing backups nums = sorted([ int(i[len(backupbase)+1-len(i):-1]) for i in glob(backupbase+".*~")]) # Extract list of backup numbers from existing backups newnum = max(nums)+1 if nums else 1 backupname = backupbase+"."+str(newnum)+"~" # Backup the font newfont.logger.log("Backing up input font to "+backupname,"P") shutil.copytree(outfont,backupname) # Purge old backups for i in range(0, len(nums) - backupmax + 1) : backupname = backupbase+"."+str(nums[i])+"~" newfont.logger.log("Purging old backup "+backupname,"I") shutil.rmtree(backupname) else: newfont.logger.log("No font backup done due to backup parameter setting","W") # Output the font if ff: if not quiet : logger.log( "Saving font to " + outfont, "P") if outfontext.lower() == ".ufo" or outfontext.lower() == '.ttf': newfont.generate(outfont) else : newfont.save(outfont) else: # Must be Pyslifont Ufont newfont.write(outfont) if logfile : logfile.close()
#!/home/rakesh/pdf2htmlEX/fontforge-pdf2htmlEX/fontforgeexe/fontforge #Needs: fonts/Ambrosia.sfd #Test the fontforge module (but not its types) import os, fontforge; foo = fontforge.getPrefs("DetectDiagonalStems") fontforge.setPrefs("DetectDiagonalStems",~foo) fontforge.loadPrefs(); # fontforge.savePrefs() fontforge.defaultOtherSubrs(); # fontforge.readOtherSubrsFile(); foo = fontforge.hasSpiro() # fontforge.loadEncodingFile() # fontforge.loadNamelist() # fontforge.loadNamelistDir() # fontforge.loadPlugin() # fontforge.loadPluginDir() # fontforge.preloadCidmap() fontforge.printSetup("lpr") if ( (fontforge.unicodeFromName("A")!=65) | (fontforge.unicodeFromName("uni030D")!=0x30D) ) : raise ValueError, "Wrong return from unicodeFromName" foo = fontforge.version() ambrosia = os.path.join("/home/rakesh/pdf2htmlEX/fontforge-pdf2htmlEX/tests", "fonts", "Ambrosia.sfd")
def __init__(self): fontforge.loadPrefs() pth = os.path.expanduser('~/Library/Fonts/') self.font=fontforge.open(pth+'aaux_pro_bd_it_osf.ttf') self.layers = defaultdict(list)
#!/usr/local/bin/fontforge #Needs: fonts/Ambrosia.sfd #Test the fontforge module (but not its types) import fontforge; foo = fontforge.getPrefs("DetectDiagonalStems") fontforge.setPrefs("DetectDiagonalStems",~foo) fontforge.loadPrefs(); # fontforge.savePrefs() fontforge.defaultOtherSubrs(); # fontforge.readOtherSubrsFile(); foo = fontforge.hasSpiro() # fontforge.loadEncodingFile() # fontforge.loadNamelist() # fontforge.loadNamelistDir() # fontforge.loadPlugin() # fontforge.loadPluginDir() # fontforge.preloadCidmap() fontforge.printSetup("lpr") if ( (fontforge.unicodeFromName("A")!=65) | (fontforge.unicodeFromName("uni030D")!=0x30D) ) : raise ValueError, "Wrong return from unicodeFromName" foo = fontforge.version() fonts = fontforge.fonts() if ( len(fonts)!=0 ) :