def build_fontmake(self,
                    designspace_path,
                    output=['variable'],
                    output_dir=None,
                    remove_overlaps=False,
                    interpolate=False,
                    autohint=None):
     if not os.path.isdir(output_dir):
         os.makedirs(output_dir)
     project = FontProject()
     fontmake_args = {
         'output': output,
         'output_path': None,
         'output_dir': output_dir,
         # 'feature_writers': None,
         'interpolate': interpolate,
         'remove_overlaps': remove_overlaps,
         'overlaps_backend': 'booleanOperations',
         'autohint': autohint,
         'use_production_names': False
     }
     if 'variable' in output:
         fontmake_args['optimize_gvar'] = True
     if 'otf' in output:
         fontmake_args['optimize_cff'] = CFFOptimization.SUBROUTINIZE
     print(
         project.run_from_designspace(designspace_path=designspace_path,
                                      **fontmake_args))
def makeVanillaFamily(family, *output, newName=" "):
    # if family not in pan_european_fonts:
    #     print("Please note that these fonts may lack")
    #     print("basic figures and punctuation.")
    path, folder = getFile(".designspace", family)
    designSpace = openDesignSpace(path)
    destination = folder + "/" + "Instances"
    ### test if mti
    for file in os.listdir(folder):
        if file.endswith(".plist"):
            # INJECT COMPILED TABLE IN FONTS
            ufoWithMTIfeatures2font(family, output)
            if newName != " ":
                renameFonts(family, newName)
            return
    fp = FontProject()
    fonts = fp.run_from_designspace(expand_features_to_instances=True,
                                    use_mutatormath=False,
                                    designspace_path=path,
                                    interpolate=True,
                                    output=("otf"),
                                    output_dir=destination)
    ufolist = list()
    for ufo in os.listdir(os.path.join(folder, "instance_ufos")):
        if ufo[-4:] == ".ufo":
            ufolist.append(ufo)
    instancesFolder = family + "/instance_ufos"
    ufo2font(instancesFolder, ufolist, output, fromInstances=True)
    if newName != " ":
        renameFonts(family, newName)
예제 #3
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument('-o',
                        '--output',
                        nargs='+',
                        default=('otf', 'ttf'),
                        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable'))
    parser.add_argument('-i',
                        '--interpolate',
                        action='store_true',
                        help='interpolate masters (Glyphs source only)')
    parser.add_argument('--mti-source')
    parser.add_argument('--use-afdko', action='store_true')
    parser.add_argument('--timing', action='store_true')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if not sum(1
               for p in [glyphs_path, ufo_paths, designspace_path] if p) == 1:
        raise ValueError('Exactly one source type required (Glyphs or UFO).')

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)

    elif designspace_path:
        project.run_from_designspace(designspace_path, **args)

    else:
        excluded = 'interpolate'
        if args[excluded]:
            raise ValueError(
                '"%s" argument only available for Glyphs or Designspace source'
                % excluded)
        del args[excluded]

    if ufo_paths:
        project.run_from_ufos(ufo_paths, **args)
def makeOtfFamily(family, newName=" ", onlyOtf=False):
    if family not in pan_european_fonts:
        print("Please note that non-latin OTF fonts can't have")
        print("the figures and punctuation from Latin merged in it")
    output = "otf"
    path, folder = getFile(".designspace", family)
    designSpace = openDesignSpace(path)
    destination = folder + "/" + "Instances"
    ### test if mti
    for file in os.listdir(folder):
        if file.endswith(".plist"):
            # INJECT COMPILED TABLE IN FONTS
            ufoWithMTIfeatures2font(family, output)
            if newName != " ":
                renameFonts(family, newName)
            return
    if onlyOtf is True:
        fp = FontProject()
        fonts = fp.run_from_designspace(expand_features_to_instances=True,
                                        use_mutatormath=False,
                                        designspace_path=path,
                                        interpolate=True,
                                        output=("otf"),
                                        output_dir=destination)
    ufolist = list()
    for ufo in os.listdir(os.path.join(folder, "instance_ufos")):
        if ufo[-4:] == ".ufo":
            ufolist.append(ufo)
    instancesFolder = family + "/instance_ufos"
    ufo2font(instancesFolder, ufolist, output, fromInstances=True)
    if newName != " ":
        renameFonts(family, newName)


### TEST FUNCTIONS ###
# mastersUfos2fonts("NotoSansThaana", "otf")
# makeTTFInstancesFromVF("NotoSerifSinhala")
# makeTTFInstancesFromVF("NotoSansArabic")
# subsetFonts("NotoNaskhArabic", "Core_Arabic")
# designSpace2Instances("NotoSansVai", "otf", "ttf")
# ufoWithMTIfeatures2font("NotoNastaliqUrdu", "ttf")
# subsetFonts("NotoSans", "CyrillicPro", familyNewName = "Avocado Sans", flavor=["ttf"])
# subsetFonts("NotoNaskhArabicUI", "Core_Arabic")
# subsetFonts("NotoSans", "SecureSet")
# mastersUfos2fonts("NotoSansThaana", "woff2")
# renameFonts("NotoMusic", "Tomato Soup", 'otf')
# mergeFonts("NotoSans","NotoNastaliqUrdu")
# designSpace2Var("NotoSerifThai")
# makeTTFInstancesFromVF("NotoSerif")
# makeOneInstanceFromVF("NotoSansThaana", {'wght': 190.0})
# mastersUfos2fonts("NotoSansThaana", "ttf")
# ufosToGlyphs("NotoSansThaana")
# ufo2font("NotoSans", ["NotoSans-Bold.ufo"], "ttf")
# designSpace2Instances("NotoSansNko", "ttf", secureSet=False)
# mergeFonts("NotoSansThaana", "NotoSerifHebrew")
# addSecureSet("NotoSansArabic", "ttf")
예제 #5
0
def build(args):
    designspace = os.path.join(args.build, args.designspace)

    os.environ["SOURCE_DATE_EPOCH"] = epoch(args)

    autohint = None
    if args.release:
        autohint = ""

    project = FontProject(verbose="WARNING")
    if args.ufo:
        project.run_from_ufos([args.ufo],
                              output=args.output,
                              remove_overlaps=False,
                              reverse_direction=False,
                              subroutinize=args.release,
                              autohint=autohint)
    else:
        project.run_from_designspace(designspace,
                                     output=args.output,
                                     subroutinize=args.release,
                                     autohint=autohint)
예제 #6
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument('-o', '--output', nargs='+', default=('otf', 'ttf'),
                        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable'))
    parser.add_argument('-i', '--interpolate', action='store_true',
                        help='interpolate masters (Glyphs source only)')
    parser.add_argument('--mti-source')
    parser.add_argument('--use-afdko', action='store_true')
    parser.add_argument('--timing', action='store_true')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if not sum(1 for p in [glyphs_path, ufo_paths, designspace_path] if p) == 1:
        raise ValueError('Exactly one source type required (Glyphs or UFO).')

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)

    elif designspace_path:
        project.run_from_designspace(designspace_path, **args)

    else:
        excluded = 'interpolate'
        if args[excluded]:
            raise ValueError(
                '"%s" argument only available for Glyphs or Designspace source'
                % excluded)
        del args[excluded]

    if ufo_paths:
        project.run_from_ufos(ufo_paths, **args)
예제 #7
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument('-o',
                        '--output',
                        nargs='+',
                        default=('otf', 'ttf'),
                        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable',
                                 'variable'))

    parser.add_argument('-i',
                        '--interpolate',
                        action='store_true',
                        help='interpolate masters (for Glyphs or MutatorMath '
                        'sources only)')
    parser.add_argument('-M',
                        '--masters-as-instances',
                        action='store_true',
                        help='treat masters as instances')
    parser.add_argument('-a',
                        '--autohint',
                        nargs='?',
                        const="",
                        help='can provide arguments to ttfautohint, quoted')
    parser.add_argument('--mti-source')
    parser.add_argument('--family-name',
                        help='Family name to use for masters,'
                        ' and to filter output instances by')
    parser.add_argument('--use-afdko', action='store_true')
    parser.add_argument('--keep-direction',
                        dest="reverse_direction",
                        action='store_false',
                        help='Do not reverse contour '
                        'direction when output is ttf or ttf-interpolatable')
    parser.add_argument('--keep-overlaps',
                        dest="remove_overlaps",
                        action='store_false',
                        help='Do not remove any overlap.')
    group1 = parser.add_mutually_exclusive_group(required=False)
    group1.add_argument(
        '--production-names',
        dest='use_production_names',
        action='store_true',
        help='Rename glyphs with '
        'production names if available otherwise use uninames.')
    group1.add_argument('--no-production-names',
                        dest='use_production_names',
                        action='store_false')
    group2 = parser.add_mutually_exclusive_group(required=False)
    group2.add_argument('--subset',
                        dest='subset',
                        action='store_true',
                        help='Subset font using export '
                        'flags set by glyphsLib')
    group2.add_argument('--no-subset', dest='subset', action='store_false')
    group3 = parser.add_mutually_exclusive_group(required=False)
    group3.add_argument('-s',
                        '--subroutinize',
                        action='store_true',
                        help='Optimize CFF table using compreffor (default)')
    group3.add_argument('-S',
                        '--no-subroutinize',
                        dest='subroutinize',
                        action='store_false')
    parser.add_argument('-e',
                        '--conversion-error',
                        type=float,
                        default=None,
                        metavar='ERROR',
                        help="Maximum approximation error for"
                        " cubic to quadratic conversion measured in EM")
    parser.set_defaults(use_production_names=None,
                        subset=None,
                        subroutinize=True)
    parser.add_argument('--timing', action='store_true')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if not sum(1
               for p in [glyphs_path, ufo_paths, designspace_path] if p) == 1:
        parser.error('Exactly one source type required (Glyphs, UFO, or '
                     'MutatorMath).')

    def exclude_args(parser, args, excluded_args):
        exclusive_msg = '"{}" argument only available for {} source'
        exclusive_src = {
            'interpolate': 'Glyphs or MutatorMath',
            'family_name': 'Glyphs'
        }
        for excluded in excluded_args:
            if args[excluded]:
                parser.error(
                    exclusive_msg.format(excluded, exclusive_src[excluded]))
            del args[excluded]

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)

    elif designspace_path:
        exclude_args(parser, args, ['family_name'])
        project.run_from_designspace(designspace_path, **args)

    else:
        exclude_args(parser, args, ['family_name', 'interpolate'])

    if ufo_paths:
        project.run_from_ufos(ufo_paths,
                              is_instance=args.pop('masters_as_instances'),
                              **args)
예제 #8
0
def main(args=None):
    parser = ArgumentParser()
    parser.add_argument("--version", action="version", version=__version__)
    inputGroup = parser.add_argument_group(
        title="Input arguments",
        description="The following arguments are mutually exclusive.",
    )
    xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
    xInputGroup.add_argument("-g",
                             "--glyphs-path",
                             metavar="GLYPHS",
                             help="Path to .glyphs source file")
    xInputGroup.add_argument(
        "-u",
        "--ufo-paths",
        nargs="+",
        metavar="UFO",
        help="One or more paths to UFO files",
    )
    xInputGroup.add_argument(
        "-m",
        "--mm-designspace",
        metavar="DESIGNSPACE",
        help="Path to .designspace file",
    )

    outputGroup = parser.add_argument_group(title="Output arguments")
    outputGroup.add_argument(
        "-o",
        "--output",
        nargs="+",
        default=("otf", "ttf"),
        metavar="FORMAT",
        help="Output font formats. Choose between: %(choices)s. "
        "Default: otf, ttf",
        choices=(
            "ufo",
            "otf",
            "ttf",
            "ttf-interpolatable",
            "otf-interpolatable",
            "variable",
            "variable-cff2",
        ),
    )
    outputSubGroup = outputGroup.add_mutually_exclusive_group()
    outputSubGroup.add_argument(
        "--output-path",
        default=None,
        help="Output font file path. Only valid when the output is a single "
        "file (e.g. input is a single UFO or output is variable font)",
    )
    outputSubGroup.add_argument(
        "--output-dir",
        default=None,
        help="Output folder. By default, output folders are created in the "
        "current working directory, grouping output fonts by format.",
    )
    outputGroup.add_argument(
        "-i",
        "--interpolate",
        nargs="?",
        default=False,
        const=True,
        metavar="INSTANCE_NAME",
        help="Interpolate masters and generate all the instances defined. "
        "To only interpolate a specific instance (or instances) that "
        'match a given "name" attribute, you can pass as argument '
        "the full instance name or a regular expression. "
        'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
        "(for Glyphs or MutatorMath sources only). ",
    )
    outputGroup.add_argument(
        "-M",
        "--masters-as-instances",
        action="store_true",
        help="Output masters as instances",
    )
    outputGroup.add_argument(
        "--family-name",
        help="Family name to use for masters, and to filter output instances",
    )
    outputGroup.add_argument(
        "--round-instances",
        dest="round_instances",
        action="store_true",
        help="Apply integer rounding to all geometry when interpolating",
    )
    outputGroup.add_argument(
        "--designspace-path",
        default=None,
        help="Path to output designspace file (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--master-dir",
        default=None,
        help='Directory where to write master UFO. Default: "./master_ufo". '
        'If value is "{tmp}", a temporary directory is created and '
        "removed at the end (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--instance-dir",
        default=None,
        help="Directory where to write instance UFOs. Default: "
        '"./instance_ufo". If value is "{tmp}", a temporary directory '
        "is created and removed at the end (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--validate-ufo",
        action="store_true",
        help="Enable ufoLib validation on reading/writing UFO files. It is "
        "disabled by default",
    )

    contourGroup = parser.add_argument_group(title="Handling of contours")
    contourGroup.add_argument(
        "--keep-overlaps",
        dest="remove_overlaps",
        action="store_false",
        help="Do not remove any overlap.",
    )
    contourGroup.add_argument(
        "--overlaps-backend",
        dest="overlaps_backend",
        metavar="BACKEND",
        choices=("booleanOperations", "pathops"),
        default="booleanOperations",
        help="Select library to remove overlaps. Choose between: %(choices)s "
        "(default: %(default)s)",
    )
    contourGroup.add_argument(
        "--keep-direction",
        dest="reverse_direction",
        action="store_false",
        help="Do not reverse contour direction when output is ttf or "
        "ttf-interpolatable",
    )
    contourGroup.add_argument(
        "-e",
        "--conversion-error",
        type=float,
        default=None,
        metavar="ERROR",
        help="Maximum approximation error for cubic to quadratic conversion "
        "measured in EM",
    )
    contourGroup.add_argument(
        "-a",
        "--autohint",
        nargs="?",
        const="",
        help="Run ttfautohint. Can provide arguments, quoted",
    )
    contourGroup.add_argument(
        "--cff-round-tolerance",
        type=float,
        default=None,
        metavar="FLOAT",
        help="Restrict rounding of point coordinates in CFF table to only "
        "those floats whose absolute difference from their integral part "
        "is less than or equal to the tolerance. By default, all floats "
        "are rounded to integer (tolerance 0.5); 0 disables rounding.",
    )
    contourGroup.add_argument(
        "--optimize-cff",
        type=lambda s: CFFOptimization(int(s)),
        default=CFFOptimization.SUBROUTINIZE,
        help="0 disables all optimizations; 1 specializes the CFF charstring "
        "operators; 2 (default) also enables subroutinization",
    )

    layoutGroup = parser.add_argument_group(
        title="Handling of OpenType Layout")
    layoutGroup.add_argument(
        "--interpolate-binary-layout",
        nargs="?",
        default=False,
        const=True,
        metavar="MASTER_DIR",
        help="Interpolate layout tables from compiled master binaries. "
        "Requires Glyphs or MutatorMath source.",
    )
    layoutGroup.add_argument(
        "--feature-writer",
        metavar="CLASS",
        action="append",
        dest="feature_writer_specs",
        help="string specifying a feature writer class to load, either "
        "built-in or from an external module, optionally initialized with "
        "the given keyword arguments. The class and module names are "
        "separated by '::'. The option can be repeated multiple times "
        "for each writer class. A special value of 'None' will disable "
        "all automatic feature generation. The option overrides both the "
        "default ufo2ft writers and those specified in the UFO lib.",
    )

    feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
    feaCompilerGroup.add_argument(
        "--use-afdko",
        action="store_true",
        help="Use makeOTF instead of feaLib to compile FEA.",
    )
    feaCompilerGroup.add_argument(
        "--mti-source",
        help="Path to mtiLib .txt feature definitions (use instead of FEA)",
    )

    glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
    glyphnamesGroup.add_argument(
        "--production-names",
        dest="use_production_names",
        action="store_true",
        help="Rename glyphs with production names if available otherwise use "
        "uninames.",
    )
    glyphnamesGroup.add_argument("--no-production-names",
                                 dest="use_production_names",
                                 action="store_false")

    subsetGroup = parser.add_mutually_exclusive_group(required=False)
    subsetGroup.add_argument(
        "--subset",
        dest="subset",
        action="store_true",
        help="Subset font using export flags set by glyphsLib",
    )
    subsetGroup.add_argument("--no-subset",
                             dest="subset",
                             action="store_false")

    subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
    subroutinizeGroup.add_argument(
        "-s",
        "--subroutinize",
        action="store_true",
        help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
        "--optimize-cff option instead]",
    )
    subroutinizeGroup.add_argument("-S",
                                   "--no-subroutinize",
                                   dest="subroutinize",
                                   action="store_false")

    parser.set_defaults(use_production_names=None,
                        subset=None,
                        subroutinize=None)

    logGroup = parser.add_argument_group(title="Logging arguments")
    logGroup.add_argument("--timing",
                          action="store_true",
                          help="Print the elapsed time for each steps")
    logGroup.add_argument(
        "--verbose",
        default="INFO",
        metavar="LEVEL",
        choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
        help="Configure the logger verbosity level. Choose between: "
        "%(choices)s. Default: INFO",
    )
    args = vars(parser.parse_args(args))

    specs = args.pop("feature_writer_specs")
    if specs is not None:
        args["feature_writers"] = _loadFeatureWriters(parser, specs)

    glyphs_path = args.pop("glyphs_path")
    ufo_paths = args.pop("ufo_paths")
    designspace_path = args.pop("mm_designspace")
    input_format = ("Glyphs" if glyphs_path else
                    "designspace" if designspace_path else "UFO") + " source"

    if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
        if not (glyphs_path or designspace_path):
            parser.error(
                "Glyphs or designspace source required for variable font")
        exclude_args(
            parser,
            args,
            [
                "interpolate", "masters_as_instances",
                "interpolate_binary_layout"
            ],
            "variable output",
        )

    try:
        project = FontProject(
            timing=args.pop("timing"),
            verbose=args.pop("verbose"),
            validate_ufo=args.pop("validate_ufo"),
        )

        if glyphs_path:
            with _make_tempdirs(parser, args):
                project.run_from_glyphs(glyphs_path, **args)
            return

        exclude_args(
            parser,
            args,
            [
                "family_name",
                "mti_source",
                "designspace_path",
                "master_dir",
                "instance_dir",
            ],
            input_format,
        )
        if designspace_path:
            project.run_from_designspace(designspace_path, **args)
            return

        exclude_args(
            parser,
            args,
            ["interpolate", "interpolate_binary_layout", "round_instances"],
            input_format,
        )
        project.run_from_ufos(ufo_paths,
                              is_instance=args.pop("masters_as_instances"),
                              **args)
    except FontmakeError as e:
        import sys

        sys.exit("fontmake: error: %s" % e)
