Bug#1058388: fontmake: FTBFS: dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" returned exit code 13
Source: fontmake
Version: 2.4.1-2
Severity: serious
Justification: FTBFS
Tags: trixie sid ftbfs
User: lucas@debian.org
Usertags: ftbfs-20231212 ftbfs-trixie
Hi,
During a rebuild of all packages in sid, your package failed to build
on amd64.
Relevant part (hopefully):
> make[1]: Entering directory '/<<PKGBUILDDIR>>'
> dh_auto_test
> I: pybuild base:310: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build; python3.12 -m pytest tests
> ============================= test session starts ==============================
> platform linux -- Python 3.12.1, pytest-7.4.3, pluggy-1.3.0
> rootdir: /<<PKGBUILDDIR>>
> configfile: setup.cfg
> collected 52 items
>
> tests/test_instantiator.py ............................ [ 53%]
> tests/test_main.py FFFF.....FFFFF.FFFF..F.. [100%]
>
> =================================== FAILURES ===================================
> ______________________________ test_interpolation ______________________________
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07c0a930>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07c76360>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> > self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
>
> fontmake/font_project.py:946:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/font_project.py:1006: in _run_from_designspace_static
> ufos.extend(
> fontmake/font_project.py:744: in interpolate_instance_ufos
> from glyphsLib.interpolation import apply_instance_data_to_ufo
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
>
> The above exception was the direct cause of the following exception:
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> > project.run_from_designspace(designspace_path, **args)
>
> fontmake/__main__.py:501:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07c0a930>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07c76360>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
> if interp_outputs:
> self._run_from_designspace_interpolatable(
> designspace,
> outputs=interp_outputs,
> feature_writers=feature_writers,
> filters=filters,
> **kwargs,
> )
> except FontmakeError as e:
> # Some called functions already added the Designspace file to the source
> # trail.
> if e.source_trail[-1] != designspace.path:
> e.source_trail.append(designspace.path)
> raise
> except Exception as e:
> > raise FontmakeError(
> "Generating fonts from Designspace failed", designspace.path
> ) from e
> E fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/font_project.py:974: FontmakeError
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation0')
>
> def test_interpolation(data_dir, tmp_path):
> shutil.copytree(data_dir / "DesignspaceTest", tmp_path / "sources")
>
> > fontmake.__main__.main(
> [
> "-m",
> str(tmp_path / "sources" / "DesignspaceTest.designspace"),
> "-i",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:14:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> project.run_from_ufos(
> 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)}")
> E SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/__main__.py:523: SystemExit
> ________________________ test_interpolation_mutatormath ________________________
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07be5f40>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07b08770>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> > self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
>
> fontmake/font_project.py:946:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/font_project.py:998: in _run_from_designspace_static
> self.interpolate_instance_ufos_mutatormath(
> fontmake/font_project.py:832: in interpolate_instance_ufos_mutatormath
> from glyphsLib.interpolation import apply_instance_data
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
>
> The above exception was the direct cause of the following exception:
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> > project.run_from_designspace(designspace_path, **args)
>
> fontmake/__main__.py:501:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07be5f40>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d07b08770>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
> if interp_outputs:
> self._run_from_designspace_interpolatable(
> designspace,
> outputs=interp_outputs,
> feature_writers=feature_writers,
> filters=filters,
> **kwargs,
> )
> except FontmakeError as e:
> # Some called functions already added the Designspace file to the source
> # trail.
> if e.source_trail[-1] != designspace.path:
> e.source_trail.append(designspace.path)
> raise
> except Exception as e:
> > raise FontmakeError(
> "Generating fonts from Designspace failed", designspace.path
> ) from e
> E fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/font_project.py:974: FontmakeError
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0')
>
> def test_interpolation_mutatormath(data_dir, tmp_path):
> shutil.copytree(data_dir / "DesignspaceTest", tmp_path / "sources")
>
> > fontmake.__main__.main(
> [
> "-m",
> str(tmp_path / "sources" / "DesignspaceTest.designspace"),
> "-i",
> "--use-mutatormath",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:48:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> project.run_from_ufos(
> 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)}")
> E SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/__main__.py:523: SystemExit
> _________________ test_interpolation_mutatormath_source_layer __________________
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07be4620>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d079576e0>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> > self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
>
> fontmake/font_project.py:946:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/font_project.py:998: in _run_from_designspace_static
> self.interpolate_instance_ufos_mutatormath(
> fontmake/font_project.py:832: in interpolate_instance_ufos_mutatormath
> from glyphsLib.interpolation import apply_instance_data
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
>
> The above exception was the direct cause of the following exception:
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> > project.run_from_designspace(designspace_path, **args)
>
> fontmake/__main__.py:501:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07be4620>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = True
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d079576e0>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
> if interp_outputs:
> self._run_from_designspace_interpolatable(
> designspace,
> outputs=interp_outputs,
> feature_writers=feature_writers,
> filters=filters,
> **kwargs,
> )
> except FontmakeError as e:
> # Some called functions already added the Designspace file to the source
> # trail.
> if e.source_trail[-1] != designspace.path:
> e.source_trail.append(designspace.path)
> raise
> except Exception as e:
> > raise FontmakeError(
> "Generating fonts from Designspace failed", designspace.path
> ) from e
> E fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/font_project.py:974: FontmakeError
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1')
>
> def test_interpolation_mutatormath_source_layer(data_dir, tmp_path):
> shutil.copyfile(
> data_dir / "MutatorSans" / "MutatorSans.designspace",
> tmp_path / "MutatorSans.designspace",
> )
>
> with pytest.raises(SystemExit, match="sources with 'layer'"):
> > fontmake.__main__.main(
> [
> "-m",
> str(tmp_path / "MutatorSans.designspace"),
> "-i",
> "--use-mutatormath",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:87:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> project.run_from_ufos(
> 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)}")
> E SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/__main__.py:523: SystemExit
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1')
>
> def test_interpolation_mutatormath_source_layer(data_dir, tmp_path):
> shutil.copyfile(
> data_dir / "MutatorSans" / "MutatorSans.designspace",
> tmp_path / "MutatorSans.designspace",
> )
>
> > with pytest.raises(SystemExit, match="sources with 'layer'"):
> E AssertionError: Regex pattern did not match.
> E Regex: "sources with 'layer'"
> E Input: "fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_mutatormath1/MutatorSans.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'"
>
> tests/test_main.py:86: AssertionError
> _________________ test_interpolation_and_masters_as_instances __________________
>
> self = <fontmake.font_project.FontProject object at 0x7f3d077633e0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = True
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077e4620>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> > self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
>
> fontmake/font_project.py:946:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/font_project.py:1006: in _run_from_designspace_static
> ufos.extend(
> fontmake/font_project.py:744: in interpolate_instance_ufos
> from glyphsLib.interpolation import apply_instance_data_to_ufo
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
>
> The above exception was the direct cause of the following exception:
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> > project.run_from_designspace(designspace_path, **args)
>
> fontmake/__main__.py:501:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> self = <fontmake.font_project.FontProject object at 0x7f3d077633e0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace'
> output = ('otf', 'ttf'), interpolate = True, masters_as_instances = True
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = False
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'otf', 'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077e4620>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
> if interp_outputs:
> self._run_from_designspace_interpolatable(
> designspace,
> outputs=interp_outputs,
> feature_writers=feature_writers,
> filters=filters,
> **kwargs,
> )
> except FontmakeError as e:
> # Some called functions already added the Designspace file to the source
> # trail.
> if e.source_trail[-1] != designspace.path:
> e.source_trail.append(designspace.path)
> raise
> except Exception as e:
> > raise FontmakeError(
> "Generating fonts from Designspace failed", designspace.path
> ) from e
> E fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/font_project.py:974: FontmakeError
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0')
>
> def test_interpolation_and_masters_as_instances(data_dir, tmp_path):
> shutil.copytree(data_dir / "DesignspaceTest", tmp_path / "sources")
>
> > fontmake.__main__.main(
> [
> "-m",
> str(tmp_path / "sources" / "DesignspaceTest.designspace"),
> "-i",
> "-M",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:102:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> project.run_from_ufos(
> 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)}")
> E SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_interpolation_and_masters0/sources/DesignspaceTest.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/__main__.py:523: SystemExit
> ____________________________ test_ufo_interpolation ____________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_ufo_interpolation0')
>
> def test_ufo_interpolation(data_dir, tmp_path):
> shutil.copyfile(
> data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
> )
>
> instance_dir = tmp_path / "instance_ufos"
> > fontmake.__main__.main(
> [
> "-g",
> str(tmp_path / "GlyphsUnitTestSans.glyphs"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "--instance-dir",
> str(instance_dir),
> "-i",
> "-o",
> "ufo",
> ]
> )
>
> tests/test_main.py:256:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> _______________________ test_ufo_interpolation_specific ________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_ufo_interpolation_specifi0')
>
> def test_ufo_interpolation_specific(data_dir, tmp_path):
> shutil.copyfile(
> data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
> )
>
> instance_dir = tmp_path / "instance_ufos"
> > fontmake.__main__.main(
> [
> "-g",
> str(tmp_path / "GlyphsUnitTestSans.glyphs"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "--instance-dir",
> str(instance_dir),
> "-i",
> r".*Light.*",
> "-o",
> "ufo",
> ]
> )
>
> tests/test_main.py:288:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________________ test_subsetting[default] ___________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_subsetting_default_0')
> write_skipexportglyphs = True
>
> @pytest.mark.parametrize(
> "write_skipexportglyphs",
> [
> pytest.param(True, id="default"),
> pytest.param(False, id="no-write-skipexportglyphs"),
> ],
> )
> def test_subsetting(data_dir, tmp_path, write_skipexportglyphs):
> shutil.copyfile(data_dir / "TestSubset.glyphs", tmp_path / "TestSubset.glyphs")
>
> args = [
> "-g",
> str(tmp_path / "TestSubset.glyphs"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "--instance-dir",
> str(tmp_path / "instance_ufos"),
> "-i",
> "Test Subset Regular",
> "-o",
> "ttf",
> "otf",
> "--output-dir",
> str(tmp_path),
> ]
> if not write_skipexportglyphs:
> args.append("--no-write-skipexportglyphs")
>
> > fontmake.__main__.main(args)
>
> tests/test_main.py:337:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> __________________ test_subsetting[no-write-skipexportglyphs] __________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_subsetting_no_write_skipe0')
> write_skipexportglyphs = False
>
> @pytest.mark.parametrize(
> "write_skipexportglyphs",
> [
> pytest.param(True, id="default"),
> pytest.param(False, id="no-write-skipexportglyphs"),
> ],
> )
> def test_subsetting(data_dir, tmp_path, write_skipexportglyphs):
> shutil.copyfile(data_dir / "TestSubset.glyphs", tmp_path / "TestSubset.glyphs")
>
> args = [
> "-g",
> str(tmp_path / "TestSubset.glyphs"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "--instance-dir",
> str(tmp_path / "instance_ufos"),
> "-i",
> "Test Subset Regular",
> "-o",
> "ttf",
> "otf",
> "--output-dir",
> str(tmp_path),
> ]
> if not write_skipexportglyphs:
> args.append("--no-write-skipexportglyphs")
>
> > fontmake.__main__.main(args)
>
> tests/test_main.py:337:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ________________________ test_shared_features_expansion ________________________
>
> self = <fontmake.font_project.FontProject object at 0x7f3d077a14c0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace'
> output = ['ttf'], interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = True
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077a1280>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> > self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
>
> fontmake/font_project.py:946:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/font_project.py:1006: in _run_from_designspace_static
> ufos.extend(
> fontmake/font_project.py:744: in interpolate_instance_ufos
> from glyphsLib.interpolation import apply_instance_data_to_ufo
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
>
> The above exception was the direct cause of the following exception:
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> > project.run_from_designspace(designspace_path, **args)
>
> fontmake/__main__.py:501:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> self = <fontmake.font_project.FontProject object at 0x7f3d077a14c0>
> designspace_path = '/tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace'
> output = ['ttf'], interpolate = True, masters_as_instances = False
> interpolate_binary_layout = False, round_instances = False
> feature_writers = None, filters = None, expand_features_to_instances = True
> use_mutatormath = False
> kwargs = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
> interp_outputs = frozenset(), static_outputs = {'ttf'}
> designspace = <fontTools.designspaceLib.DesignSpaceDocument object at 0x7f3d077a1280>
>
> def run_from_designspace(
> self,
> designspace_path,
> output=(),
> interpolate=False,
> masters_as_instances=False,
> interpolate_binary_layout=False,
> round_instances=False,
> feature_writers=None,
> filters=None,
> expand_features_to_instances=False,
> use_mutatormath=False,
> **kwargs,
> ):
> """Run toolchain from a DesignSpace document to produce either static
> instance fonts (ttf or otf), interpolatable or variable fonts.
>
> Args:
> designspace_path: Path to designspace document.
> interpolate: If True output all instance fonts, otherwise just
> masters. If the value is a string, only build instance(s) that
> match given name. The string is compiled into a regular
> expression and matched against the "name" attribute of
> designspace instances using `re.fullmatch`.
> masters_as_instances: If True, output master fonts as instances.
> interpolate_binary_layout: Interpolate layout tables from compiled
> master binaries.
> round_instances: apply integer rounding when interpolating with
> MutatorMath.
> kwargs: Arguments passed along to run_from_ufos.
>
> Raises:
> TypeError: "variable" or "interpolatable" outputs are incompatible
> with arguments "interpolate", "masters_as_instances", and
> "interpolate_binary_layout".
> """
>
> interp_outputs = INTERPOLATABLE_OUTPUTS.intersection(output)
> static_outputs = set(output).difference(interp_outputs)
> if interp_outputs:
> for argname in (
> "interpolate",
> "masters_as_instances",
> "interpolate_binary_layout",
> ):
> if locals()[argname]:
> raise TypeError(
> '"%s" argument incompatible with output %r'
> % (argname, ", ".join(sorted(interp_outputs)))
> )
>
> try:
> designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path)
> except Exception as e:
> raise FontmakeError("Reading Designspace failed", designspace_path) from e
>
> # if no --feature-writers option was passed, check in the designspace's
> # <lib> element if user supplied a custom featureWriters configuration;
> # if so, use that for all the UFOs built from this designspace
> if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib:
> feature_writers = loadFeatureWriters(designspace)
>
> if filters is None and FILTERS_KEY in designspace.lib:
> preFilters, postFilters = loadFilters(designspace)
> filters = preFilters + postFilters
>
> try:
> if static_outputs:
> self._run_from_designspace_static(
> designspace,
> outputs=static_outputs,
> interpolate=interpolate,
> masters_as_instances=masters_as_instances,
> interpolate_binary_layout=interpolate_binary_layout,
> round_instances=round_instances,
> feature_writers=feature_writers,
> expand_features_to_instances=expand_features_to_instances,
> use_mutatormath=use_mutatormath,
> filters=filters,
> **kwargs,
> )
> if interp_outputs:
> self._run_from_designspace_interpolatable(
> designspace,
> outputs=interp_outputs,
> feature_writers=feature_writers,
> filters=filters,
> **kwargs,
> )
> except FontmakeError as e:
> # Some called functions already added the Designspace file to the source
> # trail.
> if e.source_trail[-1] != designspace.path:
> e.source_trail.append(designspace.path)
> raise
> except Exception as e:
> > raise FontmakeError(
> "Generating fonts from Designspace failed", designspace.path
> ) from e
> E fontmake.errors.FontmakeError: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/font_project.py:974: FontmakeError
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0')
>
> def test_shared_features_expansion(data_dir, tmp_path):
> shutil.copytree(data_dir / "DesignspaceTestSharedFeatures", tmp_path / "sources")
>
> > fontmake.__main__.main(
> [
> "-m",
> str(tmp_path / "sources" / "DesignspaceTestSharedFeatures.designspace"),
> "-i",
> "--expand-features-to-instances",
> "-o",
> "ttf",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:348:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> project.run_from_ufos(
> 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)}")
> E SystemExit: fontmake: Error: In '../../../../../../tmp/pytest-of-user42/pytest-7/test_shared_features_expansion0/sources/DesignspaceTestSharedFeatures.designspace': Generating fonts from Designspace failed: No module named 'openstep_plist.parser'
>
> fontmake/__main__.py:523: SystemExit
> _______________________________ test_mti_sources _______________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_mti_sources0')
>
> def test_mti_sources(data_dir, tmp_path):
> shutil.copytree(data_dir / "InterpolateLayoutTest", tmp_path / "sources")
>
> > fontmake.__main__.main(
> [
> "-g",
> str(tmp_path / "sources" / "InterpolateLayoutTest.glyphs"),
> "--designspace-path",
> str(tmp_path / "InterpolateLayoutTest.designspace"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "--instance-dir",
> str(tmp_path / "instance_ufos"),
> "--mti-source",
> str(tmp_path / "sources" / "InterpolateLayoutTest.plist"),
> "--no-production-names",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:391:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________________ test_interpolate_layout ____________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_interpolate_layout0')
>
> def test_interpolate_layout(data_dir, tmp_path):
> shutil.copytree(data_dir / "InterpolateLayoutTest", tmp_path / "sources")
>
> > fontmake.__main__.main(
> [
> "-g",
> str(tmp_path / "sources" / "InterpolateLayoutTest.glyphs"),
> "--designspace-path",
> str(tmp_path / "InterpolateLayoutTest.designspace"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "--instance-dir",
> str(tmp_path / "instance_ufos"),
> "--mti-source",
> str(tmp_path / "sources" / "InterpolateLayoutTest.plist"),
> "--no-production-names",
> "-o",
> "ttf",
> "--output-dir",
> str(tmp_path / "master_ttf"),
> ]
> )
>
> tests/test_main.py:445:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> _________________________ test_write_skipexportglyphs __________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_write_skipexportglyphs0')
>
> def test_write_skipexportglyphs(data_dir, tmp_path):
> shutil.copyfile(
> data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
> )
>
> args = [
> "-g",
> str(tmp_path / "GlyphsUnitTestSans.glyphs"),
> "--master-dir",
> str(tmp_path / "master_ufos"),
> "-o",
> "ufo",
> ]
> > fontmake.__main__.main(args)
>
> tests/test_main.py:572:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________________ test_debug_feature_file ____________________________
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_debug_feature_file0')
>
> def test_debug_feature_file(data_dir, tmp_path):
> shutil.copyfile(
> data_dir / "GlyphsUnitTestSans.glyphs", tmp_path / "GlyphsUnitTestSans.glyphs"
> )
>
> debug_feature_path = data_dir / "test.fea"
>
> > fontmake.__main__.main(
> [
> "-g",
> str(tmp_path / "GlyphsUnitTestSans.glyphs"),
> "--master-dir",
> "{tmp}",
> "--instance-dir",
> "{tmp}",
> "-i",
> "-o",
> "ttf",
> "--debug-feature-file",
> str(debug_feature_path),
> ]
> )
>
> tests/test_main.py:610:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/__main__.py:482: in main
> project.run_from_glyphs(glyphs_path, **args)
> fontmake/font_project.py:700: in run_from_glyphs
> designspace_path = self.build_master_ufos(
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:144: in build_master_ufos
> import glyphsLib
> /usr/lib/python3/dist-packages/glyphsLib/__init__.py:21: in <module>
> from glyphsLib.classes import GSFont, __all__ as __all_classes__
> /usr/lib/python3/dist-packages/glyphsLib/classes.py:26: in <module>
> import openstep_plist
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> > from .parser import load, loads, ParseError
> E ModuleNotFoundError: No module named 'openstep_plist.parser'
>
> /usr/lib/python3/dist-packages/openstep_plist/__init__.py:1: ModuleNotFoundError
> ___________________ test_static_otf_compreffor_subroutinizer ___________________
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07000800>
> ufos = [<ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>]
> ttf = False, debugFeatureFile = None
> kwargs = {'cffVersion': 1, 'cubicConversionError': None, 'featureWriters': None, 'filters': None, ...}
> options = {'cffVersion': 1, 'featureWriters': None, 'filters': None, 'inplace': True, ...}
> key = 'flattenComponents'
> compile_func = <function compileOTF at 0x7f3d07bd5440>, fmt = 'OTF'
> writeFontName = False
> ufo = <ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>
> name = 'MyFont-Light'
>
> def _iter_compile(self, ufos, ttf=False, debugFeatureFile=None, **kwargs):
> # generator function that calls ufo2ft compiler for each ufo and
> # yields ttFont instances
> options = dict(kwargs)
> if ttf:
> for key in ("optimizeCFF", "roundTolerance", "subroutinizer", "cffVersion"):
> options.pop(key, None)
> compile_func, fmt = ufo2ft.compileTTF, "TTF"
> else:
> for key in (
> "cubicConversionError",
> "reverseDirection",
> "flattenComponents",
> ):
> options.pop(key, None)
> compile_func, fmt = ufo2ft.compileOTF, "OTF"
>
> writeFontName = len(ufos) > 1
> for ufo in ufos:
> name = self._font_name(ufo)
> logger.info(f"Building {fmt} for {name}")
>
> if debugFeatureFile and writeFontName:
> debugFeatureFile.write(f"\n### {name} ###\n")
>
> try:
> > yield compile_func(ufo, debugFeatureFile=debugFeatureFile, **options)
>
> fontmake/font_project.py:383:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> /usr/lib/python3/dist-packages/ufo2ft/__init__.py:201: in compileOTF
> return call_postprocessor(
> /usr/lib/python3/dist-packages/ufo2ft/__init__.py:88: in call_postprocessor
> otf = postProcessor.process(**kwargs)
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:98: in process
> self.process_cff(
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:123: in process_cff
> self._subroutinize(backend, self.otf, cffOutputVersion)
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:331: in _subroutinize
> subroutinize(otf, cffVersion)
> /usr/lib/python3/dist-packages/ufo2ft/postProcessor.py:335: in _subroutinize_with_compreffor
> from compreffor import compress
> /usr/lib/python3/dist-packages/compreffor/__init__.py:68: in <module>
> from compreffor import cxxCompressor, pyCompressor
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> #!/usr/bin/env python
> #
> # Copyright 2015 Google Inc. All Rights Reserved.
> #
> # Licensed under the Apache License, Version 2.0 (the "License");
> # you may not use this file except in compliance with the License.
> # You may obtain a copy of the License at
> #
> # http://www.apache.org/licenses/LICENSE-2.0
> #
> # Unless required by applicable law or agreed to in writing, software
> # distributed under the License is distributed on an "AS IS" BASIS,
> # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> # See the License for the specific language governing permissions and
> # limitations under the License.
>
> """
> Tool to subroutinize a CFF OpenType font. Backed by a C++ binary.
>
> This file is a bootstrap for the C++ edition of the FontTools compreffor.
> It prepares the input data for the extension and reads back in the results,
> applying them to the input font.
>
> Usage (command line):
> >> ./cxxCompressor.py /path/to/font.otf
> # font written to /path/to/font.compressed.otf
>
> Usage (python):
> >> font = TTFont("/path/to/font.otf")
> >> cxxCompressor.compreff(font)
> >> font.save("/path/to/output.otf")
> """
>
> import array
> from io import BytesIO
> import struct
> import logging
> from compreffor.pyCompressor import (
> Compreffor, CandidateSubr, tokenCost)
> > from compreffor import _compreffor as lib, timer
> E ImportError: cannot import name '_compreffor' from partially initialized module 'compreffor' (most likely due to a circular import) (/usr/lib/python3/dist-packages/compreffor/__init__.py)
>
> /usr/lib/python3/dist-packages/compreffor/cxxCompressor.py:40: ImportError
>
> The above exception was the direct cause of the following exception:
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> > project.run_from_ufos(
> ufo_paths, is_instance=args.pop("masters_as_instances"), **args
> )
>
> fontmake/__main__.py:516:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> fontmake/font_project.py:1095: in run_from_ufos
> self.build_otfs(ufos, cff_version=cff_version, **kwargs)
> fontmake/font_project.py:223: in build_otfs
> self.save_otfs(ufos, **kwargs)
> /usr/lib/python3/dist-packages/fontTools/misc/loggingTools.py:375: in wrapper
> return func(*args, **kwds)
> fontmake/font_project.py:521: in save_otfs
> for font, ufo in zip(fonts, ufos):
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> self = <fontmake.font_project.FontProject object at 0x7f3d07000800>
> ufos = [<ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>]
> ttf = False, debugFeatureFile = None
> kwargs = {'cffVersion': 1, 'cubicConversionError': None, 'featureWriters': None, 'filters': None, ...}
> options = {'cffVersion': 1, 'featureWriters': None, 'filters': None, 'inplace': True, ...}
> key = 'flattenComponents'
> compile_func = <function compileOTF at 0x7f3d07bd5440>, fmt = 'OTF'
> writeFontName = False
> ufo = <ufoLib2.objects.font.Font 'MyFont Light' at 0x7f3d076fed50>
> name = 'MyFont-Light'
>
> def _iter_compile(self, ufos, ttf=False, debugFeatureFile=None, **kwargs):
> # generator function that calls ufo2ft compiler for each ufo and
> # yields ttFont instances
> options = dict(kwargs)
> if ttf:
> for key in ("optimizeCFF", "roundTolerance", "subroutinizer", "cffVersion"):
> options.pop(key, None)
> compile_func, fmt = ufo2ft.compileTTF, "TTF"
> else:
> for key in (
> "cubicConversionError",
> "reverseDirection",
> "flattenComponents",
> ):
> options.pop(key, None)
> compile_func, fmt = ufo2ft.compileOTF, "OTF"
>
> writeFontName = len(ufos) > 1
> for ufo in ufos:
> name = self._font_name(ufo)
> logger.info(f"Building {fmt} for {name}")
>
> if debugFeatureFile and writeFontName:
> debugFeatureFile.write(f"\n### {name} ###\n")
>
> try:
> yield compile_func(ufo, debugFeatureFile=debugFeatureFile, **options)
> except Exception as e:
> > raise FontmakeError("Compiling UFO failed", ufo.path) from e
> E fontmake.errors.FontmakeError: In 'tests/data/DesignspaceTest/MyFont-Light.ufo': Compiling UFO failed: cannot import name '_compreffor' from partially initialized module 'compreffor' (most likely due to a circular import) (/usr/lib/python3/dist-packages/compreffor/__init__.py)
>
> fontmake/font_project.py:385: FontmakeError
>
> During handling of the above exception, another exception occurred:
>
> data_dir = PosixPath('/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build/tests/data')
> tmp_path = PosixPath('/tmp/pytest-of-user42/pytest-7/test_static_otf_compreffor_sub0')
>
> def test_static_otf_compreffor_subroutinizer(data_dir, tmp_path):
> > fontmake.__main__.main(
> [
> "-u",
> str(data_dir / "DesignspaceTest" / "MyFont-Light.ufo"),
> "-o",
> "otf",
> "--subroutinizer",
> "compreffor",
> "--output-dir",
> str(tmp_path),
> ]
> )
>
> tests/test_main.py:666:
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
>
> args = {'autohint': None, 'cff_round_tolerance': None, 'conversion_error': None, 'debug_feature_file': None, ...}
>
> 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 (pick only one):",
> )
> 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 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(
> "--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)
>
> 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",
> "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 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,
> )
> exclude_args(
> parser, args, ["write_skipexportglyphs"], input_format, positive=False
> )
> if designspace_path:
> project.run_from_designspace(designspace_path, **args)
> return
>
> exclude_args(
> parser,
> args,
> [
> "interpolate",
> "use_mutatormath",
> "interpolate_binary_layout",
> "round_instances",
> "expand_features_to_instances",
> ],
> input_format,
> )
> project.run_from_ufos(
> 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)}")
> E SystemExit: fontmake: Error: In 'tests/data/DesignspaceTest/MyFont-Light.ufo': Compiling UFO failed: cannot import name '_compreffor' from partially initialized module 'compreffor' (most likely due to a circular import) (/usr/lib/python3/dist-packages/compreffor/__init__.py)
>
> fontmake/__main__.py:523: SystemExit
> =============================== warnings summary ===============================
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
> /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
> __import__("pkg_resources").declare_namespace(__name__) # type: ignore
>
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
> /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
> Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
> __import__("pkg_resources").declare_namespace(__name__) # type: ignore
>
> ../../../../../../usr/lib/python3/dist-packages/fs/opener/__init__.py:6
> /usr/lib/python3/dist-packages/fs/opener/__init__.py:6: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs.opener')`.
> Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
> __import__("pkg_resources").declare_namespace(__name__) # type: ignore
>
> ../../../../../../usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350
> /usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
> Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
> declare_namespace(parent)
>
> .pybuild/cpython3_3.12/build/tests/test_main.py: 19 warnings
> /usr/lib/python3/dist-packages/ufo2ft/fontInfoData.py:108: DeprecationWarning: datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.fromtimestamp(timestamp, datetime.UTC).
> t = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"]))
>
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_variable_otf
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_no_interpolation
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_no_interpolation
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_ufo_to_static_otf_cff2
> .pybuild/cpython3_3.12/build/tests/test_main.py::test_static_otf_cffsubr_subroutinizer
> /usr/lib/python3/dist-packages/cffsubr/__init__.py:63: DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
> with path(__name__, TX_EXE) as tx_cli:
>
> -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
> =========================== short test summary info ============================
> FAILED tests/test_main.py::test_interpolation - SystemExit: fontmake: Error: ...
> FAILED tests/test_main.py::test_interpolation_mutatormath - SystemExit: fontm...
> FAILED tests/test_main.py::test_interpolation_mutatormath_source_layer - Asse...
> FAILED tests/test_main.py::test_interpolation_and_masters_as_instances - Syst...
> FAILED tests/test_main.py::test_ufo_interpolation - ModuleNotFoundError: No m...
> FAILED tests/test_main.py::test_ufo_interpolation_specific - ModuleNotFoundEr...
> FAILED tests/test_main.py::test_subsetting[default] - ModuleNotFoundError: No...
> FAILED tests/test_main.py::test_subsetting[no-write-skipexportglyphs] - Modul...
> FAILED tests/test_main.py::test_shared_features_expansion - SystemExit: fontm...
> FAILED tests/test_main.py::test_mti_sources - ModuleNotFoundError: No module ...
> FAILED tests/test_main.py::test_interpolate_layout - ModuleNotFoundError: No ...
> FAILED tests/test_main.py::test_write_skipexportglyphs - ModuleNotFoundError:...
> FAILED tests/test_main.py::test_debug_feature_file - ModuleNotFoundError: No ...
> FAILED tests/test_main.py::test_static_otf_compreffor_subroutinizer - SystemE...
> ================== 14 failed, 38 passed, 28 warnings in 2.99s ==================
> E: pybuild pybuild:395: test: plugin distutils failed with: exit code=1: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.12/build; python3.12 -m pytest tests
> I: pybuild base:310: cd /<<PKGBUILDDIR>>/.pybuild/cpython3_3.11/build; python3.11 -m pytest tests
> ============================= test session starts ==============================
> platform linux -- Python 3.11.7, pytest-7.4.3, pluggy-1.3.0
> rootdir: /<<PKGBUILDDIR>>
> configfile: setup.cfg
> collected 52 items
>
> tests/test_instantiator.py ............................ [ 53%]
> tests/test_main.py ........................ [100%]
>
> =============================== warnings summary ===============================
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
> /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
> __import__("pkg_resources").declare_namespace(__name__) # type: ignore
>
> ../../../../../../usr/lib/python3/dist-packages/fs/__init__.py:4
> /usr/lib/python3/dist-packages/fs/__init__.py:4: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
> Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
> __import__("pkg_resources").declare_namespace(__name__) # type: ignore
>
> ../../../../../../usr/lib/python3/dist-packages/fs/opener/__init__.py:6
> /usr/lib/python3/dist-packages/fs/opener/__init__.py:6: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs.opener')`.
> Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
> __import__("pkg_resources").declare_namespace(__name__) # type: ignore
>
> ../../../../../../usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350
> /usr/lib/python3/dist-packages/pkg_resources/__init__.py:2350: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('fs')`.
> Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
> declare_namespace(parent)
>
> .pybuild/cpython3_3.11/build/tests/test_main.py: 14 warnings
> /usr/lib/python3/dist-packages/cffsubr/__init__.py:63: DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
> with path(__name__, TX_EXE) as tx_cli:
>
> .pybuild/cpython3_3.11/build/tests/test_main.py::test_ufo_interpolation
> /usr/lib/python3/dist-packages/glyphsLib/glyphdata.py:108: DeprecationWarning: open_binary is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
> with open_binary("glyphsLib.data", "GlyphData.xml") as f1:
>
> .pybuild/cpython3_3.11/build/tests/test_main.py::test_ufo_interpolation
> /usr/lib/python3/dist-packages/glyphsLib/glyphdata.py:109: DeprecationWarning: open_binary is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
> with open_binary("glyphsLib.data", "GlyphData_Ideographs.xml") as f2:
>
> -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
> ======================= 52 passed, 20 warnings in 2.85s ========================
> dh_auto_test: error: pybuild --test --test-pytest -i python{version} -p "3.12 3.11" returned exit code 13
The full build log is available from:
http://qa-logs.debian.net/2023/12/12/fontmake_2.4.1-2_unstable.log
All bugs filed during this archive rebuild are listed at:
https://bugs.debian.org/cgi-bin/pkgreport.cgi?tag=ftbfs-20231212;users=lucas@debian.org
or:
https://udd.debian.org/bugs/?release=na&merged=ign&fnewerval=7&flastmodval=7&fusertag=only&fusertagtag=ftbfs-20231212&fusertaguser=lucas@debian.org&allbugs=1&cseverity=1&ctags=1&caffected=1#results
A list of current common problems and possible solutions is available at
http://wiki.debian.org/qa.debian.org/FTBFS . You're welcome to contribute!
If you reassign this bug to another package, please mark it as 'affects'-ing
this package. See https://www.debian.org/Bugs/server-control#affects
If you fail to reproduce this, please provide a build log and diff it with mine
so that we can identify if something relevant changed in the meantime.
Reply to: