From ff46e2838f37d54f4517fa1db6de84e6e38a9db3 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 16:25:35 -0700 Subject: [PATCH 01/13] [varLib.plot] Reorder imports to make missing library easier to detect $ pip install matplotlib --- Lib/fontTools/varLib/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py index b6561dc6a..0eb654327 100644 --- a/Lib/fontTools/varLib/plot.py +++ b/Lib/fontTools/varLib/plot.py @@ -2,8 +2,8 @@ from fontTools.varLib.models import VariationModel, supportScalar from fontTools.designspaceLib import DesignSpaceDocument -from mpl_toolkits.mplot3d import axes3d from matplotlib import pyplot +from mpl_toolkits.mplot3d import axes3d from itertools import cycle import math import logging From 28fae1d95f29f5fc866c439a391ff2ee138842d6 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 18:27:06 -0700 Subject: [PATCH 02/13] [cff] Simplify rounding logic No semantic change. Just refactoring and simplification in anticipation of coming changes. --- Lib/fontTools/pens/t2CharStringPen.py | 13 ++++++------- Lib/fontTools/varLib/cff.py | 17 +++-------------- Lib/fontTools/varLib/plot.py | 4 ++-- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py index a4b1d8f67..2824b5cf5 100644 --- a/Lib/fontTools/pens/t2CharStringPen.py +++ b/Lib/fontTools/pens/t2CharStringPen.py @@ -20,15 +20,14 @@ def t2c_round(number, tolerance=0.5): # else return the value un-rounded return number -def makeRoundFunc(tolerance): +def roundFunc(tolerance): if tolerance < 0: raise ValueError("Rounding tolerance must be positive") - def roundPoint(point): - x, y = point - return t2c_round(x, tolerance), t2c_round(y, tolerance) + def round(v): + return t2c_round(v, tolerance) - return roundPoint + return round class T2CharStringPen(BasePen): @@ -44,7 +43,7 @@ class T2CharStringPen(BasePen): def __init__(self, width, glyphSet, roundTolerance=0.5, CFF2=False): super(T2CharStringPen, self).__init__(glyphSet) - self.roundPoint = makeRoundFunc(roundTolerance) + self.round = roundFunc(roundTolerance) self._CFF2 = CFF2 self._width = width self._commands = [] @@ -52,7 +51,7 @@ class T2CharStringPen(BasePen): def _p(self, pt): p0 = self._p0 - pt = self._p0 = self.roundPoint(pt) + pt = self._p0 = (self.round(pt[0]), self.round(pt[1])) return [pt[0]-p0[0], pt[1]-p0[1]] def _moveTo(self, pt): diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py index 0a6ba220b..6f704bbee 100644 --- a/Lib/fontTools/varLib/cff.py +++ b/Lib/fontTools/varLib/cff.py @@ -18,7 +18,7 @@ from fontTools.ttLib import newTable from fontTools import varLib from fontTools.varLib.models import allEqual from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor -from fontTools.pens.t2CharStringPen import T2CharStringPen, t2c_round +from fontTools.pens.t2CharStringPen import T2CharStringPen, roundFunc from .errors import VarLibCFFDictMergeError, VarLibCFFPointTypeMergeError, VarLibMergeError @@ -422,16 +422,6 @@ def merge_charstrings(glyphOrder, num_masters, top_dicts, masterModel): return cvData -def makeRoundNumberFunc(tolerance): - if tolerance < 0: - raise ValueError("Rounding tolerance must be positive") - - def roundNumber(val): - return t2c_round(val, tolerance) - - return roundNumber - - class CFFToCFF2OutlineExtractor(T2OutlineExtractor): """ This class is used to remove the initial width from the CFF charstring without trying to add the width to self.nominalWidthX, @@ -518,7 +508,7 @@ class CFF2CharStringMergePen(T2CharStringPen): self.prev_move_idx = 0 self.seen_moveto = False self.glyphName = glyphName - self.roundNumber = makeRoundNumberFunc(roundTolerance) + self.round = roundFunc(roundTolerance) def add_point(self, point_type, pt_coords): if self.m_index == 0: @@ -649,8 +639,7 @@ class CFF2CharStringMergePen(T2CharStringPen): self, private=None, globalSubrs=None, var_model=None, optimize=True): commands = self._commands - commands = self.reorder_blend_args(commands, var_model.getDeltas, - self.roundNumber) + commands = self.reorder_blend_args(commands, var_model.getDeltas, self.round) if optimize: commands = specializeCommands( commands, generalizeFirst=False, diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py index 0eb654327..811559fa6 100644 --- a/Lib/fontTools/varLib/plot.py +++ b/Lib/fontTools/varLib/plot.py @@ -68,10 +68,10 @@ def plotLocations(locations, fig, names=None, **kwargs): def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs): + subplot = fig.add_subplot(111) for i, (support, color, name) in enumerate( zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names)) ): - subplot = fig.add_subplot(rows, cols, i + 1) if name is not None: subplot.set_title(name) subplot.set_xlabel(axis) @@ -91,10 +91,10 @@ def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs): def _plotLocations3D(model, axes, fig, rows, cols, names, **kwargs): ax1, ax2 = axes + axis3D = fig.add_subplot(111, projection='3d') for i, (support, color, name) in enumerate( zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names)) ): - axis3D = fig.add_subplot(rows, cols, i + 1, projection='3d') if name is not None: axis3D.set_title(name) axis3D.set_xlabel(ax1) From ed77aeaebff58abb61baf79d9e481959f56b335c Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 18:31:11 -0700 Subject: [PATCH 03/13] [cff] Speed-up rounding We can choose specific round function based on tolerance once, instead of choosing every time a number is being rounded. --- Lib/fontTools/pens/t2CharStringPen.py | 28 +++++++++++---------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py index 2824b5cf5..7af9b7aad 100644 --- a/Lib/fontTools/pens/t2CharStringPen.py +++ b/Lib/fontTools/pens/t2CharStringPen.py @@ -7,27 +7,21 @@ from fontTools.pens.basePen import BasePen from fontTools.cffLib.specializer import specializeCommands, commandsToProgram -def t2c_round(number, tolerance=0.5): - if tolerance == 0: - return number # no-op - rounded = otRound(number) - # return rounded integer if the tolerance >= 0.5, or if the absolute - # difference between the original float and the rounded integer is - # within the tolerance - if tolerance >= .5 or abs(rounded - number) <= tolerance: - return rounded - else: - # else return the value un-rounded - return number - -def roundFunc(tolerance): +def roundFunc(tolerance, round=otRound): if tolerance < 0: raise ValueError("Rounding tolerance must be positive") - def round(v): - return t2c_round(v, tolerance) + if tolerance == 0: + return lambda x: x - return round + if tolerance >= .5: + return round + + def maybe_round(v): + rounded = round(v) + return rounded if abs(rounded - v) <= tolerance else v + + return maybe_round class T2CharStringPen(BasePen): From abc1ba07a4046f0f7e955802e695be16967830f3 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 18:48:08 -0700 Subject: [PATCH 04/13] [misc.roundTools] New module; move otRound() here Code relying on old place (fixedTools.otRound) still works. --- Lib/fontTools/colorLib/geometry.py | 2 +- Lib/fontTools/colorLib/table_builder.py | 2 +- Lib/fontTools/misc/arrayTools.py | 2 +- Lib/fontTools/misc/fixedTools.py | 26 +----------------- Lib/fontTools/misc/roundTools.py | 36 +++++++++++++++++++++++++ Lib/fontTools/pens/roundingPen.py | 2 +- Lib/fontTools/pens/t2CharStringPen.py | 2 +- Lib/fontTools/pens/ttGlyphPen.py | 1 + Lib/fontTools/subset/__init__.py | 2 +- Lib/fontTools/subset/cff.py | 2 +- Lib/fontTools/ttLib/tables/_h_m_t_x.py | 2 +- Lib/fontTools/ttLib/tables/otTables.py | 2 +- Lib/fontTools/varLib/merger.py | 2 +- Lib/fontTools/varLib/mutator.py | 3 ++- 14 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 Lib/fontTools/misc/roundTools.py diff --git a/Lib/fontTools/colorLib/geometry.py b/Lib/fontTools/colorLib/geometry.py index ec6475357..e62aead1c 100644 --- a/Lib/fontTools/colorLib/geometry.py +++ b/Lib/fontTools/colorLib/geometry.py @@ -1,7 +1,7 @@ """Helpers for manipulating 2D points and vectors in COLR table.""" from math import copysign, cos, hypot, pi -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound def _vector_between(origin, target): diff --git a/Lib/fontTools/colorLib/table_builder.py b/Lib/fontTools/colorLib/table_builder.py index 18e2de181..6fba6b0fc 100644 --- a/Lib/fontTools/colorLib/table_builder.py +++ b/Lib/fontTools/colorLib/table_builder.py @@ -22,7 +22,7 @@ from fontTools.ttLib.tables.otConverters import ( IntValue, FloatValue, ) -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound class BuildCallback(enum.Enum): diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py index 4b5f08298..9a64b2929 100644 --- a/Lib/fontTools/misc/arrayTools.py +++ b/Lib/fontTools/misc/arrayTools.py @@ -3,7 +3,7 @@ so on. """ from fontTools.misc.py23 import * -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools.misc.vector import Vector as _Vector import math import warnings diff --git a/Lib/fontTools/misc/fixedTools.py b/Lib/fontTools/misc/fixedTools.py index 931b665e2..eda81c33c 100644 --- a/Lib/fontTools/misc/fixedTools.py +++ b/Lib/fontTools/misc/fixedTools.py @@ -18,6 +18,7 @@ functions for converting between fixed-point, float and string representations. """ from fontTools.misc.py23 import * +from .roundTools import otRound import math import logging @@ -25,7 +26,6 @@ log = logging.getLogger(__name__) __all__ = [ "MAX_F2DOT14", - "otRound", "fixedToFloat", "floatToFixed", "floatToFixedToFloat", @@ -41,30 +41,6 @@ __all__ = [ MAX_F2DOT14 = 0x7FFF / (1 << 14) -def otRound(value): - """Round float value to nearest integer towards ``+Infinity``. - - The OpenType spec (in the section on `"normalization" of OpenType Font Variations `_) - defines the required method for converting floating point values to - fixed-point. In particular it specifies the following rounding strategy: - - for fractional values of 0.5 and higher, take the next higher integer; - for other fractional values, truncate. - - This function rounds the floating-point value according to this strategy - in preparation for conversion to fixed-point. - - Args: - value (float): The input floating-point value. - - Returns - float: The rounded value. - """ - # See this thread for how we ended up with this implementation: - # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166 - return int(math.floor(value + 0.5)) - - def fixedToFloat(value, precisionBits): """Converts a fixed-point number to a float given the number of precision bits. diff --git a/Lib/fontTools/misc/roundTools.py b/Lib/fontTools/misc/roundTools.py new file mode 100644 index 000000000..af3e317d2 --- /dev/null +++ b/Lib/fontTools/misc/roundTools.py @@ -0,0 +1,36 @@ +""" +Various round-to-integer helpers. +""" + +import math +import logging + +log = logging.getLogger(__name__) + +__all__ = [ + "otRound", +] + +def otRound(value): + """Round float value to nearest integer towards ``+Infinity``. + + The OpenType spec (in the section on `"normalization" of OpenType Font Variations `_) + defines the required method for converting floating point values to + fixed-point. In particular it specifies the following rounding strategy: + + for fractional values of 0.5 and higher, take the next higher integer; + for other fractional values, truncate. + + This function rounds the floating-point value according to this strategy + in preparation for conversion to fixed-point. + + Args: + value (float): The input floating-point value. + + Returns + float: The rounded value. + """ + # See this thread for how we ended up with this implementation: + # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166 + return int(math.floor(value + 0.5)) + diff --git a/Lib/fontTools/pens/roundingPen.py b/Lib/fontTools/pens/roundingPen.py index c032cad1f..2a7c476c3 100644 --- a/Lib/fontTools/pens/roundingPen.py +++ b/Lib/fontTools/pens/roundingPen.py @@ -1,4 +1,4 @@ -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools.misc.transform import Transform from fontTools.pens.filterPen import FilterPen, FilterPointPen diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py index 7af9b7aad..8b718ccbc 100644 --- a/Lib/fontTools/pens/t2CharStringPen.py +++ b/Lib/fontTools/pens/t2CharStringPen.py @@ -1,7 +1,7 @@ # Copyright (c) 2009 Type Supply LLC # Author: Tal Leming -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools.misc.psCharStrings import T2CharString from fontTools.pens.basePen import BasePen from fontTools.cffLib.specializer import specializeCommands, commandsToProgram diff --git a/Lib/fontTools/pens/ttGlyphPen.py b/Lib/fontTools/pens/ttGlyphPen.py index f493f1acc..e7841efcb 100644 --- a/Lib/fontTools/pens/ttGlyphPen.py +++ b/Lib/fontTools/pens/ttGlyphPen.py @@ -1,5 +1,6 @@ from array import array from fontTools.misc.fixedTools import MAX_F2DOT14, otRound, floatToFixedToFloat +from fontTools.misc.roundTools import otRound from fontTools.pens.basePen import LoggingPen from fontTools.pens.transformPen import TransformPen from fontTools.ttLib.tables import ttProgram diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index 8162c09c2..c2d402162 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -2,7 +2,7 @@ # # Google Author(s): Behdad Esfahbod -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools import ttLib from fontTools.ttLib.tables import otTables from fontTools.otlLib.maxContextCalc import maxCtxFont diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py index 7db6d880a..b59c6b96d 100644 --- a/Lib/fontTools/subset/cff.py +++ b/Lib/fontTools/subset/cff.py @@ -1,7 +1,7 @@ from fontTools.misc import psCharStrings from fontTools import ttLib from fontTools.pens.basePen import NullPen -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools.varLib.varStore import VarStoreInstancer def _add_method(*clazzes): diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py index a690a6e6e..730574733 100644 --- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py +++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py @@ -1,5 +1,5 @@ from fontTools.misc.py23 import * -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools import ttLib from fontTools.misc.textTools import safeEval from . import DefaultTable diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 008909bdd..a65ec7b87 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -9,7 +9,7 @@ from enum import IntEnum import itertools from collections import namedtuple from fontTools.misc.py23 import * -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import otRound from fontTools.misc.textTools import pad, safeEval from .otBase import ( BaseTable, FormatSwitchingBaseTable, ValueRecord, CountReference, diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py index 071942b86..cb53f04d3 100644 --- a/Lib/fontTools/varLib/merger.py +++ b/Lib/fontTools/varLib/merger.py @@ -3,8 +3,8 @@ Merge OpenType Layout tables (GDEF / GPOS / GSUB). """ import copy from operator import ior -from fontTools.misc.fixedTools import otRound from fontTools.misc import classifyTools +from fontTools.misc.roundTools import otRound from fontTools.ttLib.tables import otTables as ot from fontTools.ttLib.tables import otBase as otBase from fontTools.ttLib.tables.DefaultTable import DefaultTable diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py index ad76420a1..56f36e6ea 100644 --- a/Lib/fontTools/varLib/mutator.py +++ b/Lib/fontTools/varLib/mutator.py @@ -3,7 +3,8 @@ Instantiate a variation font. Run, eg: $ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85 """ -from fontTools.misc.fixedTools import floatToFixedToFloat, otRound, floatToFixed +from fontTools.misc.fixedTools import floatToFixedToFloat, floatToFixed +from fontTools.misc.roundTools import otRound from fontTools.pens.boundsPen import BoundsPen from fontTools.ttLib import TTFont, newTable from fontTools.ttLib.tables import ttProgram From 3a9a2bd4b1afbb75ee33ffff545c50a862484b34 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 18:55:54 -0700 Subject: [PATCH 05/13] [misc.roundTools] Add noRound(), maybeRound(), and roundFunc() Moving out of CFF code, to be used in VariationModel(). Part of https://github.com/fonttools/fonttools/issues/2213 --- Lib/fontTools/misc/roundTools.py | 22 ++++++++++++++++++++++ Lib/fontTools/pens/t2CharStringPen.py | 19 +------------------ Lib/fontTools/varLib/cff.py | 3 ++- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Lib/fontTools/misc/roundTools.py b/Lib/fontTools/misc/roundTools.py index af3e317d2..c1d546f1f 100644 --- a/Lib/fontTools/misc/roundTools.py +++ b/Lib/fontTools/misc/roundTools.py @@ -3,14 +3,21 @@ Various round-to-integer helpers. """ import math +import functools import logging log = logging.getLogger(__name__) __all__ = [ + "noRound", "otRound", + "maybeRound", + "roundFunc", ] +def noRound(value): + return value + def otRound(value): """Round float value to nearest integer towards ``+Infinity``. @@ -34,3 +41,18 @@ def otRound(value): # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166 return int(math.floor(value + 0.5)) +def maybeRound(v, tolerance, round=otRound): + rounded = round(v) + return rounded if abs(rounded - v) <= tolerance else v + +def roundFunc(tolerance, round=otRound): + if tolerance < 0: + raise ValueError("Rounding tolerance must be positive") + + if tolerance == 0: + return noRound + + if tolerance >= .5: + return round + + return functools.partial(maybeRound, tolerance=tolerance, round=round) diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py index 8b718ccbc..0fddec1ae 100644 --- a/Lib/fontTools/pens/t2CharStringPen.py +++ b/Lib/fontTools/pens/t2CharStringPen.py @@ -1,29 +1,12 @@ # Copyright (c) 2009 Type Supply LLC # Author: Tal Leming -from fontTools.misc.roundTools import otRound +from fontTools.misc.roundTools import otRound, roundFunc from fontTools.misc.psCharStrings import T2CharString from fontTools.pens.basePen import BasePen from fontTools.cffLib.specializer import specializeCommands, commandsToProgram -def roundFunc(tolerance, round=otRound): - if tolerance < 0: - raise ValueError("Rounding tolerance must be positive") - - if tolerance == 0: - return lambda x: x - - if tolerance >= .5: - return round - - def maybe_round(v): - rounded = round(v) - return rounded if abs(rounded - v) <= tolerance else v - - return maybe_round - - class T2CharStringPen(BasePen): """Pen to draw Type 2 CharStrings. diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py index 6f704bbee..ae14986e5 100644 --- a/Lib/fontTools/varLib/cff.py +++ b/Lib/fontTools/varLib/cff.py @@ -17,8 +17,9 @@ from fontTools.cffLib.specializer import ( from fontTools.ttLib import newTable from fontTools import varLib from fontTools.varLib.models import allEqual +from fontTools.misc.roundTools import roundFunc from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor -from fontTools.pens.t2CharStringPen import T2CharStringPen, roundFunc +from fontTools.pens.t2CharStringPen import T2CharStringPen from .errors import VarLibCFFDictMergeError, VarLibCFFPointTypeMergeError, VarLibMergeError From fdd8267065acdc506786cab3d8a591807589281a Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 19:03:12 -0700 Subject: [PATCH 06/13] [varLib.models] Add optional rounding to VariationModel() relevant methods Users to be updated to benefit. Part of https://github.com/fonttools/fonttools/issues/2213 --- Lib/fontTools/varLib/models.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py index 9cc40b1c9..417ecb42b 100644 --- a/Lib/fontTools/varLib/models.py +++ b/Lib/fontTools/varLib/models.py @@ -5,6 +5,7 @@ __all__ = ['nonNone', 'allNone', 'allEqual', 'allEqualTo', 'subList', 'supportScalar', 'VariationModel'] +from fontTools.misc.roundTools import noRound from .errors import VariationModelError @@ -358,7 +359,7 @@ class VariationModel(object): self.supports = supports self.deltaWeights = deltaWeights - def getDeltas(self, masterValues): + def getDeltas(self, masterValues, round=noRound): assert len(masterValues) == len(self.deltaWeights) mapping = self.reverseMapping out = [] @@ -366,12 +367,12 @@ class VariationModel(object): delta = masterValues[mapping[i]] for j,weight in weights.items(): delta -= out[j] * weight - out.append(delta) + out.append(round(delta)) return out - def getDeltasAndSupports(self, items): + def getDeltasAndSupports(self, items, round=noRound): model, items = self.getSubModel(items) - return model.getDeltas(items), model.supports + return model.getDeltas(items, round=round), model.supports def getScalars(self, loc): return [supportScalar(loc, support) for support in self.supports] @@ -393,12 +394,12 @@ class VariationModel(object): scalars = self.getScalars(loc) return self.interpolateFromDeltasAndScalars(deltas, scalars) - def interpolateFromMasters(self, loc, masterValues): - deltas = self.getDeltas(masterValues) + def interpolateFromMasters(self, loc, masterValues, round=noRound): + deltas = self.getDeltas(masterValues, round=round) return self.interpolateFromDeltas(loc, deltas) - def interpolateFromMastersAndScalars(self, masterValues, scalars): - deltas = self.getDeltas(masterValues) + def interpolateFromMastersAndScalars(self, masterValues, scalars, round=noRound): + deltas = self.getDeltas(masterValues, round=round) return self.interpolateFromDeltasAndScalars(deltas, scalars) From 77acdbced3a3ad0109992194fe43e3dfee64755f Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 19:36:10 -0700 Subject: [PATCH 07/13] [glyf] Remove stale comment about Python 2 --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index e12969e40..9886d7ab3 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -1627,10 +1627,6 @@ class GlyphCoordinates(object): a[i] = -a[i] return r def __round__(self): - """ - Note: This is Python 3 only. Python 2 does not call __round__. - As such, we cannot test this method either. :( - """ r = self.copy() r.toInt() return r From 68004b8fec94729b7cfe9755eb956fa39feff5de Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 3 Mar 2021 19:37:50 -0700 Subject: [PATCH 08/13] [varLib] Shift most (all?) delta-rounding to VarModel Reduces error. The main varfont-builder now asks the model to do rounding, and asks VariationStore to do no rounding, so we don't spend extra times rounding multiple times (specially with the heavy otRound). I *think* I got it all and right... Fixes https://github.com/fonttools/fonttools/issues/2213 --- Lib/fontTools/varLib/__init__.py | 23 +++++++++---------- Lib/fontTools/varLib/cff.py | 7 +++--- Lib/fontTools/varLib/varStore.py | 21 ++++++++--------- .../data/test_results/TestSparseCFF2VF.ttx | 2 +- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index dd320b040..4172dd13c 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -19,7 +19,7 @@ Then you can make a variable-font this way: API *will* change in near future. """ from fontTools.misc.py23 import * -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import noRound, otRound from fontTools.misc.vector import Vector from fontTools.ttLib import TTFont, newTable from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance @@ -253,7 +253,7 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True): # Update gvar gvar.variations[glyph] = [] - deltas = model.getDeltas(allCoords) + deltas = model.getDeltas(allCoords, round=round) # builtin round calls into GlyphCoordinates.__round__() supports = model.supports assert len(deltas) == len(supports) @@ -262,7 +262,7 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True): endPts = control.endPts for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])): - if all(abs(v) <= tolerance for v in delta.array) and not isComposite: + if all(v == 0 for v in delta.array) and not isComposite: continue var = TupleVariation(support, delta) if optimize: @@ -304,7 +304,7 @@ def _remove_TTHinting(font): font["glyf"].removeHinting() # TODO: Modify gasp table to deactivate gridfitting for all ranges? -def _merge_TTHinting(font, masterModel, master_ttfs, tolerance=0.5): +def _merge_TTHinting(font, masterModel, master_ttfs): log.info("Merging TT hinting") assert "cvar" not in font @@ -363,10 +363,9 @@ def _merge_TTHinting(font, masterModel, master_ttfs, tolerance=0.5): return variations = [] - deltas, supports = masterModel.getDeltasAndSupports(all_cvs) + deltas, supports = masterModel.getDeltasAndSupports(all_cvs, round=round) # builtin round calls into Vector.__round__ for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])): - delta = [otRound(d) for d in delta] - if all(abs(v) <= tolerance for v in delta): + if all(v == 0 for v in delta): continue var = TupleVariation(support, delta) variations.append(var) @@ -441,7 +440,7 @@ def _get_advance_metrics(font, masterModel, master_ttfs, vOrigDeltasAndSupports = {} for glyph in glyphOrder: vhAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses] - vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances) + vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances, round=otRound) singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values()) @@ -453,7 +452,7 @@ def _get_advance_metrics(font, masterModel, master_ttfs, # glyphs which have a non-default vOrig. vOrigs = [metrics[glyph] if glyph in metrics else defaultVOrig for metrics, defaultVOrig in vOrigMetricses] - vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs) + vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs, round=otRound) directStore = None if singleModel: @@ -463,7 +462,7 @@ def _get_advance_metrics(font, masterModel, master_ttfs, varTupleIndexes = list(range(len(supports))) varData = builder.buildVarData(varTupleIndexes, [], optimize=False) for glyphName in glyphOrder: - varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0]) + varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0], round=noRound) varData.optimize() directStore = builder.buildVarStore(varTupleList, [varData]) @@ -473,14 +472,14 @@ def _get_advance_metrics(font, masterModel, master_ttfs, for glyphName in glyphOrder: deltas, supports = vhAdvanceDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) - advMapping[glyphName] = storeBuilder.storeDeltas(deltas) + advMapping[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound) if vOrigMetricses: vOrigMap = {} for glyphName in glyphOrder: deltas, supports = vOrigDeltasAndSupports[glyphName] storeBuilder.setSupports(supports) - vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas) + vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound) indirectStore = storeBuilder.finish() mapping2 = indirectStore.optimize() diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py index ae14986e5..1a6bf14c3 100644 --- a/Lib/fontTools/varLib/cff.py +++ b/Lib/fontTools/varLib/cff.py @@ -20,6 +20,7 @@ from fontTools.varLib.models import allEqual from fontTools.misc.roundTools import roundFunc from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor from fontTools.pens.t2CharStringPen import T2CharStringPen +from functools import partial from .errors import VarLibCFFDictMergeError, VarLibCFFPointTypeMergeError, VarLibMergeError @@ -585,7 +586,7 @@ class CFF2CharStringMergePen(T2CharStringPen): def getCommands(self): return self._commands - def reorder_blend_args(self, commands, get_delta_func, round_func): + def reorder_blend_args(self, commands, get_delta_func): """ We first re-order the master coordinate values. For a moveto to lineto, the args are now arranged as: @@ -628,8 +629,6 @@ class CFF2CharStringMergePen(T2CharStringPen): else: # convert to deltas deltas = get_delta_func(coord)[1:] - if round_func: - deltas = [round_func(delta) for delta in deltas] coord = [coord[0]] + deltas new_coords.append(coord) cmd[1] = new_coords @@ -640,7 +639,7 @@ class CFF2CharStringMergePen(T2CharStringPen): self, private=None, globalSubrs=None, var_model=None, optimize=True): commands = self._commands - commands = self.reorder_blend_args(commands, var_model.getDeltas, self.round) + commands = self.reorder_blend_args(commands, partial (var_model.getDeltas, round=self.round)) if optimize: commands = specializeCommands( commands, generalizeFirst=False, diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index b28d2a657..6d2fcebc4 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -1,4 +1,4 @@ -from fontTools.misc.fixedTools import otRound +from fontTools.misc.roundTools import noRound, otRound from fontTools.ttLib.tables import otTables as ot from fontTools.varLib.models import supportScalar from fontTools.varLib.builder import (buildVarRegionList, buildVarStore, @@ -83,15 +83,12 @@ class OnlineVarStoreBuilder(object): def storeMasters(self, master_values): - deltas = self._model.getDeltas(master_values) - base = otRound(deltas.pop(0)) - return base, self.storeDeltas(deltas) + deltas = self._model.getDeltas(master_values, round=otRound) + base = deltas.pop(0) + return base, self.storeDeltas(deltas, round=noRound) - def storeDeltas(self, deltas): - # Pity that this exists here, since VarData_addItem - # does the same. But to look into our cache, it's - # good to adjust deltas here as well... - deltas = [otRound(d) for d in deltas] + def storeDeltas(self, deltas, round=otRound): + deltas = [round(d) for d in deltas] if len(deltas) == len(self._supports) + 1: deltas = tuple(deltas[1:]) else: @@ -109,14 +106,14 @@ class OnlineVarStoreBuilder(object): # Full array. Start new one. self._add_VarData() return self.storeDeltas(deltas) - self._data.addItem(deltas) + self._data.addItem(deltas, round=noRound) varIdx = (self._outer << 16) + inner self._cache[deltas] = varIdx return varIdx -def VarData_addItem(self, deltas): - deltas = [otRound(d) for d in deltas] +def VarData_addItem(self, deltas, round=otRound): + deltas = [round(d) for d in deltas] countUs = self.VarRegionCount countThem = len(deltas) diff --git a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx index f05f62f74..a4fc03cd0 100644 --- a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx +++ b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx @@ -290,7 +290,7 @@ 2 vsindex - -60 30 203 30 -9 9 67 7 -7 14 -14 30 -20 20 80 30 59 30 121 30 18 93 -30 30 -30 108 -23 0 -26 67 2 76 -98 -2 -111 42 0 47 -13 0 -14 13 0 14 -33 0 -37 11 0 13 -11 0 -13 8 0 9 -7 0 -8 53 0 60 -32 0 -36 32 0 36 -52 0 -59 57 1 65 -33 0 -38 53 0 60 -83 -1 -93 54 0 60 -6 -19 -24 33 19 55 -76 -1 -86 76 1 86 -76 -1 -86 59 1 67 26 blend + -60 30 203 30 -9 9 67 7 -7 14 -14 30 -20 20 80 30 59 30 121 30 18 93 -30 30 -30 108 -23 0 -26 67 2 76 -98 -2 -111 42 0 47 -13 0 -14 13 0 14 -33 0 -37 11 0 13 -11 0 -13 8 0 9 -7 -1 -8 53 0 60 -32 0 -36 32 0 36 -52 0 -59 57 1 65 -33 0 -38 53 0 60 -83 -1 -93 54 0 60 -6 -19 -24 33 19 55 -76 -1 -86 76 1 86 -76 -1 -86 59 1 67 26 blend hstemhm 77 30 42 30 139 30 23 30 71 10 74 30 15 30 16 30 158 30 28 30 -4 29 -14 0 -16 88 1 99 -82 -1 -92 87 1 98 -130 -1 -146 102 1 114 -73 -1 -82 74 2 84 -112 -2 -126 27 0 30 13 0 15 90 1 101 -126 -1 -142 75 1 84 -68 -1 -76 102 1 115 -144 -1 -162 94 1 105 -79 -1 -88 95 1 106 -81 -1 -91 74 1 83 22 blend vstemhm From 606b072f409c79075c7ac2fd6160612e2c3da435 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 4 Mar 2021 09:31:15 -0700 Subject: [PATCH 09/13] [glyf] Add round=otRound parameter to GlyphCoordinates.toInt() & __round__() --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 9886d7ab3..b8ed20d5e 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -1506,12 +1506,12 @@ class GlyphCoordinates(object): p = self._checkFloat(p) self._a.extend(p) - def toInt(self): + def toInt(self, *, round=otRound): if not self.isFloat(): return a = array.array("h") for n in self._a: - a.append(otRound(n)) + a.append(round(n)) self._a = a def relativeToAbsolute(self): @@ -1626,9 +1626,9 @@ class GlyphCoordinates(object): for i in range(len(a)): a[i] = -a[i] return r - def __round__(self): + def __round__(self, *, round=otRound): r = self.copy() - r.toInt() + r.toInt(round=round) return r def __add__(self, other): return self.copy().__iadd__(other) From f5ef0b517bb0d037fe21e7409a9090f54d9672a7 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 4 Mar 2021 09:37:43 -0700 Subject: [PATCH 10/13] [varLib] Round most deltas using bankers' round, not otRound See https://github.com/fonttools/fonttools/pull/2214#issuecomment-790742479 --- Lib/fontTools/varLib/__init__.py | 4 ++-- Lib/fontTools/varLib/varStore.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 4172dd13c..c2c5ed4c9 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -440,7 +440,7 @@ def _get_advance_metrics(font, masterModel, master_ttfs, vOrigDeltasAndSupports = {} for glyph in glyphOrder: vhAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses] - vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances, round=otRound) + vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances, round=round) singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values()) @@ -452,7 +452,7 @@ def _get_advance_metrics(font, masterModel, master_ttfs, # glyphs which have a non-default vOrig. vOrigs = [metrics[glyph] if glyph in metrics else defaultVOrig for metrics, defaultVOrig in vOrigMetricses] - vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs, round=otRound) + vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs, round=round) directStore = None if singleModel: diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index 6d2fcebc4..905f47089 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -83,11 +83,11 @@ class OnlineVarStoreBuilder(object): def storeMasters(self, master_values): - deltas = self._model.getDeltas(master_values, round=otRound) + deltas = self._model.getDeltas(master_values, round=round) base = deltas.pop(0) return base, self.storeDeltas(deltas, round=noRound) - def storeDeltas(self, deltas, round=otRound): + def storeDeltas(self, deltas, round=round): deltas = [round(d) for d in deltas] if len(deltas) == len(self._supports) + 1: deltas = tuple(deltas[1:]) @@ -112,7 +112,7 @@ class OnlineVarStoreBuilder(object): self._cache[deltas] = varIdx return varIdx -def VarData_addItem(self, deltas, round=otRound): +def VarData_addItem(self, deltas, round=round): deltas = [round(d) for d in deltas] countUs = self.VarRegionCount From 07a7dd3eb544c8b6b7b49acb48b3aecea7e5feba Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 4 Mar 2021 09:44:34 -0700 Subject: [PATCH 11/13] [varLib] Make newly-added round arguments keyword-only So they are not accidentally set, only intentionally. Keeps positional-args open for more direct uses. --- Lib/fontTools/varLib/models.py | 8 ++++---- Lib/fontTools/varLib/varStore.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py index 417ecb42b..8c71058b6 100644 --- a/Lib/fontTools/varLib/models.py +++ b/Lib/fontTools/varLib/models.py @@ -359,7 +359,7 @@ class VariationModel(object): self.supports = supports self.deltaWeights = deltaWeights - def getDeltas(self, masterValues, round=noRound): + def getDeltas(self, masterValues, *, round=noRound): assert len(masterValues) == len(self.deltaWeights) mapping = self.reverseMapping out = [] @@ -370,7 +370,7 @@ class VariationModel(object): out.append(round(delta)) return out - def getDeltasAndSupports(self, items, round=noRound): + def getDeltasAndSupports(self, items, *, round=noRound): model, items = self.getSubModel(items) return model.getDeltas(items, round=round), model.supports @@ -394,11 +394,11 @@ class VariationModel(object): scalars = self.getScalars(loc) return self.interpolateFromDeltasAndScalars(deltas, scalars) - def interpolateFromMasters(self, loc, masterValues, round=noRound): + def interpolateFromMasters(self, loc, masterValues, *, round=noRound): deltas = self.getDeltas(masterValues, round=round) return self.interpolateFromDeltas(loc, deltas) - def interpolateFromMastersAndScalars(self, masterValues, scalars, round=noRound): + def interpolateFromMastersAndScalars(self, masterValues, scalars, *, round=noRound): deltas = self.getDeltas(masterValues, round=round) return self.interpolateFromDeltasAndScalars(deltas, scalars) diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index 905f47089..8a382df01 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -87,7 +87,7 @@ class OnlineVarStoreBuilder(object): base = deltas.pop(0) return base, self.storeDeltas(deltas, round=noRound) - def storeDeltas(self, deltas, round=round): + def storeDeltas(self, deltas, *, round=round): deltas = [round(d) for d in deltas] if len(deltas) == len(self._supports) + 1: deltas = tuple(deltas[1:]) @@ -112,7 +112,7 @@ class OnlineVarStoreBuilder(object): self._cache[deltas] = varIdx return varIdx -def VarData_addItem(self, deltas, round=round): +def VarData_addItem(self, deltas, *, round=round): deltas = [round(d) for d in deltas] countUs = self.VarRegionCount From d56b844142c3b08689dde1d28f19cdf379eb2c8f Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 4 Mar 2021 13:00:04 -0700 Subject: [PATCH 12/13] [varLib] Round outline deltas using banker's round as well See https://github.com/fonttools/fonttools/pull/2214#issuecomment-790742479 --- Lib/fontTools/misc/vector.py | 2 +- Lib/fontTools/varLib/__init__.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/misc/vector.py b/Lib/fontTools/misc/vector.py index 995385c04..244436e0a 100644 --- a/Lib/fontTools/misc/vector.py +++ b/Lib/fontTools/misc/vector.py @@ -71,7 +71,7 @@ class Vector(tuple): def __neg__(self): return self._unaryOp(operator.neg) - def __round__(self): + def __round__(self, *, round=round): return self._unaryOp(round) def __eq__(self, other): diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index c2c5ed4c9..995159bcd 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -34,6 +34,7 @@ from fontTools.varLib.mvar import MVAR_ENTRIES from fontTools.varLib.iup import iup_delta_optimize from fontTools.varLib.featureVars import addFeatureVariations from fontTools.designspaceLib import DesignSpaceDocument +from functools import partial from collections import OrderedDict, namedtuple import os.path import logging @@ -253,7 +254,7 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True): # Update gvar gvar.variations[glyph] = [] - deltas = model.getDeltas(allCoords, round=round) # builtin round calls into GlyphCoordinates.__round__() + deltas = model.getDeltas(allCoords, round=partial(GlyphCoordinates.__round__, round=round)) supports = model.supports assert len(deltas) == len(supports) @@ -363,7 +364,7 @@ def _merge_TTHinting(font, masterModel, master_ttfs): return variations = [] - deltas, supports = masterModel.getDeltasAndSupports(all_cvs, round=round) # builtin round calls into Vector.__round__ + deltas, supports = masterModel.getDeltasAndSupports(all_cvs, round=round) # builtin round calls into Vector.__round__, which uses builtin round as we like for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])): if all(v == 0 for v in delta): continue From ed6164a44f685a448d04d7822d6d73767eddcf3c Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 4 Mar 2021 13:01:09 -0700 Subject: [PATCH 13/13] [varLib.cff] Round deltas using bankers round as well See https://github.com/fonttools/fonttools/pull/2214#issuecomment-790742479 Good news: this reverted the one CFF test that needed its expectation to change when I started this patchset. Bad news: it introduced a couple other diffs to the same glyph of the same test, which is consistent with changing the rounding from otRound to round3. --- Lib/fontTools/varLib/cff.py | 2 +- Tests/varLib/data/test_results/TestSparseCFF2VF.ttx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py index 1a6bf14c3..db1889888 100644 --- a/Lib/fontTools/varLib/cff.py +++ b/Lib/fontTools/varLib/cff.py @@ -510,7 +510,7 @@ class CFF2CharStringMergePen(T2CharStringPen): self.prev_move_idx = 0 self.seen_moveto = False self.glyphName = glyphName - self.round = roundFunc(roundTolerance) + self.round = roundFunc(roundTolerance, round=round) def add_point(self, point_type, pt_coords): if self.m_index == 0: diff --git a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx index a4fc03cd0..5bbd925c7 100644 --- a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx +++ b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx @@ -290,7 +290,7 @@ 2 vsindex - -60 30 203 30 -9 9 67 7 -7 14 -14 30 -20 20 80 30 59 30 121 30 18 93 -30 30 -30 108 -23 0 -26 67 2 76 -98 -2 -111 42 0 47 -13 0 -14 13 0 14 -33 0 -37 11 0 13 -11 0 -13 8 0 9 -7 -1 -8 53 0 60 -32 0 -36 32 0 36 -52 0 -59 57 1 65 -33 0 -38 53 0 60 -83 -1 -93 54 0 60 -6 -19 -24 33 19 55 -76 -1 -86 76 1 86 -76 -1 -86 59 1 67 26 blend + -60 30 203 30 -9 9 67 7 -7 14 -14 30 -20 20 80 30 59 30 121 30 18 93 -30 30 -30 108 -23 0 -26 67 2 76 -98 -2 -111 42 0 47 -13 0 -14 13 0 14 -33 0 -37 11 0 13 -11 0 -13 8 0 8 -8 0 -8 53 0 60 -32 0 -36 32 0 36 -52 0 -59 57 1 65 -33 0 -38 53 0 60 -83 -1 -93 54 0 60 -6 -19 -24 33 19 55 -76 -1 -86 76 1 86 -76 -1 -86 59 1 67 26 blend hstemhm 77 30 42 30 139 30 23 30 71 10 74 30 15 30 16 30 158 30 28 30 -4 29 -14 0 -16 88 1 99 -82 -1 -92 87 1 98 -130 -1 -146 102 1 114 -73 -1 -82 74 2 84 -112 -2 -126 27 0 30 13 0 15 90 1 101 -126 -1 -142 75 1 84 -68 -1 -76 102 1 115 -144 -1 -162 94 1 105 -79 -1 -88 95 1 106 -81 -1 -91 74 1 83 22 blend vstemhm