1*e1fe3e4aSElliott Hughesimport os 2*e1fe3e4aSElliott Hughesimport argparse 3*e1fe3e4aSElliott Hughesimport logging 4*e1fe3e4aSElliott Hughesimport shutil 5*e1fe3e4aSElliott Hughesimport multiprocessing as mp 6*e1fe3e4aSElliott Hughesfrom contextlib import closing 7*e1fe3e4aSElliott Hughesfrom functools import partial 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott Hughesimport fontTools 10*e1fe3e4aSElliott Hughesfrom .ufo import font_to_quadratic, fonts_to_quadratic 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughesufo_module = None 13*e1fe3e4aSElliott Hughestry: 14*e1fe3e4aSElliott Hughes import ufoLib2 as ufo_module 15*e1fe3e4aSElliott Hughesexcept ImportError: 16*e1fe3e4aSElliott Hughes try: 17*e1fe3e4aSElliott Hughes import defcon as ufo_module 18*e1fe3e4aSElliott Hughes except ImportError as e: 19*e1fe3e4aSElliott Hughes pass 20*e1fe3e4aSElliott Hughes 21*e1fe3e4aSElliott Hughes 22*e1fe3e4aSElliott Hugheslogger = logging.getLogger("fontTools.cu2qu") 23*e1fe3e4aSElliott Hughes 24*e1fe3e4aSElliott Hughes 25*e1fe3e4aSElliott Hughesdef _cpu_count(): 26*e1fe3e4aSElliott Hughes try: 27*e1fe3e4aSElliott Hughes return mp.cpu_count() 28*e1fe3e4aSElliott Hughes except NotImplementedError: # pragma: no cover 29*e1fe3e4aSElliott Hughes return 1 30*e1fe3e4aSElliott Hughes 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughesdef open_ufo(path): 33*e1fe3e4aSElliott Hughes if hasattr(ufo_module.Font, "open"): # ufoLib2 34*e1fe3e4aSElliott Hughes return ufo_module.Font.open(path) 35*e1fe3e4aSElliott Hughes return ufo_module.Font(path) # defcon 36*e1fe3e4aSElliott Hughes 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughesdef _font_to_quadratic(input_path, output_path=None, **kwargs): 39*e1fe3e4aSElliott Hughes ufo = open_ufo(input_path) 40*e1fe3e4aSElliott Hughes logger.info("Converting curves for %s", input_path) 41*e1fe3e4aSElliott Hughes if font_to_quadratic(ufo, **kwargs): 42*e1fe3e4aSElliott Hughes logger.info("Saving %s", output_path) 43*e1fe3e4aSElliott Hughes if output_path: 44*e1fe3e4aSElliott Hughes ufo.save(output_path) 45*e1fe3e4aSElliott Hughes else: 46*e1fe3e4aSElliott Hughes ufo.save() # save in-place 47*e1fe3e4aSElliott Hughes elif output_path: 48*e1fe3e4aSElliott Hughes _copytree(input_path, output_path) 49*e1fe3e4aSElliott Hughes 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughesdef _samepath(path1, path2): 52*e1fe3e4aSElliott Hughes # TODO on python3+, there's os.path.samefile 53*e1fe3e4aSElliott Hughes path1 = os.path.normcase(os.path.abspath(os.path.realpath(path1))) 54*e1fe3e4aSElliott Hughes path2 = os.path.normcase(os.path.abspath(os.path.realpath(path2))) 55*e1fe3e4aSElliott Hughes return path1 == path2 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes 58*e1fe3e4aSElliott Hughesdef _copytree(input_path, output_path): 59*e1fe3e4aSElliott Hughes if _samepath(input_path, output_path): 60*e1fe3e4aSElliott Hughes logger.debug("input and output paths are the same file; skipped copy") 61*e1fe3e4aSElliott Hughes return 62*e1fe3e4aSElliott Hughes if os.path.exists(output_path): 63*e1fe3e4aSElliott Hughes shutil.rmtree(output_path) 64*e1fe3e4aSElliott Hughes shutil.copytree(input_path, output_path) 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughes 67*e1fe3e4aSElliott Hughesdef main(args=None): 68*e1fe3e4aSElliott Hughes """Convert a UFO font from cubic to quadratic curves""" 69*e1fe3e4aSElliott Hughes parser = argparse.ArgumentParser(prog="cu2qu") 70*e1fe3e4aSElliott Hughes parser.add_argument("--version", action="version", version=fontTools.__version__) 71*e1fe3e4aSElliott Hughes parser.add_argument( 72*e1fe3e4aSElliott Hughes "infiles", 73*e1fe3e4aSElliott Hughes nargs="+", 74*e1fe3e4aSElliott Hughes metavar="INPUT", 75*e1fe3e4aSElliott Hughes help="one or more input UFO source file(s).", 76*e1fe3e4aSElliott Hughes ) 77*e1fe3e4aSElliott Hughes parser.add_argument("-v", "--verbose", action="count", default=0) 78*e1fe3e4aSElliott Hughes parser.add_argument( 79*e1fe3e4aSElliott Hughes "-e", 80*e1fe3e4aSElliott Hughes "--conversion-error", 81*e1fe3e4aSElliott Hughes type=float, 82*e1fe3e4aSElliott Hughes metavar="ERROR", 83*e1fe3e4aSElliott Hughes default=None, 84*e1fe3e4aSElliott Hughes help="maxiumum approximation error measured in EM (default: 0.001)", 85*e1fe3e4aSElliott Hughes ) 86*e1fe3e4aSElliott Hughes parser.add_argument( 87*e1fe3e4aSElliott Hughes "-m", 88*e1fe3e4aSElliott Hughes "--mixed", 89*e1fe3e4aSElliott Hughes default=False, 90*e1fe3e4aSElliott Hughes action="store_true", 91*e1fe3e4aSElliott Hughes help="whether to used mixed quadratic and cubic curves", 92*e1fe3e4aSElliott Hughes ) 93*e1fe3e4aSElliott Hughes parser.add_argument( 94*e1fe3e4aSElliott Hughes "--keep-direction", 95*e1fe3e4aSElliott Hughes dest="reverse_direction", 96*e1fe3e4aSElliott Hughes action="store_false", 97*e1fe3e4aSElliott Hughes help="do not reverse the contour direction", 98*e1fe3e4aSElliott Hughes ) 99*e1fe3e4aSElliott Hughes 100*e1fe3e4aSElliott Hughes mode_parser = parser.add_mutually_exclusive_group() 101*e1fe3e4aSElliott Hughes mode_parser.add_argument( 102*e1fe3e4aSElliott Hughes "-i", 103*e1fe3e4aSElliott Hughes "--interpolatable", 104*e1fe3e4aSElliott Hughes action="store_true", 105*e1fe3e4aSElliott Hughes help="whether curve conversion should keep interpolation compatibility", 106*e1fe3e4aSElliott Hughes ) 107*e1fe3e4aSElliott Hughes mode_parser.add_argument( 108*e1fe3e4aSElliott Hughes "-j", 109*e1fe3e4aSElliott Hughes "--jobs", 110*e1fe3e4aSElliott Hughes type=int, 111*e1fe3e4aSElliott Hughes nargs="?", 112*e1fe3e4aSElliott Hughes default=1, 113*e1fe3e4aSElliott Hughes const=_cpu_count(), 114*e1fe3e4aSElliott Hughes metavar="N", 115*e1fe3e4aSElliott Hughes help="Convert using N multiple processes (default: %(default)s)", 116*e1fe3e4aSElliott Hughes ) 117*e1fe3e4aSElliott Hughes 118*e1fe3e4aSElliott Hughes output_parser = parser.add_mutually_exclusive_group() 119*e1fe3e4aSElliott Hughes output_parser.add_argument( 120*e1fe3e4aSElliott Hughes "-o", 121*e1fe3e4aSElliott Hughes "--output-file", 122*e1fe3e4aSElliott Hughes default=None, 123*e1fe3e4aSElliott Hughes metavar="OUTPUT", 124*e1fe3e4aSElliott Hughes help=( 125*e1fe3e4aSElliott Hughes "output filename for the converted UFO. By default fonts are " 126*e1fe3e4aSElliott Hughes "modified in place. This only works with a single input." 127*e1fe3e4aSElliott Hughes ), 128*e1fe3e4aSElliott Hughes ) 129*e1fe3e4aSElliott Hughes output_parser.add_argument( 130*e1fe3e4aSElliott Hughes "-d", 131*e1fe3e4aSElliott Hughes "--output-dir", 132*e1fe3e4aSElliott Hughes default=None, 133*e1fe3e4aSElliott Hughes metavar="DIRECTORY", 134*e1fe3e4aSElliott Hughes help="output directory where to save converted UFOs", 135*e1fe3e4aSElliott Hughes ) 136*e1fe3e4aSElliott Hughes 137*e1fe3e4aSElliott Hughes options = parser.parse_args(args) 138*e1fe3e4aSElliott Hughes 139*e1fe3e4aSElliott Hughes if ufo_module is None: 140*e1fe3e4aSElliott Hughes parser.error("Either ufoLib2 or defcon are required to run this script.") 141*e1fe3e4aSElliott Hughes 142*e1fe3e4aSElliott Hughes if not options.verbose: 143*e1fe3e4aSElliott Hughes level = "WARNING" 144*e1fe3e4aSElliott Hughes elif options.verbose == 1: 145*e1fe3e4aSElliott Hughes level = "INFO" 146*e1fe3e4aSElliott Hughes else: 147*e1fe3e4aSElliott Hughes level = "DEBUG" 148*e1fe3e4aSElliott Hughes logging.basicConfig(level=level) 149*e1fe3e4aSElliott Hughes 150*e1fe3e4aSElliott Hughes if len(options.infiles) > 1 and options.output_file: 151*e1fe3e4aSElliott Hughes parser.error("-o/--output-file can't be used with multile inputs") 152*e1fe3e4aSElliott Hughes 153*e1fe3e4aSElliott Hughes if options.output_dir: 154*e1fe3e4aSElliott Hughes output_dir = options.output_dir 155*e1fe3e4aSElliott Hughes if not os.path.exists(output_dir): 156*e1fe3e4aSElliott Hughes os.mkdir(output_dir) 157*e1fe3e4aSElliott Hughes elif not os.path.isdir(output_dir): 158*e1fe3e4aSElliott Hughes parser.error("'%s' is not a directory" % output_dir) 159*e1fe3e4aSElliott Hughes output_paths = [ 160*e1fe3e4aSElliott Hughes os.path.join(output_dir, os.path.basename(p)) for p in options.infiles 161*e1fe3e4aSElliott Hughes ] 162*e1fe3e4aSElliott Hughes elif options.output_file: 163*e1fe3e4aSElliott Hughes output_paths = [options.output_file] 164*e1fe3e4aSElliott Hughes else: 165*e1fe3e4aSElliott Hughes # save in-place 166*e1fe3e4aSElliott Hughes output_paths = [None] * len(options.infiles) 167*e1fe3e4aSElliott Hughes 168*e1fe3e4aSElliott Hughes kwargs = dict( 169*e1fe3e4aSElliott Hughes dump_stats=options.verbose > 0, 170*e1fe3e4aSElliott Hughes max_err_em=options.conversion_error, 171*e1fe3e4aSElliott Hughes reverse_direction=options.reverse_direction, 172*e1fe3e4aSElliott Hughes all_quadratic=False if options.mixed else True, 173*e1fe3e4aSElliott Hughes ) 174*e1fe3e4aSElliott Hughes 175*e1fe3e4aSElliott Hughes if options.interpolatable: 176*e1fe3e4aSElliott Hughes logger.info("Converting curves compatibly") 177*e1fe3e4aSElliott Hughes ufos = [open_ufo(infile) for infile in options.infiles] 178*e1fe3e4aSElliott Hughes if fonts_to_quadratic(ufos, **kwargs): 179*e1fe3e4aSElliott Hughes for ufo, output_path in zip(ufos, output_paths): 180*e1fe3e4aSElliott Hughes logger.info("Saving %s", output_path) 181*e1fe3e4aSElliott Hughes if output_path: 182*e1fe3e4aSElliott Hughes ufo.save(output_path) 183*e1fe3e4aSElliott Hughes else: 184*e1fe3e4aSElliott Hughes ufo.save() 185*e1fe3e4aSElliott Hughes else: 186*e1fe3e4aSElliott Hughes for input_path, output_path in zip(options.infiles, output_paths): 187*e1fe3e4aSElliott Hughes if output_path: 188*e1fe3e4aSElliott Hughes _copytree(input_path, output_path) 189*e1fe3e4aSElliott Hughes else: 190*e1fe3e4aSElliott Hughes jobs = min(len(options.infiles), options.jobs) if options.jobs > 1 else 1 191*e1fe3e4aSElliott Hughes if jobs > 1: 192*e1fe3e4aSElliott Hughes func = partial(_font_to_quadratic, **kwargs) 193*e1fe3e4aSElliott Hughes logger.info("Running %d parallel processes", jobs) 194*e1fe3e4aSElliott Hughes with closing(mp.Pool(jobs)) as pool: 195*e1fe3e4aSElliott Hughes pool.starmap(func, zip(options.infiles, output_paths)) 196*e1fe3e4aSElliott Hughes else: 197*e1fe3e4aSElliott Hughes for input_path, output_path in zip(options.infiles, output_paths): 198*e1fe3e4aSElliott Hughes _font_to_quadratic(input_path, output_path, **kwargs) 199