예제 #9
0
def main(args=None):
    parser = ArgumentParser()
    parser.add_argument("--version", action="version", version=__version__)
    inputGroup = parser.add_argument_group(
        title="Input arguments (flags)",
        description=
        "The following arguments are mutually exclusive (pick only one):",
    )
    xInputGroup = inputGroup.add_mutually_exclusive_group()
    xInputGroup.add_argument("-g",
                             "--glyphs-path",
                             metavar="GLYPHS",
                             help="Path to .glyphs source file")
    xInputGroup.add_argument(
        "-u",
        "--ufo-paths",
        nargs="+",
        metavar="UFO",
        help="One or more paths to UFO files",
    )
    xInputGroup.add_argument(
        "-m",
        "--mm-designspace",
        metavar="DESIGNSPACE",
        help="Path to .designspace file",
    )
    positionalInputs = parser.add_argument_group(
        title="Input arguments (positonal)",
        description=
        "Alternatively, guess source format from filename extension",
    )
    positionalInputs.add_argument(
        "posargs",
        nargs="*",
        metavar="INPUTS",
        help="Either one *.designspace or *.glyphs file, or one or more *.ufo",
    )

    outputGroup = parser.add_argument_group(title="Output arguments")
    outputGroup.add_argument(
        "-o",
        "--output",
        nargs="+",
        default=("otf", "ttf"),
        metavar="FORMAT",
        help=
        "Output font formats. Choose 1 or more from: %(choices)s. Default: otf, ttf. "
        "(No file paths).",
        choices=(
            "ufo",
            "otf",
            "otf-cff2",
            "ttf",
            "ttf-interpolatable",
            "otf-interpolatable",
            "variable",
            "variable-cff2",
        ),
    )
    outputSubGroup = outputGroup.add_mutually_exclusive_group()
    outputSubGroup.add_argument(
        "--output-path",
        default=None,
        help="Output font file path. Only valid when the output is a single "
        "file (e.g. input is a single UFO or output is variable font)",
    )
    outputSubGroup.add_argument(
        "--output-dir",
        default=None,
        help="Output folder. By default, output folders are created in the "
        "current working directory, grouping output fonts by format.",
    )
    outputGroup.add_argument(
        "-i",
        "--interpolate",
        nargs="?",
        default=False,
        const=True,
        metavar="INSTANCE_NAME",
        help="Interpolate masters and generate all the instances defined. "
        "To only interpolate a specific instance (or instances) that "
        'match a given "name" attribute, you can pass as argument '
        "the full instance name or a regular expression. "
        'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
        "(for Glyphs or MutatorMath sources only). ",
    )
    outputGroup.add_argument(
        "--use-mutatormath",
        action="store_true",
        help=(
            "Use MutatorMath to generate instances (supports extrapolation and "
            "anisotropic locations)."),
    )
    outputGroup.add_argument(
        "-M",
        "--masters-as-instances",
        action="store_true",
        help="Output masters as instances",
    )
    outputGroup.add_argument(
        "--family-name",
        help="Family name to use for masters, and to filter output instances",
    )
    outputGroup.add_argument(
        "--round-instances",
        dest="round_instances",
        action="store_true",
        help="Apply integer rounding to all geometry when interpolating",
    )
    outputGroup.add_argument(
        "--designspace-path",
        default=None,
        help="Path to output designspace file (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--master-dir",
        default=None,
        help='Directory where to write master UFO. Default: "./master_ufo". '
        'If value is "{tmp}", a temporary directory is created and '
        "removed at the end (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--instance-dir",
        default=None,
        help="Directory where to write instance UFOs. Default: "
        '"./instance_ufo". If value is "{tmp}", a temporary directory '
        "is created and removed at the end (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--no-write-skipexportglyphs",
        action="store_false",
        dest="write_skipexportglyphs",
        help=
        "Do not store the glyph export flags in the 'public.skipExportGlyphs' "
        "key of designspace/UFO lib, but use the old private glyph lib key "
        "'com.schriftgestaltung.Glyphs.Export' (for Glyphs sources only).",
    )
    outputGroup.add_argument(
        "--validate-ufo",
        action="store_true",
        help="Enable ufoLib validation on reading/writing UFO files. It is "
        "disabled by default",
    )
    outputGroup.add_argument(
        "--check-compatibility",
        action="store_true",
        help="Check if the source files are interpolatable. It is "
        "disabled by default, but enabled when building variable fonts "
        "or what the 'Enforce Compatibility Check' custom parameter is "
        "set on a Glyphs file",
    )
    outputGroup.add_argument(
        "--expand-features-to-instances",
        action="store_true",
        help="Resolves all include()s in the master feature file and writes "
        "the full feature file to all instance UFOs. Only valid when "
        "interpolating. Use if you share feature files of masters in "
        "external files, as instances can end up elsewhere.",
    )
    outputGroup.add_argument(
        "--no-generate-GDEF",
        dest="generate_GDEF",
        action="store_false",
        help=
        "Do not auto-generate a GDEF table, but keep an existing one intact.",
    )

    contourGroup = parser.add_argument_group(title="Handling of contours")
    contourGroup.add_argument(
        "--keep-overlaps",
        dest="remove_overlaps",
        action="store_false",
        help="Do not remove any overlap.",
    )
    contourGroup.add_argument(
        "--overlaps-backend",
        dest="overlaps_backend",
        metavar="BACKEND",
        choices=("booleanOperations", "pathops"),
        default="booleanOperations",
        help="Select library to remove overlaps. Choose between: %(choices)s "
        "(default: %(default)s)",
    )
    contourGroup.add_argument(
        "--keep-direction",
        dest="reverse_direction",
        action="store_false",
        help="Do not reverse contour direction when output is ttf or "
        "ttf-interpolatable",
    )
    contourGroup.add_argument(
        "-e",
        "--conversion-error",
        type=float,
        default=None,
        metavar="ERROR",
        help="Maximum approximation error for cubic to quadratic conversion "
        "measured in EM",
    )
    contourGroup.add_argument(
        "-f",
        "--flatten-components",
        dest="flatten_components",
        action="store_true",
        help="Flatten nested components to single level.",
    )
    contourGroup.add_argument(
        "-a",
        "--autohint",
        nargs="?",
        const="",
        help="Run ttfautohint. Can provide arguments, quoted",
    )
    contourGroup.add_argument(
        "--cff-round-tolerance",
        type=float,
        default=None,
        metavar="FLOAT",
        help="Restrict rounding of point coordinates in CFF table to only "
        "those floats whose absolute difference from their integral part "
        "is less than or equal to the tolerance. By default, all floats "
        "are rounded to integer (tolerance 0.5); 0 disables rounding.",
    )
    contourGroup.add_argument(
        "--optimize-cff",
        type=lambda s: CFFOptimization(int(s)),
        default=CFFOptimization.SUBROUTINIZE,
        help="0 disables all optimizations; 1 specializes the CFF charstring "
        "operators; 2 (default) also enables subroutinization",
    )
    contourGroup.add_argument(
        "--subroutinizer",
        default=None,
        choices=["compreffor", "cffsubr"],
        help="name of the library to use for compressing CFF charstrings. "
        "Choose between: %(choices)s. By default compreffor is used for CFF 1, "
        "and cffsubr for CFF2. NOTE: compreffor doesn't support CFF2.",
    )
    contourGroup.add_argument(
        "--no-optimize-gvar",
        dest="optimize_gvar",
        action="store_false",
        help="Do not perform IUP optimization on variable font's 'gvar' table. "
        "(only works with 'variable' TrueType-flavored output)",
    )
    contourGroup.add_argument(
        "--filter",
        metavar="CLASS",
        action="append",
        dest="filter_specs",
        help="string specifying a filter class to load, either "
        "built-in or from an external module, optionally initialized with "
        "the given keyword arguments. The class and module names are "
        "separated by '::'. The option can be repeated multiple times "
        "for each filter class. The option overrides the filters specified "
        "in the UFO lib.",
    )

    layoutGroup = parser.add_argument_group(
        title="Handling of OpenType Layout")
    layoutGroup.add_argument(
        "--interpolate-binary-layout",
        nargs="?",
        default=False,
        const=True,
        metavar="MASTER_DIR",
        help="Interpolate layout tables from compiled master binaries. "
        "Requires Glyphs or MutatorMath source.",
    )
    layoutGroup.add_argument(
        "--feature-writer",
        metavar="CLASS",
        action="append",
        dest="feature_writer_specs",
        help="string specifying a feature writer class to load, either "
        "built-in or from an external module, optionally initialized with "
        "the given keyword arguments. The class and module names are "
        "separated by '::'. The option can be repeated multiple times "
        "for each writer class. A special value of 'None' will disable "
        "all automatic feature generation. The option overrides both the "
        "default ufo2ft writers and those specified in the UFO lib.",
    )
    layoutGroup.add_argument(
        "--debug-feature-file",
        metavar="FILE",
        type=FileType("w", encoding="utf-8"),
        default=None,
        help=(
            "Path were to dump OpenType features text to debug auto-generated "
            "features (kern, mark, mkmk, etc.)."),
    )

    feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
    feaCompilerGroup.add_argument(
        "--mti-source",
        help="mtiLib feature definition .plist file path (use instead of FEA)",
    )

    glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
    glyphnamesGroup.add_argument(
        "--production-names",
        dest="use_production_names",
        action="store_true",
        help="Rename glyphs with production names if available otherwise use "
        "uninames.",
    )
    glyphnamesGroup.add_argument("--no-production-names",
                                 dest="use_production_names",
                                 action="store_false")

    subsetGroup = parser.add_mutually_exclusive_group(required=False)
    subsetGroup.add_argument(
        "--subset",
        dest="subset",
        action="store_true",
        help="Subset font using export flags set by glyphsLib",
    )
    subsetGroup.add_argument("--no-subset",
                             dest="subset",
                             action="store_false")

    subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
    subroutinizeGroup.add_argument(
        "-s",
        "--subroutinize",
        action="store_true",
        help="Optimize CFF table using compreffor (default) [DEPRECATED: use "
        "--optimize-cff option instead]",
    )
    subroutinizeGroup.add_argument("-S",
                                   "--no-subroutinize",
                                   dest="subroutinize",
                                   action="store_false")

    parser.set_defaults(use_production_names=None,
                        subset=None,
                        subroutinize=None)

    logGroup = parser.add_argument_group(title="Logging arguments")
    logGroup.add_argument("--timing",
                          action="store_true",
                          help="Print the elapsed time for each steps")
    logGroup.add_argument(
        "--verbose",
        default="INFO",
        metavar="LEVEL",
        choices=("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"),
        help="Configure the logger verbosity level. Choose between: "
        "%(choices)s. Default: INFO",
    )

    args = vars(parser.parse_args(args))

    specs = args.pop("feature_writer_specs")
    if specs is not None:
        args["feature_writers"] = _loadFeatureWriters(parser, specs)

    specs = args.pop("filter_specs")
    if specs is not None:
        args["filters"] = _loadFilters(parser, specs)

    inputs = parse_mutually_exclusive_inputs(parser, args)

    if INTERPOLATABLE_OUTPUTS.intersection(args["output"]):
        if not (inputs.glyphs_path or inputs.designspace_path):
            parser.error(
                "Glyphs or designspace source required for variable font")
        exclude_args(
            parser,
            args,
            [
                "interpolate",
                "masters_as_instances",
                "interpolate_binary_layout",
                "use_mutatormath",
            ],
            "variable output",
        )
    else:
        exclude_args(parser,
                     args, ["optimize_gvar"],
                     "static output",
                     positive=False)

    if args.get("use_mutatormath"):
        for module in ("defcon", "mutatorMath"):
            try:
                __import__(module)
            except ImportError:
                parser.error(
                    f"{module} module not found; reinstall fontmake with the "
                    "[mutatormath] extra")

    PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG"
    try:
        project = FontProject(
            timing=args.pop("timing"),
            verbose=args.pop("verbose"),
            validate_ufo=args.pop("validate_ufo"),
        )

        if inputs.glyphs_path:
            with _make_tempdirs(parser, args):
                project.run_from_glyphs(inputs.glyphs_path, **args)
            return

        exclude_args(
            parser,
            args,
            [
                "family_name",
                "mti_source",
                "designspace_path",
                "master_dir",
                "instance_dir",
            ],
            inputs.format_name,
        )
        exclude_args(parser,
                     args, ["write_skipexportglyphs"],
                     inputs.format_name,
                     positive=False)
        if inputs.designspace_path:
            project.run_from_designspace(inputs.designspace_path, **args)
            return

        exclude_args(
            parser,
            args,
            [
                "interpolate",
                "use_mutatormath",
                "interpolate_binary_layout",
                "round_instances",
                "expand_features_to_instances",
                "check_compatibility",
            ],
            inputs.format_name,
        )
        project.run_from_ufos(inputs.ufo_paths,
                              is_instance=args.pop("masters_as_instances"),
                              **args)
    except FontmakeError as e:
        if PRINT_TRACEBACK:
            logging.exception(e)
            sys.exit(1)
        sys.exit(f"fontmake: Error: {str(e)}")
    finally:
        debug_feature_file = args.get("debug_feature_file")
        if debug_feature_file is not None:
            debug_feature_file.close()
예제 #10
0
파일: __main__.py 프로젝트: brawer/fontmake
def main(args=None):
    parser = ArgumentParser()
    parser.add_argument('--version', action='version', version=__version__)
    inputGroup = parser.add_argument_group(
        title='Input arguments',
        description='The following arguments are mutually exclusive.')
    xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
    xInputGroup.add_argument(
        '-g', '--glyphs-path', metavar='GLYPHS',
        help='Path to .glyphs source file')
    xInputGroup.add_argument(
        '-u', '--ufo-paths', nargs='+', metavar='UFO',
        help='One or more paths to UFO files')
    xInputGroup.add_argument(
        '-m', '--mm-designspace', metavar='DESIGNSPACE',
        help='Path to .designspace file')

    outputGroup = parser.add_argument_group(title='Output arguments')
    outputGroup.add_argument(
        '-o', '--output', nargs='+', default=('otf', 'ttf'), metavar="FORMAT",
        help='Output font formats. Choose between: %(choices)s. '
             'Default: otf, ttf',
        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable', 'variable'))
    outputGroup.add_argument(
        '-i', '--interpolate', action='store_true',
        help='Interpolate masters (for Glyphs or MutatorMath sources only)')
    outputGroup.add_argument(
        '-M', '--masters-as-instances', action='store_true',
        help='Output masters as instances')
    outputGroup.add_argument(
        '--family-name',
        help='Family name to use for masters, and to filter output instances')

    contourGroup = parser.add_argument_group(title='Handling of contours')
    contourGroup.add_argument(
        '--keep-overlaps', dest='remove_overlaps', action='store_false',
        help='Do not remove any overlap.')
    contourGroup.add_argument(
        '--keep-direction', dest='reverse_direction', action='store_false',
        help='Do not reverse contour direction when output is ttf or '
             'ttf-interpolatable')
    contourGroup.add_argument(
        '-e', '--conversion-error', type=float, default=None, metavar='ERROR',
        help='Maximum approximation error for cubic to quadratic conversion '
             'measured in EM')
    contourGroup.add_argument(
        '-a', '--autohint', nargs='?', const='',
        help='Run ttfautohint. Can provide arguments, quoted')

    layoutGroup = parser.add_argument_group(title='Handling of OpenType Layout')
    layoutGroup.add_argument(
        '--interpolate-binary-layout', action='store_true',
        help='Interpolate layout tables from compiled master binaries. '
             'Requires Glyphs or MutatorMath source.')
    layoutGroup.add_argument(
        '--use-afdko', action='store_true',
        help='Use makeOTF instead of feaLib to compile FEA.')
    layoutGroup.add_argument(
        '--mti-source',
        help='Path to mtiLib .txt feature definitions (use instead of FEA)')
    layoutGroup.add_argument(
        '--kern-writer-module', metavar="MODULE", dest='kern_writer_class',
        type=PyClassType('KernFeatureWriter'),
        help='Module containing a custom `KernFeatureWriter` class.')
    layoutGroup.add_argument(
        '--mark-writer-module', metavar="MODULE", dest='mark_writer_class',
        type=PyClassType('MarkFeatureWriter'),
        help='Module containing a custom `MarkFeatureWriter` class.')

    glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
    glyphnamesGroup.add_argument(
        '--production-names', dest='use_production_names', action='store_true',
        help='Rename glyphs with production names if available otherwise use '
             'uninames.')
    glyphnamesGroup.add_argument(
        '--no-production-names', dest='use_production_names',
        action='store_false')

    subsetGroup = parser.add_mutually_exclusive_group(required=False)
    subsetGroup.add_argument(
        '--subset', dest='subset', action='store_true',
        help='Subset font using export flags set by glyphsLib')
    subsetGroup.add_argument(
        '--no-subset', dest='subset', action='store_false')

    subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
    subroutinizeGroup.add_argument(
        '-s', '--subroutinize', action='store_true',
        help='Optimize CFF table using compreffor (default)')
    subroutinizeGroup.add_argument(
        '-S', '--no-subroutinize', dest='subroutinize', action='store_false')

    parser.set_defaults(use_production_names=None, subset=None,
                        subroutinize=True)

    logGroup = parser.add_argument_group(title='Logging arguments')
    logGroup.add_argument(
        '--timing', action='store_true',
        help="Print the elapsed time for each steps")
    logGroup.add_argument(
        '--verbose', default='INFO', metavar='LEVEL',
        choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'),
        help='Configure the logger verbosity level. Choose between: '
             '%(choices)s. Default: INFO')
    args = vars(parser.parse_args(args))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')

    if 'variable' in args['output']:
        if not (glyphs_path or designspace_path):
            parser.error(
                'Glyphs or designspace source required for variable font')
        for argname in ('interpolate', 'masters_as_instances',
                        'interpolate_binary_layout'):
            if args[argname]:
                parser.error('--%s option invalid for variable font'
                             % argname.replace("_", "-"))

    project = FontProject(timing=args.pop('timing'),
                          verbose=args.pop('verbose'))

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)
        return

    exclude_args(parser, args, ['family_name'], 'Glyphs')
    if designspace_path:
        project.run_from_designspace(designspace_path, **args)
        return

    exclude_args(
        parser, args, ['interpolate', 'interpolate_binary_layout'],
        'Glyphs or MutatorMath')
    project.run_from_ufos(
        ufo_paths, is_instance=args.pop('masters_as_instances'), **args)
