Merge pull request #2214 from fonttools/varLib-round
[varLib] Move rounding to VariationModel, to avoid error accumulation from multiple deltas
This commit is contained in:
commit
062b7c434e
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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 <https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>`_)
|
||||
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.
|
||||
|
58
Lib/fontTools/misc/roundTools.py
Normal file
58
Lib/fontTools/misc/roundTools.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""
|
||||
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``.
|
||||
|
||||
The OpenType spec (in the section on `"normalization" of OpenType Font Variations <https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>`_)
|
||||
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 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)
|
@ -76,7 +76,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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,36 +1,12 @@
|
||||
# Copyright (c) 2009 Type Supply LLC
|
||||
# Author: Tal Leming
|
||||
|
||||
from fontTools.misc.fixedTools 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 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 makeRoundFunc(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)
|
||||
|
||||
return roundPoint
|
||||
|
||||
|
||||
class T2CharStringPen(BasePen):
|
||||
"""Pen to draw Type 2 CharStrings.
|
||||
|
||||
@ -44,7 +20,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 +28,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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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,13 +1626,9 @@ class GlyphCoordinates(object):
|
||||
for i in range(len(a)):
|
||||
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. :(
|
||||
"""
|
||||
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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -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)
|
||||
deltas = model.getDeltas(allCoords, round=partial(GlyphCoordinates.__round__, round=round))
|
||||
supports = model.supports
|
||||
assert len(deltas) == len(supports)
|
||||
|
||||
@ -262,7 +263,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 +305,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 +364,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__, which uses builtin round as we like
|
||||
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 +441,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=round)
|
||||
|
||||
singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values())
|
||||
|
||||
@ -453,7 +453,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=round)
|
||||
|
||||
directStore = None
|
||||
if singleModel:
|
||||
@ -463,7 +463,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 +473,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()
|
||||
|
@ -17,8 +17,10 @@ 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, t2c_round
|
||||
from fontTools.pens.t2CharStringPen import T2CharStringPen
|
||||
from functools import partial
|
||||
|
||||
from .errors import VarLibCFFDictMergeError, VarLibCFFPointTypeMergeError, VarLibMergeError
|
||||
|
||||
@ -422,16 +424,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 +510,7 @@ class CFF2CharStringMergePen(T2CharStringPen):
|
||||
self.prev_move_idx = 0
|
||||
self.seen_moveto = False
|
||||
self.glyphName = glyphName
|
||||
self.roundNumber = makeRoundNumberFunc(roundTolerance)
|
||||
self.round = roundFunc(roundTolerance, round=round)
|
||||
|
||||
def add_point(self, point_type, pt_coords):
|
||||
if self.m_index == 0:
|
||||
@ -594,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:
|
||||
@ -637,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
|
||||
@ -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, partial (var_model.getDeltas, round=self.round))
|
||||
if optimize:
|
||||
commands = specializeCommands(
|
||||
commands, generalizeFirst=False,
|
||||
|
@ -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
|
||||
|
@ -5,6 +5,7 @@ __all__ = ['nonNone', 'allNone', 'allEqual', 'allEqualTo', 'subList',
|
||||
'supportScalar',
|
||||
'VariationModel']
|
||||
|
||||
from fontTools.misc.roundTools import noRound
|
||||
from .errors import VariationModelError
|
||||
|
||||
|
||||
@ -367,7 +368,7 @@ class VariationModel(object):
|
||||
deltaWeights.append(deltaWeight)
|
||||
self.deltaWeights = deltaWeights
|
||||
|
||||
def getDeltas(self, masterValues):
|
||||
def getDeltas(self, masterValues, *, round=noRound):
|
||||
assert len(masterValues) == len(self.deltaWeights)
|
||||
mapping = self.reverseMapping
|
||||
out = []
|
||||
@ -375,12 +376,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]
|
||||
@ -402,12 +403,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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -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)
|
||||
|
@ -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=round)
|
||||
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=round):
|
||||
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=round):
|
||||
deltas = [round(d) for d in deltas]
|
||||
|
||||
countUs = self.VarRegionCount
|
||||
countThem = len(deltas)
|
||||
|
@ -290,7 +290,7 @@
|
||||
</CharString>
|
||||
<CharString name="cid06449" fdSelectIndex="1">
|
||||
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 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user