diff -Nru fontmake-2.0.10/debian/changelog fontmake-2.1.4/debian/changelog --- fontmake-2.0.10/debian/changelog 2020-03-01 17:04:45.000000000 +0000 +++ fontmake-2.1.4/debian/changelog 2020-05-30 01:29:13.000000000 +0000 @@ -1,3 +1,10 @@ +fontmake (2.1.4-1) unstable; urgency=medium + + * New upstream version 2.1.4 + * Bump Build-Depends + + -- Yao Wei (魏銘廷) Sat, 30 May 2020 09:29:13 +0800 + fontmake (2.0.10-1) unstable; urgency=medium * New upstream version 2.0.10 diff -Nru fontmake-2.0.10/debian/control fontmake-2.1.4/debian/control --- fontmake-2.0.10/debian/control 2020-03-01 17:04:45.000000000 +0000 +++ fontmake-2.1.4/debian/control 2020-05-30 01:29:13.000000000 +0000 @@ -9,15 +9,15 @@ python3-all, python3-setuptools, python3-setuptools-scm, - python3-fonttools (>= 4.4.0), + python3-fonttools (>= 4.10.2), python3-cu2qu (>= 1.6.7), - python3-glyphslib (>= 5.1.6), - python3-ufo2ft (>= 2.12.0), + python3-glyphslib (>= 5.1.10), + python3-ufo2ft (>= 2.14.0), python3-mutatormath (>= 2.1.2), - python3-fontmath (>= 0.5.2), + python3-fontmath (>= 0.6.0), python3-defcon (>= 0.6.0), python3-booleanoperations (>= 0.9.0), - python3-ufolib2 (>= 0.6.1), + python3-ufolib2 (>= 0.7.1), python3-attr (>= 18.2.0), python3-pytest , Standards-Version: 4.5.0 diff -Nru fontmake-2.0.10/Lib/fontmake/errors.py fontmake-2.1.4/Lib/fontmake/errors.py --- fontmake-2.0.10/Lib/fontmake/errors.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/Lib/fontmake/errors.py 2020-05-21 15:45:28.000000000 +0000 @@ -1,12 +1,43 @@ +import os + + class FontmakeError(Exception): - """Base class for all fontmake exceptions.""" + """Base class for all fontmake exceptions. + + This exception is intended to be chained to the original exception. The + main purpose is to provide a source file trail that points to where the + explosion came from. + """ + + def __init__(self, msg, source_file): + self.msg = msg + self.source_trail = [source_file] + + def __str__(self): + trail = " -> ".join( + f"'{str(os.path.relpath(s))}'" + for s in reversed(self.source_trail) + if s is not None + ) + cause = str(self.__cause__) if self.__cause__ is not None else None + + message = "" + if trail: + message = f"In {trail}: " + message += f"{self.msg}" + if cause: + message += f": {cause}" - pass + return message class TTFAError(FontmakeError): - def __init__(self, exitcode): + def __init__(self, exitcode, source_file): self.exitcode = exitcode + self.source_trail = source_file def __str__(self): - return "ttfautohint command failed: error " + str(self.exitcode) + return ( + f"ttfautohint failed for '{str(os.path.relpath(self.source_trail))}': " + f"error code {str(self.exitcode)}." + ) diff -Nru fontmake-2.0.10/Lib/fontmake/font_project.py fontmake-2.1.4/Lib/fontmake/font_project.py --- fontmake-2.0.10/Lib/fontmake/font_project.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/Lib/fontmake/font_project.py 2020-05-21 15:45:28.000000000 +0000 @@ -25,6 +25,7 @@ import attr import ufo2ft +import ufo2ft.errors import ufoLib2 from fontTools import designspaceLib from fontTools.misc.loggingTools import Timer, configLogger @@ -80,7 +81,11 @@ https://github.com/LettError/designSpaceDocument/issues/16 https://github.com/fonttools/fonttools/pull/1395 """ - designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path) + try: + designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path) + except Exception as e: + raise FontmakeError("Reading Designspace failed", designspace_path) from e + for axis in designspace.axes: axis.minimum = axis.map_forward(axis.minimum) axis.default = axis.map_forward(axis.default) @@ -111,10 +116,16 @@ self.validate_ufo = validate_ufo def open_ufo(self, path): - return ufoLib2.Font.open(path, validate=self.validate_ufo) + try: + return ufoLib2.Font.open(path, validate=self.validate_ufo) + except Exception as e: + raise FontmakeError("Reading UFO source failed", path) from e def save_ufo_as(self, font, path): - font.save(path, overwrite=True, validate=self.validate_ufo) + try: + font.save(path, overwrite=True, validate=self.validate_ufo) + except Exception as e: + raise FontmakeError("Writing UFO source failed", path) from e @timer() def build_master_ufos( @@ -139,7 +150,10 @@ if not os.path.isdir(instance_dir): os.mkdir(instance_dir) - font = glyphsLib.GSFont(glyphs_path) + try: + font = glyphsLib.GSFont(glyphs_path) + except Exception as e: + raise FontmakeError("Loading Glyphs file failed", glyphs_path) from e if designspace_path is not None: designspace_dir = os.path.dirname(designspace_path) @@ -215,7 +229,10 @@ # that can be modified in-place. ds_path = designspace.path if ds_path is not None: - designspace = designspaceLib.DesignSpaceDocument.fromfile(ds_path) + try: + designspace = designspaceLib.DesignSpaceDocument.fromfile(ds_path) + except Exception as e: + raise FontmakeError("Reading Designspace failed", ds_path) from e designspace.loadSourceFonts(opener=self.open_ufo) @@ -342,7 +359,10 @@ if debugFeatureFile and writeFontName: debugFeatureFile.write(f"\n### {name} ###\n") - yield compile_func(ufo, debugFeatureFile=debugFeatureFile, **options) + try: + yield compile_func(ufo, debugFeatureFile=debugFeatureFile, **options) + except Exception as e: + raise FontmakeError("Compiling UFO failed", ufo.path) from e @timer() def save_otfs( @@ -350,7 +370,6 @@ ufos, ttf=False, is_instance=False, - interpolatable=False, autohint=None, subset=None, use_production_names=None, @@ -375,7 +394,6 @@ ufos: Font objects to compile. ttf: If True, build fonts with TrueType outlines and .ttf extension. is_instance: If output fonts are instances, for generating paths. - interpolatable: If output is interpolatable, for generating paths. autohint: Parameters to provide to ttfautohint. If not provided, the autohinting step is skipped. subset: Whether to subset the output according to data in the UFOs. @@ -436,9 +454,7 @@ if interpolate_layout_from is not None: if interpolate_layout_dir is None: - interpolate_layout_dir = self._output_dir( - ext, is_instance=False, interpolatable=interpolatable - ) + interpolate_layout_dir = self._output_dir(ext, is_instance=False) finder = partial(_varLib_finder, directory=interpolate_layout_dir, ext=ext) # no need to generate automatic features in ufo2ft, since here we # are interpolating precompiled GPOS table with fontTools.varLib. @@ -448,7 +464,13 @@ # completely skip compiling features into OTL tables feature_writers = [] - compiler_options = dict( + fonts = self._iter_compile( + ufos, + ttf, + removeOverlaps=remove_overlaps, + overlapsBackend=overlaps_backend, + optimizeCFF=optimize_cff, + roundTolerance=cff_round_tolerance, useProductionNames=use_production_names, reverseDirection=reverse_direction, cubicConversionError=conversion_error, @@ -457,24 +479,6 @@ inplace=True, # avoid extra copy ) - if interpolatable: - if not ttf: - raise NotImplementedError("interpolatable CFF not supported yet") - - logger.info("Building interpolation-compatible TTFs") - - fonts = ufo2ft.compileInterpolatableTTFs(ufos, **compiler_options) - else: - fonts = self._iter_compile( - ufos, - ttf, - removeOverlaps=remove_overlaps, - overlapsBackend=overlaps_backend, - optimizeCFF=optimize_cff, - roundTolerance=cff_round_tolerance, - **compiler_options, - ) - do_autohint = ttf and autohint is not None for font, ufo in zip(fonts, ufos): @@ -500,7 +504,7 @@ os.close(fd) elif output_path is None: otf_path = self._output_path( - ufo, ext, is_instance, interpolatable, output_dir=output_dir + ufo, ext, is_instance, output_dir=output_dir ) else: otf_path = output_path @@ -526,12 +530,7 @@ hinted_otf_path = output_path else: hinted_otf_path = self._output_path( - ufo, - ext, - is_instance, - interpolatable, - autohinted=True, - output_dir=output_dir, + ufo, ext, is_instance, autohinted=True, output_dir=output_dir ) try: ttfautohint(otf_path, hinted_otf_path, args=autohint) @@ -669,7 +668,11 @@ mti_source=mti_source, write_skipexportglyphs=write_skipexportglyphs, ) - self.run_from_designspace(designspace_path, **kwargs) + try: + self.run_from_designspace(designspace_path, **kwargs) + except FontmakeError as e: + e.source_trail.append(glyphs_path) + raise def interpolate_instance_ufos( self, @@ -695,17 +698,26 @@ Returns: generator of ufoLib2.Font objects corresponding to the UFO instances. Raises: - ValueError: The Designspace has no default source or contains incompatible - glyphs or contains anisotropic (MutatorMath) locations or contains a - substitution for a non-existant glyph. + FontmakeError: instances could not be prepared for interpolation or + interpolation failed. + ValueError: an instance descriptor did not have a filename attribute set. """ from glyphsLib.interpolation import apply_instance_data_to_ufo logger.info("Interpolating master UFOs from designspace") - designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace.path) - generator = instantiator.Instantiator.from_designspace( - designspace, round_geometry=round_instances - ) + try: + designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace.path) + except Exception as e: + raise FontmakeError("Reading Designspace failed", designspace.path) from e + + try: + generator = instantiator.Instantiator.from_designspace( + designspace, round_geometry=round_instances + ) + except instantiator.InstantiatorError as e: + raise FontmakeError( + "Preparing the Designspace for interpolation failed", designspace.path + ) from e if expand_features_to_instances: logger.debug("Expanding features to instance UFOs") @@ -724,7 +736,13 @@ logger.info(f'Generating instance UFO for "{instance.name}"') - instance.font = generator.generate_instance(instance) + try: + instance.font = generator.generate_instance(instance) + except instantiator.InstantiatorError as e: + raise FontmakeError( + f"Interpolating instance '{instance.styleName}' failed.", + designspace.path, + ) from e apply_instance_data_to_ufo(instance.font, instance, designspace) @@ -778,7 +796,8 @@ if any(source.layerName is not None for source in designspace.sources): raise FontmakeError( "MutatorMath doesn't support DesignSpace sources with 'layer' " - "attribute" + "attribute", + None, ) with temporarily_disabling_axis_maps(designspace.path) as temp_designspace_path: @@ -867,7 +886,10 @@ % (argname, ", ".join(sorted(interp_outputs))) ) - designspace = designspaceLib.DesignSpaceDocument.fromfile(designspace_path) + 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 # element if user supplied a custom featureWriters configuration; @@ -875,26 +897,37 @@ if feature_writers is None and FEATURE_WRITERS_KEY in designspace.lib: feature_writers = loadFeatureWriters(designspace) - 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, - **kwargs, - ) - if interp_outputs: - self._run_from_designspace_interpolatable( - designspace, - outputs=interp_outputs, - feature_writers=feature_writers, - **kwargs, - ) + 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, + **kwargs, + ) + if interp_outputs: + self._run_from_designspace_interpolatable( + designspace, + outputs=interp_outputs, + feature_writers=feature_writers, + **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 def _run_from_designspace_static( self, @@ -1002,10 +1035,9 @@ ufos = [self.open_ufo(x) if isinstance(x, str) else x for x in ufos] ufo_paths = [x.path for x in ufos] else: - raise FontmakeError( + raise TypeError( "UFOs parameter is neither a defcon.Font object, a path or a glob, " - "nor a list of any of these.", - ufos, + f"nor a list of any of these: {ufos:r}." ) need_reload = False diff -Nru fontmake-2.0.10/Lib/fontmake/instantiator.py fontmake-2.1.4/Lib/fontmake/instantiator.py --- fontmake-2.0.10/Lib/fontmake/instantiator.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/Lib/fontmake/instantiator.py 2020-05-21 15:45:28.000000000 +0000 @@ -181,21 +181,36 @@ ): """Instantiates a new data class from a Designspace object.""" if designspace.default is None: - raise InstantiatorError( - "Can't generate UFOs from this designspace: no default font." - ) + raise InstantiatorError(_error_msg_no_default(designspace)) if any(anisotropic(instance.location) for instance in designspace.instances): raise InstantiatorError( "The Designspace contains anisotropic instance locations, which are " - "not supported by varLib." + "not supported by varLib. Look for and remove all 'yvalue=\"...\"' or " + "use MutatorMath instead." ) designspace.loadSourceFonts(ufoLib2.Font.open) - glyph_names: Set[str] = set() + # The default font (default layer) determines which glyphs are interpolated, + # because the math behind varLib and MutatorMath uses the default font as the + # point of reference for all data. + default_font = designspace.default.font + glyph_names: Set[str] = set(default_font.keys()) + for source in designspace.sources: - glyph_names.update(source.font.keys()) + other_names = set(source.font.keys()) + diff_names = other_names - glyph_names + if diff_names: + logger.warning( + "The source %s (%s) contains glyphs that are missing from the " + "default source, which will be ignored: %s. If this is unintended, " + "check that these glyphs have the exact same name as the " + "corresponding glyphs in the default source.", + source.name, + source.filename, + ", ".join(sorted(diff_names)), + ) # Construct Variators axis_bounds: AxisBounds = {} # Design space! @@ -213,17 +228,31 @@ special_axes[axis.tag] = axis masters_info = collect_info_masters(designspace, axis_bounds) - info_mutator = Variator.from_masters(masters_info, axis_order) + try: + info_mutator = Variator.from_masters(masters_info, axis_order) + except varLib.errors.VarLibError as e: + raise InstantiatorError( + f"Cannot set up fontinfo for interpolation: {e}'" + ) from e masters_kerning = collect_kerning_masters(designspace, axis_bounds) - kerning_mutator = Variator.from_masters(masters_kerning, axis_order) + try: + kerning_mutator = Variator.from_masters(masters_kerning, axis_order) + except varLib.errors.VarLibError as e: + raise InstantiatorError( + f"Cannot set up kerning for interpolation: {e}'" + ) from e - default_font = designspace.findDefault().font glyph_mutators: Dict[str, Variator] = {} glyph_name_to_unicodes: Dict[str, List[int]] = {} for glyph_name in glyph_names: items = collect_glyph_masters(designspace, glyph_name, axis_bounds) - glyph_mutators[glyph_name] = Variator.from_masters(items, axis_order) + try: + glyph_mutators[glyph_name] = Variator.from_masters(items, axis_order) + except varLib.errors.VarLibError as e: + raise InstantiatorError( + f"Cannot set up glyph '{glyph_name}' for interpolation: {e}'" + ) from e glyph_name_to_unicodes[glyph_name] = default_font[glyph_name].unicodes # Construct defaults to copy over @@ -325,7 +354,11 @@ # whatever reason (usually outline incompatibility)... if glyph_name not in self.skip_export_glyphs: raise InstantiatorError( - f"Failed to generate instance of glyph '{glyph_name}'." + f"Failed to generate instance of glyph '{glyph_name}': " + f"{str(e)}. (Note: the most common cause for an error here is " + "that the glyph outlines are not point-for-point compatible or " + "have the same starting point or are in the same order in all " + "masters.)" ) from e # ...except if the glyph is in public.skipExportGlyphs and would @@ -417,6 +450,28 @@ ) +def _error_msg_no_default(designspace: designspaceLib.DesignSpaceDocument) -> str: + if any(axis.map for axis in designspace.axes): + bonus_msg = ( + "For axes with a mapping, the 'default' values should have an " + "'input=\"...\"' map value, where the corresponding 'output=\"...\"' " + "value then points to the master source." + ) + else: + bonus_msg = "" + + default_location = ", ".join( + f"{k}: {v}" for k, v in designspace.newDefaultLocation().items() + ) + + return ( + "Can't generate UFOs from this Designspace because there is no default " + f"master source at location '{default_location}'. Check that all 'default' " + "values of all axes together point to a single actual master source. " + f"{bonus_msg}" + ) + + def location_to_key(location: Location) -> LocationKey: """Converts a Location into a sorted tuple so it can be used as a dict key.""" @@ -452,20 +507,31 @@ designspace: designspaceLib.DesignSpaceDocument, axis_bounds: AxisBounds ) -> List[Tuple[Location, FontMathObject]]: """Return master kerning objects wrapped by MathKerning.""" + + # Always take the groups from the default source. This also avoids fontMath + # making a union of all groups it is given. + groups = designspace.default.font.groups + locations_and_masters = [] for source in designspace.sources: if source.layerName is not None: continue # No kerning in source layers. + # If a source has groups, they should match the default's. + if source.font.groups and source.font.groups != groups: + logger.warning( + "The source %s (%s) contains different groups than the default source. " + "The default source's groups will be used for the instances.", + source.name, + source.filename, + ) + # This assumes that groups of all sources are the same. normalized_location = varLib.models.normalizeLocation( source.location, axis_bounds ) locations_and_masters.append( - ( - normalized_location, - fontMath.MathKerning(source.font.kerning, source.font.groups), - ) + (normalized_location, fontMath.MathKerning(source.font.kerning, groups)) ) return locations_and_masters diff -Nru fontmake-2.0.10/Lib/fontmake/__main__.py fontmake-2.1.4/Lib/fontmake/__main__.py --- fontmake-2.0.10/Lib/fontmake/__main__.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/Lib/fontmake/__main__.py 2020-05-21 15:45:28.000000000 +0000 @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging +import sys from argparse import ArgumentParser, FileType from contextlib import contextmanager @@ -413,6 +415,7 @@ "[mutatormath] extra" ) + PRINT_TRACEBACK = args.get("verbose", "INFO") == "DEBUG" try: project = FontProject( timing=args.pop("timing"), @@ -460,12 +463,11 @@ ufo_paths, is_instance=args.pop("masters_as_instances"), **args ) except FontmakeError as e: - import sys - - sys.exit("fontmake: error: %s" % e) + if PRINT_TRACEBACK: + logging.exception(e) + sys.exit(1) + sys.exit(f"fontmake: Error: {str(e)}") if __name__ == "__main__": - import sys - sys.exit(main()) diff -Nru fontmake-2.0.10/Lib/fontmake/ttfautohint.py fontmake-2.1.4/Lib/fontmake/ttfautohint.py --- fontmake-2.0.10/Lib/fontmake/ttfautohint.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/Lib/fontmake/ttfautohint.py 2020-05-21 15:45:28.000000000 +0000 @@ -15,7 +15,7 @@ import subprocess -from fontmake.errors import TTFAError +from fontmake.errors import TTFAError, FontmakeError def ttfautohint(in_file, out_file, args=None, **kwargs): @@ -31,9 +31,14 @@ if args is not None: if kwargs: raise TypeError("Should not provide both cmd args and kwargs.") - rv = subprocess.call(arg_list + args.split() + file_args) + try: + rv = subprocess.call(arg_list + args.split() + file_args) + except OSError as e: + raise FontmakeError( + "Could not launch ttfautohint (is it installed?)", in_file + ) from e if rv != 0: - raise TTFAError(rv) + raise TTFAError(rv, in_file) return boolean_options = ( @@ -79,4 +84,4 @@ rv = subprocess.call(arg_list + file_args) if rv != 0: - raise TTFAError(rv) + raise TTFAError(rv, in_file) diff -Nru fontmake-2.0.10/README.rst fontmake-2.1.4/README.rst --- fontmake-2.0.10/README.rst 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/README.rst 2020-05-21 15:45:28.000000000 +0000 @@ -45,10 +45,8 @@ It is recommended to install fontmake inside a "virtual environment" to prevent conflicts between its dependencies and other modules installed globally. -Alternatively, we also provide a self-contained, standalone version of fontmake -that only requires a Python 3.6 or 3.7 installation to run. These are available -to download from the fontmake `Github releases`_ page. -You simply unzip them and run the included `fontmake` command from your console. +You could also use the `pipx`_ tool to automate the installation/upgrade of +python apps like fontmake in isolated environments. Usage ~~~~~ @@ -71,6 +69,7 @@ .. _pip documentation: https://pip.readthedocs.io/en/stable/user_guide/#requirements-files .. _PyPI: https://pypi.org/project/fontmake .. _Github releases: https://github.com/googlefonts/fontmake/releases +.. _pipx: https://github.com/pipxproject/pipx .. |Travis Build Status| image:: https://travis-ci.org/googlefonts/fontmake.svg :target: https://travis-ci.org/googlefonts/fontmake .. |Python Versions| image:: https://img.shields.io/badge/python-3.6-blue.svg diff -Nru fontmake-2.0.10/requirements.txt fontmake-2.1.4/requirements.txt --- fontmake-2.0.10/requirements.txt 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/requirements.txt 2020-05-21 15:45:28.000000000 +0000 @@ -1,10 +1,10 @@ -fonttools[lxml,unicode,ufo]==4.4.0 +fonttools[lxml,unicode,ufo]==4.10.2 cu2qu==1.6.7 -glyphsLib==5.1.6 -ufo2ft[pathops]==2.12.0 +glyphsLib==5.1.10 +ufo2ft[pathops]==2.14.0 MutatorMath==2.1.2 -fontMath==0.5.2 +fontMath==0.6.0 defcon[lxml]==0.6.0 booleanOperations==0.9.0 -ufoLib2==0.6.1 +ufoLib2==0.7.1 attrs==19.3.0 diff -Nru fontmake-2.0.10/setup.py fontmake-2.1.4/setup.py --- fontmake-2.0.10/setup.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/setup.py 2020-05-21 15:45:28.000000000 +0000 @@ -39,13 +39,13 @@ setup_requires=wheel + ["setuptools_scm"], python_requires=">=3.6", install_requires=[ - "fonttools[ufo,lxml,unicode]>=4.4.0", + "fonttools[ufo,lxml,unicode]>=4.10.2", "cu2qu>=1.6.7", - "glyphsLib>=5.1.6", - "ufo2ft>=2.12.0", - "fontMath>=0.5.2", + "glyphsLib>=5.1.10", + "ufo2ft>=2.14.0", + "fontMath>=0.6.0", "booleanOperations>=0.9.0", - "ufoLib2>=0.6.1", + "ufoLib2>=0.7.1", "attrs>=19", ], extras_require={ diff -Nru fontmake-2.0.10/tests/test_instantiator.py fontmake-2.1.4/tests/test_instantiator.py --- fontmake-2.0.10/tests/test_instantiator.py 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/tests/test_instantiator.py 2020-05-21 15:45:28.000000000 +0000 @@ -60,6 +60,63 @@ assert font.info.openTypeOS2WidthClass == 9 +def test_default_groups_only(data_dir, caplog): + """Test that only the default source's groups end up in instances.""" + + d = designspaceLib.DesignSpaceDocument() + d.addAxisDescriptor( + name="Weight", tag="wght", minimum=300, default=300, maximum=900 + ) + d.addSourceDescriptor(location={"Weight": 300}, font=ufoLib2.Font()) + d.addSourceDescriptor(location={"Weight": 900}, font=ufoLib2.Font()) + d.addInstanceDescriptor(styleName="2", location={"Weight": 400}) + d.findDefault() + + d.sources[0].font.groups["public.kern1.GRK_alpha_alt_LC_1ST"] = [ + "alpha.alt", + "alphatonos.alt", + ] + d.sources[1].font.groups["public.kern1.GRK_alpha_LC_1ST"] = [ + "alpha.alt", + "alphatonos.alt", + ] + + generator = fontmake.instantiator.Instantiator.from_designspace(d) + assert "contains different groups than the default source" in caplog.text + + instance = generator.generate_instance(d.instances[0]) + assert instance.groups == { + "public.kern1.GRK_alpha_alt_LC_1ST": ["alpha.alt", "alphatonos.alt"] + } + + +def test_default_groups_only2(data_dir, caplog): + """Test that the group difference warning is not triggered if non-default + source groups are empty.""" + + d = designspaceLib.DesignSpaceDocument() + d.addAxisDescriptor( + name="Weight", tag="wght", minimum=300, default=300, maximum=900 + ) + d.addSourceDescriptor(location={"Weight": 300}, font=ufoLib2.Font()) + d.addSourceDescriptor(location={"Weight": 900}, font=ufoLib2.Font()) + d.addInstanceDescriptor(styleName="2", location={"Weight": 400}) + d.findDefault() + + d.sources[0].font.groups["public.kern1.GRK_alpha_alt_LC_1ST"] = [ + "alpha.alt", + "alphatonos.alt", + ] + + generator = fontmake.instantiator.Instantiator.from_designspace(d) + assert "contains different groups than the default source" not in caplog.text + + instance = generator.generate_instance(d.instances[0]) + assert instance.groups == { + "public.kern1.GRK_alpha_alt_LC_1ST": ["alpha.alt", "alphatonos.alt"] + } + + def test_interpolation_no_rounding(data_dir): designspace = designspaceLib.DesignSpaceDocument.fromfile( data_dir / "MutatorSans" / "MutatorSans.designspace" @@ -291,6 +348,25 @@ assert instance_font["l"].width == 220 +def test_interpolation_only_default(data_dir, caplog): + designspace = designspaceLib.DesignSpaceDocument.fromfile( + data_dir / "MutatorSans" / "MutatorSans.designspace" + ) + designspace.loadSourceFonts(ufoLib2.Font.open) + for name in designspace.default.font.glyphOrder: + if name != "A": + del designspace.default.font[name] + + with caplog.at_level(logging.WARNING): + generator = fontmake.instantiator.Instantiator.from_designspace( + designspace, round_geometry=True + ) + assert "contains glyphs that are missing from the" in caplog.text + + instance_font = generator.generate_instance(designspace.instances[0]) + assert {g.name for g in instance_font} == {"A"} + + def test_interpolation_masters_as_instances(data_dir): designspace = designspaceLib.DesignSpaceDocument.fromfile( data_dir diff -Nru fontmake-2.0.10/.travis.yml fontmake-2.1.4/.travis.yml --- fontmake-2.0.10/.travis.yml 2020-02-24 16:53:27.000000000 +0000 +++ fontmake-2.1.4/.travis.yml 2020-05-21 15:45:28.000000000 +0000 @@ -44,10 +44,9 @@ - | if [ -n "$TRAVIS_TAG" ] && [ "$TRAVIS_REPO_SLUG" == "googlefonts/fontmake" ] && [ "$TOXENV" == "py37-cov" ]; then pip install --upgrade pip - pip install --upgrade twine setuptools wheel shiv + pip install --upgrade twine setuptools wheel python setup.py sdist pip wheel --no-deps --wheel-dir dist . - ./build_pyz.sh twine upload dist/*.whl dist/*.zip fi @@ -56,9 +55,6 @@ - provider: releases api_key: secure: ZZeHx2+Yc4gjEPVyhqySp700cF6Ro8xy0IOF0wD+BKOVi9/rj7AWfz5OBCb/3D18lji6lepXtEQ6/7AgqDLZNUk7R459g7GoczRt/M/iR3aXJslCiBTG/4r1O4ZCNXKLVgoX0Cavi1we7qKTot0wIYWcjk1TU9EmRoYpsnZTTWHKW0F2ZDd4O0IdWSUO6wF+g2DY4OxVy9zC7D2wdg53+LSRFao3w3hALZ7Rb32Icq/QdG+z4t9Vsa61keHULDH5MMMcUHQ6PbM96hBTydeTJrpwrojfX7WhE+SlptwH4cnTZPSFBqgOvrOwu9vKKI5TvUvfE4qt9QP+nZEfjrwnxBXMvYpRIDDoM+stc+NZ25xiEel3JXrmmue/VKtwbO4WaKSZWJXntyEcDFTnk32w6PmoawG1Rp1noVwL0b9sPXAYm4mw0yY4g+UoUsxRcxWFeKU1gXYIBGIKgqglys+WgnIUraqQcUuHn2l90n1DM86D2yOHsrg/NSFK1ePJAlNcz8rkYtUeCaffG2iyAjxf5IiqDx6k1flKphkLTqbc0eFkocj3ay7vnFINflcQRD4ISCEd5J2FuncnPkyUHTDPDYwjsNVuqceTF22e48NL+qfqLohbQ5aU6xiol6Pv4QQFOK+LG0bJb87DXlzJV9ZRjV0zai3wjhHuw2jfvvA4gHo= - file_glob: true - file: "shivs/*.zip" - skip_cleanup: true on: repo: googlefonts/fontmake tags: true