예제 #11
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument('-o',
                        '--output',
                        nargs='+',
                        default=('otf', 'ttf'),
                        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable'))
    parser.add_argument('-i',
                        '--interpolate',
                        action='store_true',
                        help='interpolate masters (for Glyphs or MutatorMath '
                        'sources only)')
    parser.add_argument('-mi',
                        '--masters-as-instances',
                        action='store_true',
                        help='treat masters as instances')
    parser.add_argument('-a',
                        '--autohint',
                        nargs='?',
                        help='can provide arguments to ttfautohint, quoted')
    parser.add_argument('--mti-source')
    parser.add_argument('--family-name',
                        help='Family name to use for masters,'
                        ' and to filter output instances by')
    parser.add_argument('--use-afdko', action='store_true')
    parser.add_argument('--keep-direction',
                        dest="reverse_direction",
                        action='store_false',
                        help='Do not reverse contour '
                        'direction when output is ttf or ttf-interpolatable')
    parser.add_argument('--keep-overlaps',
                        dest="remove_overlaps",
                        action='store_false',
                        help='Do not remove any overlap.')
    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--production-names',
                       dest='use_production_names',
                       action='store_true',
                       help='Rename glyphs with '
                       'production names if available otherwise use uninames.')
    group.add_argument('--no-production-names',
                       dest='use_production_names',
                       action='store_false',
                       help='Do not rename glyphs with production names. '
                       'Keeps original glyph names')
    parser.set_defaults(use_production_names=None)
    parser.add_argument('--timing', action='store_true')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if not sum(1
               for p in [glyphs_path, ufo_paths, designspace_path] if p) == 1:
        parser.error('Exactly one source type required (Glyphs, UFO, or '
                     'MutatorMath).')

    def exclude_args(parser, args, excluded_args):
        exclusive_msg = '"{}" argument only available for {} source'
        exclusive_src = {
            'interpolate': 'Glyphs or MutatorMath',
            'family_name': 'Glyphs'
        }
        for excluded in excluded_args:
            if args[excluded]:
                parser.error(
                    exclusive_msg.format(excluded, exclusive_src[excluded]))
            del args[excluded]

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)

    elif designspace_path:
        exclude_args(parser, args, ['family_name'])
        project.run_from_designspace(designspace_path, **args)

    else:
        exclude_args(parser, args, ['family_name', 'interpolate'])

    if ufo_paths:
        project.run_from_ufos(ufo_paths,
                              is_instance=args.pop('masters_as_instances'),
                              **args)
def designSpace2Instances(family, *output, newName=" ", secureSet=True):
    if len(output) == 1 and "otf" in output:
        makeOtfFamily(family, newName=newName, onlyOtf=True)
        return
    securetSetIsIncluded = pan_european_fonts
    if "otf" in output:
        makeOtfFamily(family, newName=newName)
    mergeable = ["ttf", "woff2", "woff"]
    if family not in securetSetIsIncluded:
        temp = list(set(mergeable) & set(output))
        if len(temp) == 0:
            output = ["ttf"]
        else:
            output = temp
    if "ttf" not in output:
        output.append("ttf")
    path, folder = getFile(".designspace", family)
    designSpace = openDesignSpace(path)
    destination = folder + "/" + "Instances"
    ###
    ### test if mti
    for file in os.listdir(folder):
        if file.endswith(".plist"):
            print("Make fonts with Mti files")
            # INJECT COMPILED TABLE IN FONTS
            ufoWithMTIfeatures2font(family, output)
            if secureSet is True:
                for i in output:
                    addSecureSet(family, output)
            if newName != " ":
                renameFonts(family, newName)
            if "woff" in output:
                destination = os.path.join(folder, "fonts/WOFF")
                if not os.path.exists(destination):
                    os.makedirs(destination)
                for ttf in os.listdir(os.path.join(folder, "fonts/TTF")):
                    if ttf.endswith("ttf"):
                        woff = ttLib.TTFont(
                            os.path.join(folder, "fonts/TTF", ttf))
                        woff.flavor = "woff"
                        woff.save(os.path.join(destination,
                                               ttf[:-4] + ".woff"))
            if "woff2" in output:
                destination = os.path.join(folder, "fonts/WOFF2")
                if not os.path.exists(destination):
                    os.makedirs(destination)
                for ttf in os.listdir(os.path.join(folder, "fonts/TTF")):
                    if ttf.endswith("ttf"):
                        woff = ttLib.TTFont(
                            os.path.join(folder, "fonts/TTF", ttf))
                        woff.flavor = "woff2"
                        woff.save(
                            os.path.join(destination, ttf[:-4] + ".woff2"))
            return

    fp = FontProject()
    fonts = fp.run_from_designspace(expand_features_to_instances=True,
                                    use_mutatormath=False,
                                    designspace_path=path,
                                    interpolate=True,
                                    output=("otf"),
                                    output_dir=destination)
    ufolist = list()
    for ufo in os.listdir(os.path.join(folder, "instance_ufos")):
        if ufo[-4:] == ".ufo":
            ufolist.append(ufo)
    instancesFolder = family + "/instance_ufos"

    if "ttf" in output:
        ufo2font(instancesFolder, ufolist, "ttf", fromInstances=True)
        if secureSet:
            addSecureSet(family, output)

    if "woff" in output:
        destination = os.path.join(folder, "fonts/WOFF")
        if not os.path.exists(destination):
            os.makedirs(destination)
        for ttf in os.listdir(os.path.join(folder, "fonts/TTF")):
            if ttf.endswith("ttf"):
                woff = ttLib.TTFont(os.path.join(folder, "fonts/TTF", ttf))
                woff.flavor = "woff"
                woff.save(os.path.join(destination, ttf[:-4] + ".woff"))

    if "woff2" in output:
        destination = os.path.join(folder, "fonts/WOFF2")
        if not os.path.exists(destination):
            os.makedirs(destination)
        for ttf in os.listdir(os.path.join(folder, "fonts/TTF")):
            if ttf.endswith("ttf"):
                woff = ttLib.TTFont(os.path.join(folder, "fonts/TTF", ttf))
                woff.flavor = "woff2"
                woff.save(os.path.join(destination, ttf[:-4] + ".woff2"))

    if newName != " ":
        renameFonts(family, newName)
