Skip to main content
Glama
cli.py6.08 kB
import os import argparse import logging import shutil import multiprocessing as mp from contextlib import closing from functools import partial import fontTools from .ufo import font_to_quadratic, fonts_to_quadratic ufo_module = None try: import ufoLib2 as ufo_module except ImportError: try: import defcon as ufo_module except ImportError as e: pass logger = logging.getLogger("fontTools.cu2qu") def _cpu_count(): try: return mp.cpu_count() except NotImplementedError: # pragma: no cover return 1 def open_ufo(path): if hasattr(ufo_module.Font, "open"): # ufoLib2 return ufo_module.Font.open(path) return ufo_module.Font(path) # defcon def _font_to_quadratic(input_path, output_path=None, **kwargs): ufo = open_ufo(input_path) logger.info("Converting curves for %s", input_path) if font_to_quadratic(ufo, **kwargs): logger.info("Saving %s", output_path) if output_path: ufo.save(output_path) else: ufo.save() # save in-place elif output_path: _copytree(input_path, output_path) def _samepath(path1, path2): # TODO on python3+, there's os.path.samefile path1 = os.path.normcase(os.path.abspath(os.path.realpath(path1))) path2 = os.path.normcase(os.path.abspath(os.path.realpath(path2))) return path1 == path2 def _copytree(input_path, output_path): if _samepath(input_path, output_path): logger.debug("input and output paths are the same file; skipped copy") return if os.path.exists(output_path): shutil.rmtree(output_path) shutil.copytree(input_path, output_path) def _main(args=None): """Convert a UFO font from cubic to quadratic curves""" parser = argparse.ArgumentParser(prog="cu2qu") parser.add_argument("--version", action="version", version=fontTools.__version__) parser.add_argument( "infiles", nargs="+", metavar="INPUT", help="one or more input UFO source file(s).", ) parser.add_argument("-v", "--verbose", action="count", default=0) parser.add_argument( "-e", "--conversion-error", type=float, metavar="ERROR", default=None, help="maxiumum approximation error measured in EM (default: 0.001)", ) parser.add_argument( "-m", "--mixed", default=False, action="store_true", help="whether to used mixed quadratic and cubic curves", ) parser.add_argument( "--keep-direction", dest="reverse_direction", action="store_false", help="do not reverse the contour direction", ) mode_parser = parser.add_mutually_exclusive_group() mode_parser.add_argument( "-i", "--interpolatable", action="store_true", help="whether curve conversion should keep interpolation compatibility", ) mode_parser.add_argument( "-j", "--jobs", type=int, nargs="?", default=1, const=_cpu_count(), metavar="N", help="Convert using N multiple processes (default: %(default)s)", ) output_parser = parser.add_mutually_exclusive_group() output_parser.add_argument( "-o", "--output-file", default=None, metavar="OUTPUT", help=( "output filename for the converted UFO. By default fonts are " "modified in place. This only works with a single input." ), ) output_parser.add_argument( "-d", "--output-dir", default=None, metavar="DIRECTORY", help="output directory where to save converted UFOs", ) options = parser.parse_args(args) if ufo_module is None: parser.error("Either ufoLib2 or defcon are required to run this script.") if not options.verbose: level = "WARNING" elif options.verbose == 1: level = "INFO" else: level = "DEBUG" logging.basicConfig(level=level) if len(options.infiles) > 1 and options.output_file: parser.error("-o/--output-file can't be used with multile inputs") if options.output_dir: output_dir = options.output_dir if not os.path.exists(output_dir): os.mkdir(output_dir) elif not os.path.isdir(output_dir): parser.error("'%s' is not a directory" % output_dir) output_paths = [ os.path.join(output_dir, os.path.basename(p)) for p in options.infiles ] elif options.output_file: output_paths = [options.output_file] else: # save in-place output_paths = [None] * len(options.infiles) kwargs = dict( dump_stats=options.verbose > 0, max_err_em=options.conversion_error, reverse_direction=options.reverse_direction, all_quadratic=False if options.mixed else True, ) if options.interpolatable: logger.info("Converting curves compatibly") ufos = [open_ufo(infile) for infile in options.infiles] if fonts_to_quadratic(ufos, **kwargs): for ufo, output_path in zip(ufos, output_paths): logger.info("Saving %s", output_path) if output_path: ufo.save(output_path) else: ufo.save() else: for input_path, output_path in zip(options.infiles, output_paths): if output_path: _copytree(input_path, output_path) else: jobs = min(len(options.infiles), options.jobs) if options.jobs > 1 else 1 if jobs > 1: func = partial(_font_to_quadratic, **kwargs) logger.info("Running %d parallel processes", jobs) with closing(mp.Pool(jobs)) as pool: pool.starmap(func, zip(options.infiles, output_paths)) else: for input_path, output_path in zip(options.infiles, output_paths): _font_to_quadratic(input_path, output_path, **kwargs)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Lillard01/chatExcel-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server