예제 #13
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument('-o',
                        '--output',
                        nargs='+',
                        default=('otf', 'ttf'),
                        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable',
                                 'variable'))

    parser.add_argument(
        '-i',
        '--interpolate',
        action='store_true',
        help='Interpolate masters (for Glyphs or MutatorMath sources only)')
    parser.add_argument('-M',
                        '--masters-as-instances',
                        action='store_true',
                        help='Output masters as instances')
    parser.add_argument(
        '--family-name',
        help='Family name to use for masters, and to filter output instances')

    parser.add_argument('--mti-source')
    parser.add_argument(
        '--interpolate-binary-layout',
        action='store_true',
        help='Interpolate layout tables from compiled master binaries. '
        'Requires Glyphs or MutatorMath source.')
    parser.add_argument('--use-afdko', action='store_true')

    parser.add_argument('-a',
                        '--autohint',
                        nargs='?',
                        const='',
                        help='Run ttfautohint. Can provide arguments, quoted')
    parser.add_argument(
        '--keep-direction',
        dest='reverse_direction',
        action='store_false',
        help='Do not reverse contour direction when output is ttf or '
        'ttf-interpolatable')
    parser.add_argument('--keep-overlaps',
                        dest='remove_overlaps',
                        action='store_false',
                        help='Do not remove any overlap.')

    group1 = parser.add_mutually_exclusive_group(required=False)
    group1.add_argument(
        '--production-names',
        dest='use_production_names',
        action='store_true',
        help='Rename glyphs with production names if available otherwise use '
        'uninames.')
    group1.add_argument('--no-production-names',
                        dest='use_production_names',
                        action='store_false')

    group2 = parser.add_mutually_exclusive_group(required=False)
    group2.add_argument('--subset',
                        dest='subset',
                        action='store_true',
                        help='Subset font using export flags set by glyphsLib')
    group2.add_argument('--no-subset', dest='subset', action='store_false')

    group3 = parser.add_mutually_exclusive_group(required=False)
    group3.add_argument('-s',
                        '--subroutinize',
                        action='store_true',
                        help='Optimize CFF table using compreffor (default)')
    group3.add_argument('-S',
                        '--no-subroutinize',
                        dest='subroutinize',
                        action='store_false')
    parser.add_argument(
        '-e',
        '--conversion-error',
        type=float,
        default=None,
        metavar='ERROR',
        help='Maximum approximation error for cubic to quadratic conversion '
        'measured in EM')

    parser.set_defaults(use_production_names=None,
                        subset=None,
                        subroutinize=True)

    parser.add_argument('--timing', action='store_true')
    parser.add_argument('--verbose', default='INFO')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'),
                          verbose=args.pop('verbose'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if sum(1 for p in [glyphs_path, ufo_paths, designspace_path] if p) != 1:
        parser.error('Exactly one source type required (Glyphs, UFO, or '
                     'MutatorMath).')

    def exclude_args(parser, args, excluded_args, source_name):
        msg = '"%s" argument only available for %s source'
        for excluded in excluded_args:
            if args[excluded]:
                parser.error(msg % (excluded, source_name))
            del args[excluded]

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)
        return

    exclude_args(parser, args, ['family_name'], 'Glyphs')
    if designspace_path:
        project.run_from_designspace(designspace_path, **args)
        return

    exclude_args(parser, args, ['interpolate', 'interpolate_binary_layout'],
                 'Glyphs or MutatorMath')
    project.run_from_ufos(ufo_paths,
                          is_instance=args.pop('masters_as_instances'),
                          **args)
예제 #14
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument('-o', '--output', nargs='+', default=('otf', 'ttf'),
                        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable'))
    parser.add_argument('-i', '--interpolate', action='store_true',
                        help='interpolate masters (for Glyphs or MutatorMath '
                             'sources only)')
    parser.add_argument('-mi', '--masters-as-instances', action='store_true',
                        help='treat masters as instances')
    parser.add_argument('-a', '--autohint', nargs='?',
                        help='can provide arguments to ttfautohint, quoted')
    parser.add_argument('--mti-source')
    parser.add_argument('--family-name', help='Family name to use for masters,'
                        ' and to filter output instances by')
    parser.add_argument('--use-afdko', action='store_true')
    parser.add_argument('--keep-direction', dest="reverse_direction",
                        action='store_false', help='Do not reverse contour '
                        'direction when output is ttf or ttf-interpolatable')
    parser.add_argument('--keep-overlaps', dest="remove_overlaps",
                        action='store_false',
                        help='Do not remove any overlap.')
    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--production-names', dest='use_production_names',
                       action='store_true', help='Rename glyphs with '
                       'production names if available otherwise use uninames.')
    group.add_argument('--no-production-names', dest='use_production_names',
                       action='store_false',
                       help='Do not rename glyphs with production names. '
                       'Keeps original glyph names')
    parser.set_defaults(use_production_names=None)
    parser.add_argument('--timing', action='store_true')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if not sum(1 for p in [glyphs_path, ufo_paths, designspace_path] if p) == 1:
        parser.error('Exactly one source type required (Glyphs, UFO, or '
                     'MutatorMath).')

    def exclude_args(parser, args, excluded_args):
        exclusive_msg = '"{}" argument only available for {} source'
        exclusive_src = {'interpolate': 'Glyphs or MutatorMath',
                         'family_name': 'Glyphs'}
        for excluded in excluded_args:
            if args[excluded]:
                parser.error(exclusive_msg.format(
                    excluded,
                    exclusive_src[excluded]))
            del args[excluded]

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)

    elif designspace_path:
        exclude_args(parser, args, ['family_name'])
        project.run_from_designspace(designspace_path, **args)

    else:
        exclude_args(parser, args, ['family_name', 'interpolate'])

    if ufo_paths:
        project.run_from_ufos(
            ufo_paths, is_instance=args.pop('masters_as_instances'), **args)
예제 #15
0
def main(args=None):
    parser = ArgumentParser()
    parser.add_argument('--version', action='version', version=__version__)
    inputGroup = parser.add_argument_group(
        title='Input arguments',
        description='The following arguments are mutually exclusive.')
    xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
    xInputGroup.add_argument('-g',
                             '--glyphs-path',
                             metavar='GLYPHS',
                             help='Path to .glyphs source file')
    xInputGroup.add_argument('-u',
                             '--ufo-paths',
                             nargs='+',
                             metavar='UFO',
                             help='One or more paths to UFO files')
    xInputGroup.add_argument('-m',
                             '--mm-designspace',
                             metavar='DESIGNSPACE',
                             help='Path to .designspace file')

    outputGroup = parser.add_argument_group(title='Output arguments')
    outputGroup.add_argument(
        '-o',
        '--output',
        nargs='+',
        default=('otf', 'ttf'),
        metavar="FORMAT",
        help='Output font formats. Choose between: %(choices)s. '
        'Default: otf, ttf',
        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable', 'variable'))
    outputSubGroup = outputGroup.add_mutually_exclusive_group()
    outputSubGroup.add_argument(
        '--output-path',
        default=None,
        help="Output font file path. Only valid when the output is a single "
        "file (e.g. input is a single UFO or output is variable font)")
    outputSubGroup.add_argument(
        '--output-dir',
        default=None,
        help="Output folder. By default, output folders are created in the "
        "current working directory, grouping output fonts by format.")
    outputGroup.add_argument(
        '-i',
        '--interpolate',
        nargs="?",
        default=False,
        const=True,
        metavar="INSTANCE_NAME",
        help='Interpolate masters and generate all the instances defined. '
        'To only interpolate a specific instance (or instances) that '
        'match a given "name" attribute, you can pass as argument '
        'the full instance name or a regular expression. '
        'E.g.: -i "Noto Sans Bold"; or -i ".* UI Condensed". '
        '(for Glyphs or MutatorMath sources only). ')
    outputGroup.add_argument('-M',
                             '--masters-as-instances',
                             action='store_true',
                             help='Output masters as instances')
    outputGroup.add_argument(
        '--family-name',
        help='Family name to use for masters, and to filter output instances')
    outputGroup.add_argument(
        '--round-instances',
        dest='round_instances',
        action='store_true',
        help='Apply integer rounding to all geometry when interpolating')
    outputGroup.add_argument(
        '--designspace-path',
        default=None,
        help='Path to output designspace file (for Glyphs sources only).')
    outputGroup.add_argument(
        '--master-dir',
        default=None,
        help='Directory where to write master UFO. Default: "./master_ufo". '
        'If value is "{tmp}", a temporary directory is created and '
        'removed at the end (for Glyphs sources only).')
    outputGroup.add_argument(
        '--instance-dir',
        default=None,
        help='Directory where to write instance UFOs. Default: '
        '"./instance_ufo". If value is "{tmp}", a temporary directory '
        'is created and removed at the end (for Glyphs sources only).')

    contourGroup = parser.add_argument_group(title='Handling of contours')
    contourGroup.add_argument('--keep-overlaps',
                              dest='remove_overlaps',
                              action='store_false',
                              help='Do not remove any overlap.')
    contourGroup.add_argument(
        '--keep-direction',
        dest='reverse_direction',
        action='store_false',
        help='Do not reverse contour direction when output is ttf or '
        'ttf-interpolatable')
    contourGroup.add_argument(
        '-e',
        '--conversion-error',
        type=float,
        default=None,
        metavar='ERROR',
        help='Maximum approximation error for cubic to quadratic conversion '
        'measured in EM')
    contourGroup.add_argument(
        '-a',
        '--autohint',
        nargs='?',
        const='',
        help='Run ttfautohint. Can provide arguments, quoted')
    contourGroup.add_argument(
        '--cff-round-tolerance',
        type=float,
        default=None,
        metavar='FLOAT',
        help='Restrict rounding of point coordinates in CFF table to only '
        'those floats whose absolute difference from their integral part '
        'is less than or equal to the tolerance. By default, all floats '
        'are rounded to integer (tolerance 0.5); 0 disables rounding.')

    layoutGroup = parser.add_argument_group(
        title='Handling of OpenType Layout')
    layoutGroup.add_argument(
        '--interpolate-binary-layout',
        nargs="?",
        default=False,
        const=True,
        metavar="MASTER_DIR",
        help='Interpolate layout tables from compiled master binaries. '
        'Requires Glyphs or MutatorMath source.')
    layoutGroup.add_argument(
        "--feature-writer",
        metavar="CLASS",
        action="append",
        dest="feature_writer_specs",
        help="string specifying a feature writer class to load, either "
        "built-in or from an external module, optionally initialized with "
        "the given keyword arguments. The class and module names are "
        "separated by '::'. The option can be repeated multiple times "
        "for each writer class. A special value of 'None' will disable "
        "all automatic feature generation. The option overrides both the "
        "default ufo2ft writers and those specified in the UFO lib.")

    feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
    feaCompilerGroup.add_argument(
        '--use-afdko',
        action='store_true',
        help='Use makeOTF instead of feaLib to compile FEA.')
    feaCompilerGroup.add_argument(
        '--mti-source',
        help='Path to mtiLib .txt feature definitions (use instead of FEA)')

    glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
    glyphnamesGroup.add_argument(
        '--production-names',
        dest='use_production_names',
        action='store_true',
        help='Rename glyphs with production names if available otherwise use '
        'uninames.')
    glyphnamesGroup.add_argument('--no-production-names',
                                 dest='use_production_names',
                                 action='store_false')

    subsetGroup = parser.add_mutually_exclusive_group(required=False)
    subsetGroup.add_argument(
        '--subset',
        dest='subset',
        action='store_true',
        help='Subset font using export flags set by glyphsLib')
    subsetGroup.add_argument('--no-subset',
                             dest='subset',
                             action='store_false')

    subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
    subroutinizeGroup.add_argument(
        '-s',
        '--subroutinize',
        action='store_true',
        help='Optimize CFF table using compreffor (default)')
    subroutinizeGroup.add_argument('-S',
                                   '--no-subroutinize',
                                   dest='subroutinize',
                                   action='store_false')

    parser.set_defaults(use_production_names=None,
                        subset=None,
                        subroutinize=True)

    logGroup = parser.add_argument_group(title='Logging arguments')
    logGroup.add_argument('--timing',
                          action='store_true',
                          help="Print the elapsed time for each steps")
    logGroup.add_argument(
        '--verbose',
        default='INFO',
        metavar='LEVEL',
        choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'),
        help='Configure the logger verbosity level. Choose between: '
        '%(choices)s. Default: INFO')
    args = vars(parser.parse_args(args))

    specs = args.pop("feature_writer_specs")
    if specs is not None:
        args["feature_writers"] = _loadFeatureWriters(parser, specs)

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    input_format = ("Glyphs" if glyphs_path else
                    "designspace" if designspace_path else "UFO") + " source"

    if 'variable' in args['output']:
        if not (glyphs_path or designspace_path):
            parser.error(
                'Glyphs or designspace source required for variable font')
        exclude_args(parser, args, [
            'interpolate', 'masters_as_instances', 'interpolate_binary_layout'
        ], "variable output")

    try:
        project = FontProject(timing=args.pop('timing'),
                              verbose=args.pop('verbose'))

        if glyphs_path:
            with _make_tempdirs(parser, args):
                project.run_from_glyphs(glyphs_path, **args)
            return

        exclude_args(parser, args, [
            'family_name', 'mti_source', 'designspace_path', 'master_dir',
            'instance_dir'
        ], input_format)
        if designspace_path:
            project.run_from_designspace(designspace_path, **args)
            return

        exclude_args(
            parser, args,
            ['interpolate', 'interpolate_binary_layout', 'round_instances'],
            input_format)
        project.run_from_ufos(ufo_paths,
                              is_instance=args.pop('masters_as_instances'),
                              **args)
    except FontmakeError as e:
        import sys
        sys.exit("fontmake: error: %s" % e)
예제 #16
0
def getRunArguments():
    u"""Arguments to be passed to a fontmake project run. The values below
    make Decovar build without errors. See also fontmake.__main__.py."""

    args = {
        'subset': None,
        'use_production_names': False,
        #'mark_writer_class': None,
        'reverse_direction': False,
        #'kern_writer_class': None,
        'interpolate_binary_layout': False,
        'remove_overlaps': True,
        'autohint': None,
        'conversion_error': None,
        #'no_round': False,
        'masters_as_instances': True,
        'interpolate': True,
        'use_afdko': False,
        'subroutinize': True,
        'output':['ttf'],
    }
    return args

project = FontProject()

args = getRunArguments()

print(project.run_from_designspace(designspace_path=DS_PATH, **args))

# fontPath = 'otf/' + DS_PATH.replace('.designspace', '.otf')
# os.system('open %s' % fontPath)
예제 #17
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument(
        '-o', '--output', nargs='+', default=('otf', 'ttf'),
        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable', 'variable'))

    parser.add_argument('-i', '--interpolate', action='store_true',
                        help='interpolate masters (for Glyphs or MutatorMath '
                             'sources only)')
    parser.add_argument('-M', '--masters-as-instances', action='store_true',
                        help='treat masters as instances')
    parser.add_argument('-a', '--autohint', nargs='?', const="",
                        help='can provide arguments to ttfautohint, quoted')
    parser.add_argument('--mti-source')
    parser.add_argument('--family-name', help='Family name to use for masters,'
                        ' and to filter output instances by')
    parser.add_argument('--use-afdko', action='store_true')
    parser.add_argument('--keep-direction', dest="reverse_direction",
                        action='store_false', help='Do not reverse contour '
                        'direction when output is ttf or ttf-interpolatable')
    parser.add_argument('--keep-overlaps', dest="remove_overlaps",
                        action='store_false',
                        help='Do not remove any overlap.')
    group1 = parser.add_mutually_exclusive_group(required=False)
    group1.add_argument('--production-names', dest='use_production_names',
                        action='store_true', help='Rename glyphs with '
                        'production names if available otherwise use uninames.')
    group1.add_argument('--no-production-names', dest='use_production_names',
                        action='store_false')
    group2 = parser.add_mutually_exclusive_group(required=False)
    group2.add_argument('--subset', dest='subset',
                        action='store_true', help='Subset font using export '
                        'flags set by glyphsLib')
    group2.add_argument('--no-subset', dest='subset', action='store_false')
    group3 = parser.add_mutually_exclusive_group(required=False)
    group3.add_argument('-s', '--subroutinize', action='store_true',
                        help='Optimize CFF table using compreffor (default)')
    group3.add_argument('-S', '--no-subroutinize', dest='subroutinize',
                        action='store_false')
    parser.add_argument('-e', '--conversion-error', type=float, default=None,
                        metavar='ERROR', help="Maximum approximation error for"
                        " cubic to quadratic conversion measured in EM")
    parser.set_defaults(use_production_names=None, subset=None,
                        subroutinize=True)
    parser.add_argument('--timing', action='store_true')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if not sum(1 for p in [glyphs_path, ufo_paths, designspace_path] if p) == 1:
        parser.error('Exactly one source type required (Glyphs, UFO, or '
                     'MutatorMath).')

    def exclude_args(parser, args, excluded_args):
        exclusive_msg = '"{}" argument only available for {} source'
        exclusive_src = {'interpolate': 'Glyphs or MutatorMath',
                         'family_name': 'Glyphs'}
        for excluded in excluded_args:
            if args[excluded]:
                parser.error(exclusive_msg.format(
                    excluded,
                    exclusive_src[excluded]))
            del args[excluded]

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)

    elif designspace_path:
        exclude_args(parser, args, ['family_name'])
        project.run_from_designspace(designspace_path, **args)

    else:
        exclude_args(parser, args, ['family_name', 'interpolate'])

    if ufo_paths:
        project.run_from_ufos(
            ufo_paths, is_instance=args.pop('masters_as_instances'), **args)
예제 #18
0
def main(args=None):
    parser = ArgumentParser()
    parser.add_argument('--version', action='version', version=__version__)
    inputGroup = parser.add_argument_group(
        title='Input arguments',
        description='The following arguments are mutually exclusive.')
    xInputGroup = inputGroup.add_mutually_exclusive_group(required=True)
    xInputGroup.add_argument(
        '-g', '--glyphs-path', metavar='GLYPHS',
        help='Path to .glyphs source file')
    xInputGroup.add_argument(
        '-u', '--ufo-paths', nargs='+', metavar='UFO',
        help='One or more paths to UFO files')
    xInputGroup.add_argument(
        '-m', '--mm-designspace', metavar='DESIGNSPACE',
        help='Path to .designspace file')

    outputGroup = parser.add_argument_group(title='Output arguments')
    outputGroup.add_argument(
        '-o', '--output', nargs='+', default=('otf', 'ttf'), metavar="FORMAT",
        help='Output font formats. Choose between: %(choices)s. '
             'Default: otf, ttf',
        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable', 'variable'))
    outputGroup.add_argument(
        '-i', '--interpolate', action='store_true',
        help='Interpolate masters (for Glyphs or MutatorMath sources only)')
    outputGroup.add_argument(
        '-M', '--masters-as-instances', action='store_true',
        help='Output masters as instances')
    outputGroup.add_argument(
        '--family-name',
        help='Family name to use for masters, and to filter output instances')

    contourGroup = parser.add_argument_group(title='Handling of contours')
    contourGroup.add_argument(
        '--keep-overlaps', dest='remove_overlaps', action='store_false',
        help='Do not remove any overlap.')
    contourGroup.add_argument(
        '--keep-direction', dest='reverse_direction', action='store_false',
        help='Do not reverse contour direction when output is ttf or '
             'ttf-interpolatable')
    contourGroup.add_argument(
        '-e', '--conversion-error', type=float, default=None, metavar='ERROR',
        help='Maximum approximation error for cubic to quadratic conversion '
             'measured in EM')
    contourGroup.add_argument(
        '-a', '--autohint', nargs='?', const='',
        help='Run ttfautohint. Can provide arguments, quoted')

    layoutGroup = parser.add_argument_group(title='Handling of OpenType Layout')
    layoutGroup.add_argument(
        '--interpolate-binary-layout', action='store_true',
        help='Interpolate layout tables from compiled master binaries. '
             'Requires Glyphs or MutatorMath source.')
    layoutGroup.add_argument(
        '--kern-writer-module', metavar="MODULE", dest='kern_writer_class',
        type=PyClassType('KernFeatureWriter'),
        help='Module containing a custom `KernFeatureWriter` class.')
    layoutGroup.add_argument(
        '--mark-writer-module', metavar="MODULE", dest='mark_writer_class',
        type=PyClassType('MarkFeatureWriter'),
        help='Module containing a custom `MarkFeatureWriter` class.')
    feaCompilerGroup = layoutGroup.add_mutually_exclusive_group(required=False)
    feaCompilerGroup.add_argument(
        '--use-afdko', action='store_true',
        help='Use makeOTF instead of feaLib to compile FEA.')
    feaCompilerGroup.add_argument(
        '--mti-source',
        help='Path to mtiLib .txt feature definitions (use instead of FEA)')

    glyphnamesGroup = parser.add_mutually_exclusive_group(required=False)
    glyphnamesGroup.add_argument(
        '--production-names', dest='use_production_names', action='store_true',
        help='Rename glyphs with production names if available otherwise use '
             'uninames.')
    glyphnamesGroup.add_argument(
        '--no-production-names', dest='use_production_names',
        action='store_false')

    subsetGroup = parser.add_mutually_exclusive_group(required=False)
    subsetGroup.add_argument(
        '--subset', dest='subset', action='store_true',
        help='Subset font using export flags set by glyphsLib')
    subsetGroup.add_argument(
        '--no-subset', dest='subset', action='store_false')

    subroutinizeGroup = parser.add_mutually_exclusive_group(required=False)
    subroutinizeGroup.add_argument(
        '-s', '--subroutinize', action='store_true',
        help='Optimize CFF table using compreffor (default)')
    subroutinizeGroup.add_argument(
        '-S', '--no-subroutinize', dest='subroutinize', action='store_false')

    parser.set_defaults(use_production_names=None, subset=None,
                        subroutinize=True)

    logGroup = parser.add_argument_group(title='Logging arguments')
    logGroup.add_argument(
        '--timing', action='store_true',
        help="Print the elapsed time for each steps")
    logGroup.add_argument(
        '--verbose', default='INFO', metavar='LEVEL',
        choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'),
        help='Configure the logger verbosity level. Choose between: '
             '%(choices)s. Default: INFO')
    args = vars(parser.parse_args(args))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')

    if 'variable' in args['output']:
        if not (glyphs_path or designspace_path):
            parser.error(
                'Glyphs or designspace source required for variable font')
        for argname in ('interpolate', 'masters_as_instances',
                        'interpolate_binary_layout'):
            if args[argname]:
                parser.error('--%s option invalid for variable font'
                             % argname.replace("_", "-"))

    project = FontProject(timing=args.pop('timing'),
                          verbose=args.pop('verbose'))

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)
        return

    exclude_args(parser, args, ['family_name', 'mti_source'], 'Glyphs')
    if designspace_path:
        project.run_from_designspace(designspace_path, **args)
        return

    exclude_args(
        parser, args, ['interpolate', 'interpolate_binary_layout'],
        'Glyphs or MutatorMath')
    project.run_from_ufos(
        ufo_paths, is_instance=args.pop('masters_as_instances'), **args)
예제 #19
0
def main():
    parser = ArgumentParser()
    parser.add_argument('-g', '--glyphs-path')
    parser.add_argument('-u', '--ufo-paths', nargs='+')
    parser.add_argument('-m', '--mm-designspace')
    parser.add_argument(
        '-o', '--output', nargs='+', default=('otf', 'ttf'),
        choices=('ufo', 'otf', 'ttf', 'ttf-interpolatable', 'variable'))

    parser.add_argument(
        '-i', '--interpolate', action='store_true',
        help='Interpolate masters (for Glyphs or MutatorMath sources only)')
    parser.add_argument(
        '-M', '--masters-as-instances', action='store_true',
        help='Output masters as instances')
    parser.add_argument(
        '--family-name',
        help='Family name to use for masters, and to filter output instances')

    parser.add_argument(
        '--mti-source')
    parser.add_argument(
        '--interpolate-binary-layout', action='store_true',
        help='Interpolate layout tables from compiled master binaries. '
             'Requires Glyphs or MutatorMath source.')
    parser.add_argument(
        '--use-afdko', action='store_true')

    parser.add_argument(
        '-a', '--autohint', nargs='?', const='',
        help='Run ttfautohint. Can provide arguments, quoted')
    parser.add_argument(
        '--keep-direction', dest='reverse_direction', action='store_false',
        help='Do not reverse contour direction when output is ttf or '
             'ttf-interpolatable')
    parser.add_argument(
        '--keep-overlaps', dest='remove_overlaps', action='store_false',
        help='Do not remove any overlap.')

    group1 = parser.add_mutually_exclusive_group(required=False)
    group1.add_argument(
        '--production-names', dest='use_production_names', action='store_true',
        help='Rename glyphs with production names if available otherwise use '
             'uninames.')
    group1.add_argument(
        '--no-production-names', dest='use_production_names',
        action='store_false')

    group2 = parser.add_mutually_exclusive_group(required=False)
    group2.add_argument(
        '--subset', dest='subset', action='store_true',
        help='Subset font using export flags set by glyphsLib')
    group2.add_argument(
        '--no-subset', dest='subset', action='store_false')

    group3 = parser.add_mutually_exclusive_group(required=False)
    group3.add_argument(
        '-s', '--subroutinize', action='store_true',
        help='Optimize CFF table using compreffor (default)')
    group3.add_argument(
        '-S', '--no-subroutinize', dest='subroutinize', action='store_false')
    parser.add_argument(
        '-e', '--conversion-error', type=float, default=None, metavar='ERROR',
        help='Maximum approximation error for cubic to quadratic conversion '
             'measured in EM')

    parser.set_defaults(use_production_names=None, subset=None,
                        subroutinize=True)

    parser.add_argument('--timing', action='store_true')
    parser.add_argument('--verbose', default='INFO')
    args = vars(parser.parse_args())

    project = FontProject(timing=args.pop('timing'),
                          verbose=args.pop('verbose'))

    glyphs_path = args.pop('glyphs_path')
    ufo_paths = args.pop('ufo_paths')
    designspace_path = args.pop('mm_designspace')
    if sum(1 for p in [glyphs_path, ufo_paths, designspace_path] if p) != 1:
        parser.error('Exactly one source type required (Glyphs, UFO, or '
                     'MutatorMath).')

    def exclude_args(parser, args, excluded_args, source_name):
        msg = '"%s" argument only available for %s source'
        for excluded in excluded_args:
            if args[excluded]:
                parser.error(msg % (excluded, source_name))
            del args[excluded]

    if glyphs_path:
        project.run_from_glyphs(glyphs_path, **args)
        return

    exclude_args(parser, args, ['family_name'], 'Glyphs')
    if designspace_path:
        project.run_from_designspace(designspace_path, **args)
        return

    exclude_args(
        parser, args, ['interpolate', 'interpolate_binary_layout'],
        'Glyphs or MutatorMath')
    project.run_from_ufos(
        ufo_paths, is_instance=args.pop('masters_as_instances'), **args)