aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2019-03-26 08:57:41 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-03-26 08:57:41 -0700
commita7883b52840b4c2412f296e03722abaff64cab47 (patch)
treefd26782cbbf4c73650f8c5b42ebcb2b2e2b2be10
parent1c9fe670c2f9e440bcf085f682803cd3956ecb72 (diff)
parentc30bf2b15125e02d14893fb657b8a80e3c80cbf6 (diff)
downloadfonttools-a7883b52840b4c2412f296e03722abaff64cab47.tar.gz
Upgrade fonttools to 3.39.0 am: a2a985d834 am: 195ed3228f
am: c30bf2b151 Change-Id: I14f2d1dbafadb024cdceb2c5c0514ad0476644a8
-rw-r--r--Doc/source/designspaceLib/readme.rst19
-rw-r--r--Doc/source/index.rst1
-rw-r--r--Doc/source/inspect.rst7
-rw-r--r--Lib/fontTools/__init__.py2
-rw-r--r--Lib/fontTools/designspaceLib/__init__.py55
-rw-r--r--Lib/fontTools/encodings/codecs.py2
-rw-r--r--Lib/fontTools/feaLib/ast.py25
-rw-r--r--Lib/fontTools/feaLib/builder.py146
-rw-r--r--Lib/fontTools/feaLib/parser.py14
-rw-r--r--Lib/fontTools/inspect.py271
-rw-r--r--Lib/fontTools/merge.py37
-rw-r--r--Lib/fontTools/misc/encodingTools.py2
-rw-r--r--Lib/fontTools/misc/psCharStrings.py3
-rw-r--r--Lib/fontTools/misc/py23.py8
-rw-r--r--Lib/fontTools/misc/timeTools.py2
-rw-r--r--Lib/fontTools/misc/xmlWriter.py2
-rw-r--r--Lib/fontTools/pens/pointPen.py3
-rw-r--r--Lib/fontTools/subset/__init__.py21
-rw-r--r--Lib/fontTools/subset/cff.py2
-rw-r--r--Lib/fontTools/ttLib/tables/C_O_L_R_.py10
-rw-r--r--Lib/fontTools/ttLib/tables/E_B_L_C_.py2
-rw-r--r--Lib/fontTools/ttLib/tables/G__l_a_t.py4
-rw-r--r--Lib/fontTools/ttLib/tables/TupleVariation.py8
-rw-r--r--Lib/fontTools/ttLib/tables/V_O_R_G_.py16
-rw-r--r--Lib/fontTools/ttLib/tables/_c_m_a_p.py32
-rw-r--r--Lib/fontTools/ttLib/tables/_g_l_y_f.py5
-rw-r--r--Lib/fontTools/ttLib/tables/_h_e_a_d.py2
-rw-r--r--Lib/fontTools/ttLib/tables/grUtils.py4
-rw-r--r--Lib/fontTools/ttLib/tables/otBase.py2
-rw-r--r--Lib/fontTools/ttLib/tables/otTables.py8
-rw-r--r--Lib/fontTools/varLib/__init__.py56
-rw-r--r--Lib/fontTools/varLib/models.py34
-rw-r--r--Lib/fontTools/varLib/mutator.py25
-rw-r--r--Lib/fontTools/varLib/plot.py93
-rw-r--r--Lib/fonttools.egg-info/PKG-INFO62
-rw-r--r--Lib/fonttools.egg-info/SOURCES.txt14
-rw-r--r--Lib/fonttools.egg-info/entry_points.txt1
-rw-r--r--METADATA6
-rw-r--r--NEWS.rst29
-rw-r--r--PKG-INFO62
-rw-r--r--README.rst19
-rwxr-xr-xSnippets/svg2glif.py4
-rw-r--r--Tests/designspaceLib/data/test.designspace3
-rw-r--r--Tests/designspaceLib/designspace_test.py66
-rw-r--r--Tests/feaLib/builder_test.py26
-rw-r--r--Tests/feaLib/data/AlternateSubtable.fea5
-rw-r--r--Tests/feaLib/data/AlternateSubtable.ttx52
-rw-r--r--Tests/feaLib/data/ChainPosSubtable.fea7
-rw-r--r--Tests/feaLib/data/ChainPosSubtable.ttx182
-rw-r--r--Tests/feaLib/data/ChainSubstSubtable.fea9
-rw-r--r--Tests/feaLib/data/ChainSubstSubtable.ttx124
-rw-r--r--Tests/feaLib/data/GPOS_1_zero.fea2
-rw-r--r--Tests/feaLib/data/GPOS_2.fea2
-rw-r--r--Tests/feaLib/data/LigatureSubtable.fea5
-rw-r--r--Tests/feaLib/data/LigatureSubtable.ttx50
-rw-r--r--Tests/feaLib/data/MultipleSubstSubtable.fea5
-rw-r--r--Tests/feaLib/data/MultipleSubstSubtable.ttx46
-rw-r--r--Tests/feaLib/data/SingleSubstSubtable.fea5
-rw-r--r--Tests/feaLib/data/SingleSubstSubtable.ttx46
-rw-r--r--Tests/feaLib/data/bug453.fea2
-rw-r--r--Tests/feaLib/data/bug463.fea2
-rw-r--r--Tests/feaLib/data/bug501.fea2
-rw-r--r--Tests/feaLib/data/bug502.fea2
-rw-r--r--Tests/feaLib/data/bug504.fea2
-rw-r--r--Tests/feaLib/data/bug505.fea2
-rw-r--r--Tests/feaLib/data/bug506.fea2
-rw-r--r--Tests/feaLib/data/bug568.fea2
-rw-r--r--Tests/feaLib/parser_test.py32
-rw-r--r--Tests/misc/xmlWriter_test.py6
-rw-r--r--Tests/subset/subset_test.py2
-rw-r--r--Tests/ttLib/data/TestOTF-Regular.otx12
-rw-r--r--Tests/ttLib/data/TestTTF-Regular.ttx12
-rw-r--r--Tests/ttLib/tables/TupleVariation_test.py8
-rw-r--r--Tests/ttLib/tables/_c_m_a_p_test.py41
-rw-r--r--Tests/ttLib/tables/_g_l_y_f_test.py27
-rw-r--r--Tests/ttLib/tables/_g_v_a_r_test.py2
-rw-r--r--Tests/ttLib/tables/_n_a_m_e_test.py2
-rw-r--r--Tests/ttLib/tables/otTables_test.py2
-rw-r--r--Tests/ttx/data/TestOTF.ttx12
-rw-r--r--Tests/ttx/data/TestTTF.ttx12
-rw-r--r--Tests/varLib/data/test_results/Build.ttx22
-rw-r--r--Tests/varLib/data/test_results/BuildMain.ttx22
-rw-r--r--Tests/varLib/data/test_results/Mutator.ttx10
-rwxr-xr-xTests/varLib/data/test_results/Mutator_Getvar-instance.ttx6
-rwxr-xr-xTests/varLib/data/test_results/Mutator_IUP-instance.ttx6
-rw-r--r--Tests/varLib/data/test_results/SparseMasters.ttx13
-rw-r--r--Tests/varLib/models_test.py175
-rw-r--r--requirements.txt4
-rw-r--r--setup.cfg2
-rwxr-xr-xsetup.py3
90 files changed, 1470 insertions, 734 deletions
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
index 68a70091..2594084a 100644
--- a/Doc/source/designspaceLib/readme.rst
+++ b/Doc/source/designspaceLib/readme.rst
@@ -1044,9 +1044,24 @@ Recommendation for editors
- Use ``documentObject.updateFilenameFromPath()`` to explicitly set the
**filename** attributes for all instance and source descriptors.
-.. 7-this-document:
+.. 7-common-lib-key-registry:
-7 This document
+7 Common Lib Key Registry
+=========================
+
+public.skipExportGlyphs
+-----------------------
+
+This lib key works the same as the UFO lib key with the same name. The
+difference is that applications using a Designspace as the corner stone of the
+font compilation process should use the lib key in that Designspace instead of
+any of the UFOs. If the lib key is empty or not present in the Designspace, all
+glyphs should be exported, regardless of what the same lib key in any of the
+UFOs says.
+
+.. 8-this-document:
+
+8 This document
===============
- The package is rather new and changes are to be expected.
diff --git a/Doc/source/index.rst b/Doc/source/index.rst
index 67ec88ee..b3d44cf0 100644
--- a/Doc/source/index.rst
+++ b/Doc/source/index.rst
@@ -8,7 +8,6 @@ fontTools Docs
agl
cffLib
designspaceLib/index
- inspect
encodings
feaLib
merge
diff --git a/Doc/source/inspect.rst b/Doc/source/inspect.rst
deleted file mode 100644
index e0f65c2d..00000000
--- a/Doc/source/inspect.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-#######
-inspect
-#######
-
-.. automodule:: fontTools.inspect
- :members:
- :undoc-members:
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index 99dc4e19..b0b99739 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -5,6 +5,6 @@ from fontTools.misc.loggingTools import configLogger
log = logging.getLogger(__name__)
-version = __version__ = "3.38.0"
+version = __version__ = "3.39.0"
__all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
index b8b962fc..4c5be8b4 100644
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -1128,11 +1128,14 @@ class DesignSpaceDocument(LogMixin, AsDictMixin):
self.rules.append(ruleDescriptor)
def newDefaultLocation(self):
+ """Return default location in design space."""
# Without OrderedDict, output XML would be non-deterministic.
# https://github.com/LettError/designSpaceDocument/issues/10
loc = collections.OrderedDict()
for axisDescriptor in self.axes:
- loc[axisDescriptor.name] = axisDescriptor.default
+ loc[axisDescriptor.name] = axisDescriptor.map_forward(
+ axisDescriptor.default
+ )
return loc
def updateFilenameFromPath(self, masters=True, instances=True, force=False):
@@ -1176,43 +1179,43 @@ class DesignSpaceDocument(LogMixin, AsDictMixin):
return None
def findDefault(self):
- # new default finder
- # take the sourcedescriptor with the location at all the defaults
- # if we can't find it, return None, let someone else figure it out
+ """Set and return SourceDescriptor at the default location or None.
+
+ The default location is the set of all `default` values in user space
+ of all axes.
+ """
self.default = None
+
+ # Convert the default location from user space to design space before comparing
+ # it against the SourceDescriptor locations (always in design space).
+ default_location_design = {
+ axis.name: axis.map_forward(self.defaultLoc[axis.name])
+ for axis in self.axes
+ }
+
for sourceDescriptor in self.sources:
- if sourceDescriptor.location == self.defaultLoc:
- # we choose you!
+ if sourceDescriptor.location == default_location_design:
self.default = sourceDescriptor
return sourceDescriptor
+
return None
def normalizeLocation(self, location):
- # adapted from fontTools.varlib.models.normalizeLocation because:
- # - this needs to work with axis names, not tags
- # - this needs to accomodate anisotropic locations
- # - the axes are stored differently here, it's just math
+ from fontTools.varLib.models import normalizeValue
+
new = {}
for axis in self.axes:
if axis.name not in location:
# skipping this dimension it seems
continue
- v = location.get(axis.name, axis.default)
- if type(v) == tuple:
- v = v[0]
- if v == axis.default:
- v = 0.0
- elif v < axis.default:
- if axis.default == axis.minimum:
- v = 0.0
- else:
- v = (max(v, axis.minimum) - axis.default) / (axis.default - axis.minimum)
- else:
- if axis.default == axis.maximum:
- v = 0.0
- else:
- v = (min(v, axis.maximum) - axis.default) / (axis.maximum - axis.default)
- new[axis.name] = v
+ value = location[axis.name]
+ # 'anisotropic' location, take first coord only
+ if isinstance(value, tuple):
+ value = value[0]
+ triple = [
+ axis.map_forward(v) for v in (axis.minimum, axis.default, axis.maximum)
+ ]
+ new[axis.name] = normalizeValue(value, triple)
return new
def normalize(self):
diff --git a/Lib/fontTools/encodings/codecs.py b/Lib/fontTools/encodings/codecs.py
index 30e46914..5e401f06 100644
--- a/Lib/fontTools/encodings/codecs.py
+++ b/Lib/fontTools/encodings/codecs.py
@@ -1,5 +1,5 @@
"""Extend the Python codecs module with a few encodings that are used in OpenType (name table)
-but missing from Python. See https://github.com/behdad/fonttools/issues/236 for details."""
+but missing from Python. See https://github.com/fonttools/fonttools/issues/236 for details."""
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py
index 7bf64981..39dc4bcf 100644
--- a/Lib/fontTools/feaLib/ast.py
+++ b/Lib/fontTools/feaLib/ast.py
@@ -821,19 +821,20 @@ class LookupFlagStatement(Statement):
markAttach, markFilter)
def asFea(self, indent=""):
- res = "lookupflag"
+ res = []
flags = ["RightToLeft", "IgnoreBaseGlyphs", "IgnoreLigatures", "IgnoreMarks"]
curr = 1
for i in range(len(flags)):
if self.value & curr != 0:
- res += " " + flags[i]
+ res.append(flags[i])
curr = curr << 1
if self.markAttachment is not None:
- res += " MarkAttachmentType {}".format(self.markAttachment.asFea())
+ res.append("MarkAttachmentType {}".format(self.markAttachment.asFea()))
if self.markFilteringSet is not None:
- res += " UseMarkFilteringSet {}".format(self.markFilteringSet.asFea())
- res += ";"
- return res
+ res.append("UseMarkFilteringSet {}".format(self.markFilteringSet.asFea()))
+ if not res:
+ res = ["0"]
+ return "lookupflag {};".format(" ".join(res))
class LookupReferenceStatement(Statement):
@@ -905,20 +906,24 @@ class MarkMarkPosStatement(Statement):
class MultipleSubstStatement(Statement):
- def __init__(self, prefix, glyph, suffix, replacement, location=None):
+ def __init__(
+ self, prefix, glyph, suffix, replacement, forceChain=False, location=None
+ ):
Statement.__init__(self, location)
self.prefix, self.glyph, self.suffix = prefix, glyph, suffix
self.replacement = replacement
+ self.forceChain = forceChain
def build(self, builder):
prefix = [p.glyphSet() for p in self.prefix]
suffix = [s.glyphSet() for s in self.suffix]
builder.add_multiple_subst(
- self.location, prefix, self.glyph, suffix, self.replacement)
+ self.location, prefix, self.glyph, suffix, self.replacement,
+ self.forceChain)
def asFea(self, indent=""):
res = "sub "
- if len(self.prefix) or len(self.suffix):
+ if len(self.prefix) or len(self.suffix) or self.forceChain:
if len(self.prefix):
res += " ".join(map(asFea, self.prefix)) + " "
res += asFea(self.glyph) + "'"
@@ -1088,7 +1093,7 @@ class SubtableStatement(Statement):
builder.add_subtable_break(self.location)
def asFea(self, indent=""):
- return indent + "subtable;"
+ return "subtable;"
class ValueRecord(Expression):
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
index e521a0ed..456ae3cc 100644
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -9,7 +9,7 @@ from fontTools.feaLib.ast import FeatureFile
from fontTools.otlLib import builder as otl
from fontTools.ttLib import newTable, getTableModule
from fontTools.ttLib.tables import otBase, otTables
-from collections import defaultdict
+from collections import defaultdict, OrderedDict
import itertools
import logging
@@ -545,7 +545,7 @@ class Builder(object):
required_feature_indices = {} # ('latn', 'DEU') --> 23
scripts = {} # 'latn' --> {'DEU': [23, 24]} for feature #23,24
# Sort the feature table by feature tag:
- # https://github.com/behdad/fonttools/issues/568
+ # https://github.com/fonttools/fonttools/issues/568
sortFeatureTag = lambda f: (f[0][2], f[0][1], f[0][0], f[1])
for key, lookups in sorted(self.features_.items(), key=sortFeatureTag):
script, lang, feature_tag = key
@@ -881,8 +881,8 @@ class Builder(object):
lookup.ligatures[g] = replacement
def add_multiple_subst(self, location,
- prefix, glyph, suffix, replacements):
- if prefix or suffix:
+ prefix, glyph, suffix, replacements, forceChain=False):
+ if prefix or suffix or forceChain:
chain = self.get_lookup_(location, ChainContextSubstBuilder)
sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
sub.mapping[glyph] = replacements
@@ -918,19 +918,10 @@ class Builder(object):
location)
lookup.mapping[from_glyph] = to_glyph
- def find_chainable_SingleSubst_(self, chain, glyphs):
- """Helper for add_single_subst_chained_()"""
- for _, _, _, substitutions in chain.substitutions:
- for sub in substitutions:
- if (isinstance(sub, SingleSubstBuilder) and
- not any(g in glyphs for g in sub.mapping.keys())):
- return sub
- return None
-
def add_single_subst_chained_(self, location, prefix, suffix, mapping):
- # https://github.com/behdad/fonttools/issues/512
+ # https://github.com/fonttools/fonttools/issues/512
chain = self.get_lookup_(location, ChainContextSubstBuilder)
- sub = self.find_chainable_SingleSubst_(chain, set(mapping.keys()))
+ sub = chain.find_chainable_single_subst(set(mapping.keys()))
if sub is None:
sub = self.get_chained_lookup_(location, SingleSubstBuilder)
sub.mapping.update(mapping)
@@ -996,14 +987,7 @@ class Builder(object):
lookup.addClassPair(location, glyphclass1, value1, glyphclass2, value2)
def add_subtable_break(self, location):
- if type(self.cur_lookup_) is not PairPosBuilder:
- raise FeatureLibError(
- 'explicit "subtable" statement is intended for use with only '
- "Pair Adjustment Positioning Format 2 (i.e. pair class kerning)",
- location
- )
- lookup = self.get_lookup_(location, PairPosBuilder)
- lookup.add_subtable_break(location)
+ self.cur_lookup_.add_subtable_break(location)
def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
lookup = self.get_lookup_(location, PairPosBuilder)
@@ -1018,28 +1002,19 @@ class Builder(object):
for glyph in glyphs:
lookup.add_pos(location, glyph, value)
- def find_chainable_SinglePos_(self, lookups, glyphs, value):
- """Helper for add_single_pos_chained_()"""
- for look in lookups:
- if all(look.can_add(glyph, value) for glyph in glyphs):
- return look
- return None
-
def add_single_pos_chained_(self, location, prefix, suffix, pos):
# https://github.com/fonttools/fonttools/issues/514
chain = self.get_lookup_(location, ChainContextPosBuilder)
targets = []
for _, _, _, lookups in chain.rules:
- for lookup in lookups:
- if isinstance(lookup, SinglePosBuilder):
- targets.append(lookup)
+ targets.extend(lookups)
subs = []
for glyphs, value in pos:
if value is None:
subs.append(None)
continue
otValue, _ = makeOpenTypeValueRecord(value, pairPosContext=False)
- sub = self.find_chainable_SinglePos_(targets, glyphs, otValue)
+ sub = chain.find_chainable_single_pos(targets, glyphs, otValue)
if sub is None:
sub = self.get_chained_lookup_(location, SinglePosBuilder)
targets.append(sub)
@@ -1129,6 +1104,8 @@ def makeOpenTypeValueRecord(v, pairPosContext):
class LookupBuilder(object):
+ SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
+
def __init__(self, font, location, table, lookup_type):
self.font = font
self.glyphMap = font.getReverseGlyphMap()
@@ -1191,23 +1168,43 @@ class LookupBuilder(object):
coverage = otl.buildCoverage(g, self.glyphMap)
subtable.InputCoverage.append(coverage)
+ def build_subst_subtables(self, mapping, klass):
+ substitutions = [{}]
+ for key in mapping:
+ if key[0] == self.SUBTABLE_BREAK_:
+ substitutions.append({})
+ else:
+ substitutions[-1][key] = mapping[key]
+ subtables = [klass(s) for s in substitutions]
+ return subtables
+
+ def add_subtable_break(self, location):
+ log.warning(FeatureLibError(
+ 'unsupported "subtable" statement for lookup type',
+ location
+ ))
+
class AlternateSubstBuilder(LookupBuilder):
def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GSUB', 3)
- self.alternates = {}
+ self.alternates = OrderedDict()
def equals(self, other):
return (LookupBuilder.equals(self, other) and
self.alternates == other.alternates)
def build(self):
- subtable = otl.buildAlternateSubstSubtable(self.alternates)
- return self.buildLookup_([subtable])
+ subtables = self.build_subst_subtables(self.alternates,
+ otl.buildAlternateSubstSubtable)
+ return self.buildLookup_(subtables)
def getAlternateGlyphs(self):
return self.alternates
+ def add_subtable_break(self, location):
+ self.alternates[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
class ChainContextPosBuilder(LookupBuilder):
def __init__(self, font, location):
@@ -1221,6 +1218,8 @@ class ChainContextPosBuilder(LookupBuilder):
def build(self):
subtables = []
for (prefix, glyphs, suffix, lookups) in self.rules:
+ if prefix == self.SUBTABLE_BREAK_:
+ continue
st = otTables.ChainContextPos()
subtables.append(st)
st.Format = 3
@@ -1238,6 +1237,21 @@ class ChainContextPosBuilder(LookupBuilder):
st.PosLookupRecord.append(rec)
return self.buildLookup_(subtables)
+ def find_chainable_single_pos(self, lookups, glyphs, value):
+ """Helper for add_single_pos_chained_()"""
+ res = None
+ for lookup in lookups[::-1]:
+ if lookup == self.SUBTABLE_BREAK_:
+ return res
+ if isinstance(lookup, SinglePosBuilder) and \
+ all(lookup.can_add(glyph, value) for glyph in glyphs):
+ res = lookup
+ return res
+
+ def add_subtable_break(self, location):
+ self.rules.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
+ self.SUBTABLE_BREAK_, [self.SUBTABLE_BREAK_]))
+
class ChainContextSubstBuilder(LookupBuilder):
def __init__(self, font, location):
@@ -1251,6 +1265,8 @@ class ChainContextSubstBuilder(LookupBuilder):
def build(self):
subtables = []
for (prefix, input, suffix, lookups) in self.substitutions:
+ if prefix == self.SUBTABLE_BREAK_:
+ continue
st = otTables.ChainContextSubst()
subtables.append(st)
st.Format = 3
@@ -1270,40 +1286,66 @@ class ChainContextSubstBuilder(LookupBuilder):
def getAlternateGlyphs(self):
result = {}
- for (_prefix, _input, _suffix, lookups) in self.substitutions:
+ for (_, _, _, lookups) in self.substitutions:
+ if lookups == self.SUBTABLE_BREAK_:
+ continue
for lookup in lookups:
alts = lookup.getAlternateGlyphs()
for glyph, replacements in alts.items():
result.setdefault(glyph, set()).update(replacements)
return result
+ def find_chainable_single_subst(self, glyphs):
+ """Helper for add_single_subst_chained_()"""
+ res = None
+ for _, _, _, substitutions in self.substitutions[::-1]:
+ if substitutions == self.SUBTABLE_BREAK_:
+ return res
+ for sub in substitutions:
+ if (isinstance(sub, SingleSubstBuilder) and
+ not any(g in glyphs for g in sub.mapping.keys())):
+ res = sub
+ return res
+
+ def add_subtable_break(self, location):
+ self.substitutions.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
+ self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_))
+
class LigatureSubstBuilder(LookupBuilder):
def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GSUB', 4)
- self.ligatures = {} # {('f','f','i'): 'f_f_i'}
+ self.ligatures = OrderedDict() # {('f','f','i'): 'f_f_i'}
def equals(self, other):
return (LookupBuilder.equals(self, other) and
self.ligatures == other.ligatures)
def build(self):
- subtable = otl.buildLigatureSubstSubtable(self.ligatures)
- return self.buildLookup_([subtable])
+ subtables = self.build_subst_subtables(self.ligatures,
+ otl.buildLigatureSubstSubtable)
+ return self.buildLookup_(subtables)
+
+ def add_subtable_break(self, location):
+ self.ligatures[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
class MultipleSubstBuilder(LookupBuilder):
def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GSUB', 2)
- self.mapping = {}
+ self.mapping = OrderedDict()
def equals(self, other):
return (LookupBuilder.equals(self, other) and
self.mapping == other.mapping)
def build(self):
- subtable = otl.buildMultipleSubstSubtable(self.mapping)
- return self.buildLookup_([subtable])
+ subtables = self.build_subst_subtables(self.mapping,
+ otl.buildMultipleSubstSubtable)
+ return self.buildLookup_(subtables)
+
+ def add_subtable_break(self, location):
+ self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
class CursivePosBuilder(LookupBuilder):
@@ -1440,23 +1482,31 @@ class ReverseChainSingleSubstBuilder(LookupBuilder):
subtables.append(st)
return self.buildLookup_(subtables)
+ def add_subtable_break(self, location):
+ # Nothing to do here, each substitution is in its own subtable.
+ pass
+
class SingleSubstBuilder(LookupBuilder):
def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GSUB', 1)
- self.mapping = {}
+ self.mapping = OrderedDict()
def equals(self, other):
return (LookupBuilder.equals(self, other) and
self.mapping == other.mapping)
def build(self):
- subtable = otl.buildSingleSubstSubtable(self.mapping)
- return self.buildLookup_([subtable])
+ subtables = self.build_subst_subtables(self.mapping,
+ otl.buildSingleSubstSubtable)
+ return self.buildLookup_(subtables)
def getAlternateGlyphs(self):
return {glyph: set([repl]) for glyph, repl in self.mapping.items()}
+ def add_subtable_break(self, location):
+ self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
class ClassPairPosSubtableBuilder(object):
def __init__(self, builder, valueFormat1, valueFormat2):
@@ -1501,8 +1551,6 @@ class ClassPairPosSubtableBuilder(object):
class PairPosBuilder(LookupBuilder):
- SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
-
def __init__(self, font, location):
LookupBuilder.__init__(self, font, location, 'GPOS', 2)
self.pairs = [] # [(gc1, value1, gc2, value2)*]
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
index e4971719..9edfc46f 100644
--- a/Lib/fontTools/feaLib/parser.py
+++ b/Lib/fontTools/feaLib/parser.py
@@ -526,6 +526,7 @@ class Parser(object):
return self.ast.LookupFlagStatement(value, location=location)
# format A: "lookupflag RightToLeft MarkAttachmentType @M;"
+ value_seen = False
value, markAttachment, markFilteringSet = 0, None, None
flags = {
"RightToLeft": 1, "IgnoreBaseGlyphs": 2,
@@ -545,12 +546,18 @@ class Parser(object):
self.expect_keyword_("UseMarkFilteringSet")
markFilteringSet = self.parse_class_name_()
elif self.next_token_ in flags:
+ value_seen = True
value = value | flags[self.expect_name_()]
else:
raise FeatureLibError(
'"%s" is not a recognized lookupflag' % self.next_token_,
self.next_token_location_)
self.expect_symbol_(";")
+
+ if not any([value_seen, markAttachment, markFilteringSet]):
+ raise FeatureLibError(
+ 'lookupflag must have a value', self.next_token_location_)
+
return self.ast.LookupFlagStatement(value,
markAttachment=markAttachment,
markFilteringSet=markFilteringSet,
@@ -755,7 +762,8 @@ class Parser(object):
num_lookups == 0):
return self.ast.MultipleSubstStatement(
old_prefix, tuple(old[0].glyphSet())[0], old_suffix,
- tuple([list(n.glyphSet())[0] for n in new]), location=location)
+ tuple([list(n.glyphSet())[0] for n in new]),
+ forceChain=hasMarks, location=location)
# GSUB lookup type 4: Ligature substitution.
# Format: "substitute f f i by f_f_i;"
@@ -1442,7 +1450,7 @@ class Parser(object):
if isinstance(s, self.ast.SingleSubstStatement):
has_single = not any([s.prefix, s.suffix, s.forceChain])
elif isinstance(s, self.ast.MultipleSubstStatement):
- has_multiple = not any([s.prefix, s.suffix])
+ has_multiple = not any([s.prefix, s.suffix, s.forceChain])
# Upgrade all single substitutions to multiple substitutions.
if has_single and has_multiple:
@@ -1451,7 +1459,7 @@ class Parser(object):
statements[i] = self.ast.MultipleSubstStatement(
s.prefix, s.glyphs[0].glyphSet()[0], s.suffix,
[r.glyphSet()[0] for r in s.replacements],
- location=s.location)
+ s.forceChain, location=s.location)
def is_cur_keyword_(self, k):
if self.cur_token_type_ is Lexer.NAME:
diff --git a/Lib/fontTools/inspect.py b/Lib/fontTools/inspect.py
deleted file mode 100644
index bdacadf3..00000000
--- a/Lib/fontTools/inspect.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod
-
-"""GUI font inspector.
-"""
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools import misc, ttLib, cffLib
-try:
- from gi import pygtkcompat
-except ImportError:
- pygtkcompat = None
-
-if pygtkcompat is not None:
- pygtkcompat.enable()
- pygtkcompat.enable_gtk(version='3.0')
-import gtk
-import sys
-
-
-class Row(object):
- def __init__(self, parent, index, key, value, font):
- self._parent = parent
- self._index = index
- self._key = key
- self._value = value
- self._font = font
-
- if isinstance(value, ttLib.TTFont):
- self._add_font(value)
- return
-
- if not isinstance(value, basestring):
- # Try sequences
- is_sequence = True
- try:
- len(value)
- iter(value)
- # It's hard to differentiate list-type sequences
- # from dict-type ones. Try fetching item 0.
- value[0]
- except (TypeError, AttributeError, KeyError, IndexError):
- is_sequence = False
- if is_sequence:
- self._add_list(key, value)
- return
- if hasattr(value, '__dict__'):
- self._add_object(key, value)
- return
- if hasattr(value, 'items'):
- self._add_dict(key, value)
- return
-
- if isinstance(value, basestring):
- self._value_str = '"'+value+'"'
- self._children = []
- return
-
- # Everything else
- self._children = []
-
- def _filter_items(self):
- items = []
- for k,v in self._items:
- if isinstance(v, ttLib.TTFont):
- continue
- if k in ['reader', 'file', 'tableTag', 'compileStatus', 'recurse']:
- continue
- if isinstance(k, basestring) and k[0] == '_':
- continue
- items.append((k,v))
- self._items = items
-
- def _add_font(self, font):
- self._items = [(tag,font[tag]) for tag in font.keys()]
-
- def _add_object(self, key, value):
- # Make sure item is decompiled
- try:
- value.asdf # Any better way?!
- except (AttributeError, KeyError, TypeError, ttLib.TTLibError):
- pass
- if isinstance(value, ttLib.getTableModule('glyf').Glyph):
- # Glyph type needs explicit expanding to be useful
- value.expand(self._font['glyf'])
- if isinstance(value, misc.psCharStrings.T2CharString):
- try:
- value.decompile()
- except TypeError: # Subroutines can't be decompiled
- pass
- if isinstance(value, cffLib.BaseDict):
- for k in value.rawDict.keys():
- getattr(value, k)
- if isinstance(value, cffLib.Index):
- # Load all items
- for i in range(len(value)):
- value[i]
- # Discard offsets as should not be needed anymore
- if hasattr(value, 'offsets'):
- del value.offsets
-
- self._value_str = value.__class__.__name__
- if isinstance(value, ttLib.tables.DefaultTable.DefaultTable):
- self._value_str += ' (%d Bytes)' % self._font.reader.tables[key].length
- self._items = sorted(value.__dict__.items())
- self._filter_items()
-
- def _add_dict(self, key, value):
- self._value_str = '%s of %d items' % (value.__class__.__name__, len(value))
- self._items = sorted(value.items())
-
- def _add_list(self, key, value):
- if len(value) and len(value) <= 32:
- self._value_str = str(value)
- else:
- self._value_str = '%s of %d items' % (value.__class__.__name__, len(value))
- self._items = list(enumerate(value))
-
- def __len__(self):
- if hasattr(self, '_children'):
- return len(self._children)
- if hasattr(self, '_items'):
- return len(self._items)
- assert False
-
- def _ensure_children(self):
- if hasattr(self, '_children'):
- return
- children = []
- for i,(k,v) in enumerate(self._items):
- children.append(Row(self, i, k, v, self._font))
- self._children = children
- del self._items
-
- def __getitem__(self, n):
- if n >= len(self):
- return None
- if not hasattr(self, '_children'):
- self._children = [None] * len(self)
- c = self._children[n]
- if c is None:
- k,v = self._items[n]
- c = self._children[n] = Row(self, n, k, v, self._font)
- self._items[n] = None
- return c
-
- def get_parent(self):
- return self._parent
-
- def get_index(self):
- return self._index
-
- def get_key(self):
- return self._key
-
- def get_value(self):
- return self._value
-
- def get_value_str(self):
- if hasattr(self,'_value_str'):
- return self._value_str
- return str(self._value)
-
-class FontTreeModel(gtk.GenericTreeModel):
-
- __gtype_name__ = 'FontTreeModel'
-
- def __init__(self, font):
- super(FontTreeModel, self).__init__()
- self._columns = (str, str)
- self.font = font
- self._root = Row(None, 0, "font", font, font)
-
- def on_get_flags(self):
- return 0
-
- def on_get_n_columns(self):
- return len(self._columns)
-
- def on_get_column_type(self, index):
- return self._columns[index]
-
- def on_get_iter(self, path):
- rowref = self._root
- while path:
- rowref = rowref[path[0]]
- path = path[1:]
- return rowref
-
- def on_get_path(self, rowref):
- path = []
- while rowref != self._root:
- path.append(rowref.get_index())
- rowref = rowref.get_parent()
- path.reverse()
- return tuple(path)
-
- def on_get_value(self, rowref, column):
- if column == 0:
- return rowref.get_key()
- else:
- return rowref.get_value_str()
-
- def on_iter_next(self, rowref):
- return rowref.get_parent()[rowref.get_index() + 1]
-
- def on_iter_children(self, rowref):
- return rowref[0]
-
- def on_iter_has_child(self, rowref):
- return bool(len(rowref))
-
- def on_iter_n_children(self, rowref):
- return len(rowref)
-
- def on_iter_nth_child(self, rowref, n):
- if not rowref: rowref = self._root
- return rowref[n]
-
- def on_iter_parent(self, rowref):
- return rowref.get_parent()
-
-class Inspect(object):
-
- def _delete_event(self, widget, event, data=None):
- gtk.main_quit()
- return False
-
- def __init__(self, fontfile):
-
- self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
- self.window.set_title("%s - pyftinspect" % fontfile)
- self.window.connect("delete_event", self._delete_event)
- self.window.set_size_request(400, 600)
-
- self.scrolled_window = gtk.ScrolledWindow()
- self.window.add(self.scrolled_window)
-
- self.font = ttLib.TTFont(fontfile, lazy=True)
- self.treemodel = FontTreeModel(self.font)
- self.treeview = gtk.TreeView(self.treemodel)
- #self.treeview.set_reorderable(True)
-
- for i in range(2):
- col_name = ('Key', 'Value')[i]
- col = gtk.TreeViewColumn(col_name)
- col.set_sort_column_id(-1)
- self.treeview.append_column(col)
-
- cell = gtk.CellRendererText()
- col.pack_start(cell, True)
- col.add_attribute(cell, 'text', i)
-
- self.treeview.set_search_column(1)
- self.scrolled_window.add(self.treeview)
- self.window.show_all()
-
-def main(args=None):
- if args is None:
- args = sys.argv[1:]
- if len(args) < 1:
- print("usage: pyftinspect font...", file=sys.stderr)
- return 1
- for arg in args:
- Inspect(arg)
- gtk.main()
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/Lib/fontTools/merge.py b/Lib/fontTools/merge.py
index 1005b859..36ac7565 100644
--- a/Lib/fontTools/merge.py
+++ b/Lib/fontTools/merge.py
@@ -226,6 +226,23 @@ ttLib.getTableClass('hhea').mergeMap = {
'numberOfHMetrics': recalculate,
}
+ttLib.getTableClass('vhea').mergeMap = {
+ '*': equal,
+ 'tableTag': equal,
+ 'tableVersion': max,
+ 'ascent': max,
+ 'descent': min,
+ 'lineGap': max,
+ 'advanceHeightMax': max,
+ 'minTopSideBearing': min,
+ 'minBottomSideBearing': min,
+ 'yMaxExtent': max,
+ 'caretSlopeRise': first,
+ 'caretSlopeRun': first,
+ 'caretOffset': first,
+ 'numberOfVMetrics': recalculate,
+}
+
os2FsTypeMergeBitMap = {
'size': 16,
'*': lambda bit: 0,
@@ -363,10 +380,18 @@ def _glyphsAreSame(glyphSet1, glyphSet2, glyph1, glyph2):
g1.width == g2.width and
(not hasattr(g1, 'height') or g1.height == g2.height))
+# Valid (format, platformID, platEncID) triplets for cmap subtables containing
+# Unicode BMP-only and Unicode Full Repertoire semantics.
+# Cf. OpenType spec for "Platform specific encodings":
+# https://docs.microsoft.com/en-us/typography/opentype/spec/name
+class CmapUnicodePlatEncodings:
+ BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
+ FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
+
@_add_method(ttLib.getTableClass('cmap'))
def merge(self, m, tables):
# TODO Handle format=14.
- # Only merges 4/3/1 and 12/3/10 subtables, ignores all other subtables
+ # Only merge format 4 and 12 Unicode subtables, ignores all other subtables
# If there is a format 12 table for the same font, ignore the format 4 table
cmapTables = []
for fontIdx,table in enumerate(tables):
@@ -374,10 +399,16 @@ def merge(self, m, tables):
format12 = None
for subtable in table.tables:
properties = (subtable.format, subtable.platformID, subtable.platEncID)
- if properties == (4,3,1):
+ if properties in CmapUnicodePlatEncodings.BMP:
format4 = subtable
- elif properties == (12,3,10):
+ elif properties in CmapUnicodePlatEncodings.FullRepertoire:
format12 = subtable
+ else:
+ log.warning(
+ "Dropped cmap subtable from font [%s]:\t"
+ "format %2s, platformID %2s, platEncID %2s",
+ fontIdx, subtable.format, subtable.platformID, subtable.platEncID
+ )
if format12 is not None:
cmapTables.append((format12, fontIdx))
elif format4 is not None:
diff --git a/Lib/fontTools/misc/encodingTools.py b/Lib/fontTools/misc/encodingTools.py
index 275ae9fb..454ec9ac 100644
--- a/Lib/fontTools/misc/encodingTools.py
+++ b/Lib/fontTools/misc/encodingTools.py
@@ -18,7 +18,7 @@ _encodingMap = {
},
1: { # Macintosh
# See
- # https://github.com/behdad/fonttools/issues/236
+ # https://github.com/fonttools/fonttools/issues/236
0: { # Macintosh, platEncID==0, keyed by langID
15: "mac_iceland",
17: "mac_turkish",
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index 34f21bb1..7fc7a26f 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -199,8 +199,7 @@ def getIntEncoder(format):
# distinguish anymore between small ints that were supposed to
# be small fixed numbers and small ints that were just small
# ints. Hence the warning.
- import sys
- sys.stderr.write("Warning: 4-byte T2 number got passed to the "
+ log.warning("4-byte T2 number got passed to the "
"IntType handler. This should happen only when reading in "
"old XML files.\n")
code = bytechr(255) + pack(">l", value)
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
index 37020555..5cddfebc 100644
--- a/Lib/fontTools/misc/py23.py
+++ b/Lib/fontTools/misc/py23.py
@@ -7,7 +7,7 @@ import sys
__all__ = ['basestring', 'unicode', 'unichr', 'byteord', 'bytechr', 'BytesIO',
'StringIO', 'UnicodeIO', 'strjoin', 'bytesjoin', 'tobytes', 'tostr',
'tounicode', 'Tag', 'open', 'range', 'xrange', 'round', 'Py23Error',
- 'SimpleNamespace', 'zip']
+ 'SimpleNamespace', 'zip', 'RecursionError']
class Py23Error(NotImplementedError):
@@ -514,6 +514,12 @@ else:
_stream = "stderr"
+try:
+ RecursionError = RecursionError
+except NameError:
+ RecursionError = RuntimeError
+
+
if __name__ == "__main__":
import doctest, sys
sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/timeTools.py b/Lib/fontTools/misc/timeTools.py
index c30a3778..657d77f8 100644
--- a/Lib/fontTools/misc/timeTools.py
+++ b/Lib/fontTools/misc/timeTools.py
@@ -31,7 +31,7 @@ def asctime(t=None):
In Python 3.x, the day of the month is right-justified, whereas on Windows
Python 2.7 it is padded with zeros.
- See https://github.com/behdad/fonttools/issues/455
+ See https://github.com/fonttools/fonttools/issues/455
"""
if t is None:
t = time.localtime()
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index d625d3ae..ef51a79a 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -30,7 +30,7 @@ class XMLWriter(object):
# Figure out if writer expects bytes or unicodes
try:
# The bytes check should be first. See:
- # https://github.com/behdad/fonttools/pull/233
+ # https://github.com/fonttools/fonttools/pull/233
self.file.write(b'')
self.totype = tobytes
except TypeError:
diff --git a/Lib/fontTools/pens/pointPen.py b/Lib/fontTools/pens/pointPen.py
index 415972f6..6299e863 100644
--- a/Lib/fontTools/pens/pointPen.py
+++ b/Lib/fontTools/pens/pointPen.py
@@ -200,7 +200,8 @@ class PointToSegmentPen(BasePointToSegmentPen):
else:
pen.endPath()
- def addComponent(self, glyphName, transform, **kwargs):
+ def addComponent(self, glyphName, transform, identifier=None, **kwargs):
+ del identifier # unused
self.pen.addComponent(glyphName, transform)
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 9403fc15..6e17cc33 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -208,7 +208,7 @@ Font table options:
By default, the following tables are dropped:
'BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'SVG ', 'PCLT', 'LTSH'
and Graphite tables: 'Feat', 'Glat', 'Gloc', 'Silf', 'Sill'
- and color tables: 'CBLC', 'CBDT', 'sbix'.
+ and color tables: 'sbix'.
The tool will attempt to subset the remaining tables.
Examples:
--drop-tables-='SVG '
@@ -266,7 +266,7 @@ Font naming options:
--name-legacy
Keep legacy (non-Unicode) 'name' table entries (0.x, 1.x etc.).
XXX Note: This might be needed for some fonts that have no Unicode name
- entires for English. See: https://github.com/behdad/fonttools/issues/146
+ entires for English. See: https://github.com/fonttools/fonttools/issues/146
--no-name-legacy
Drop legacy (non-Unicode) 'name' table entries [default]
--name-languages[+|-]=<langID>[,<langID>]
@@ -1377,7 +1377,7 @@ def subset_glyphs(self, s):
return True
-# CBDC will inherit it
+# CBDT will inherit it
@_add_method(ttLib.getTableClass('EBDT'))
def subset_glyphs(self, s):
self.strikeData = [{g: strike[g] for g in s.glyphs if g in strike}
@@ -1425,7 +1425,7 @@ def subset_glyphs(self, s):
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
def retain_empty_scripts(self):
- # https://github.com/behdad/fonttools/issues/518
+ # https://github.com/fonttools/fonttools/issues/518
# https://bugzilla.mozilla.org/show_bug.cgi?id=1080739#c15
return self.__class__ == ttLib.getTableClass('GSUB')
@@ -1798,17 +1798,20 @@ def subset_glyphs(self, s):
used = set()
if table.AdvWidthMap:
- table.AdvWidthMap.mapping = _dict_subset(table.AdvWidthMap.mapping, s.glyphs)
+ if not s.options.retain_gids:
+ table.AdvWidthMap.mapping = _dict_subset(table.AdvWidthMap.mapping, s.glyphs)
used.update(table.AdvWidthMap.mapping.values())
else:
assert table.LsbMap is None and table.RsbMap is None, "File a bug."
used.update(s.reverseOrigGlyphMap.values())
if table.LsbMap:
- table.LsbMap.mapping = _dict_subset(table.LsbMap.mapping, s.glyphs)
+ if not s.options.retain_gids:
+ table.LsbMap.mapping = _dict_subset(table.LsbMap.mapping, s.glyphs)
used.update(table.LsbMap.mapping.values())
if table.RsbMap:
- table.RsbMap.mapping = _dict_subset(table.RsbMap.mapping, s.glyphs)
+ if not s.options.retain_gids:
+ table.RsbMap.mapping = _dict_subset(table.RsbMap.mapping, s.glyphs)
used.update(table.RsbMap.mapping.values())
varidx_map = varStore.VarStore_subset_varidxes(table.VarStore, used)
@@ -1852,9 +1855,9 @@ def subset_glyphs(self, s):
if table.TsbMap:
table.TsbMap.mapping = {k:varidx_map[v] for k,v in table.TsbMap.mapping.items()}
if table.BsbMap:
- table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+ table.BsbMap.mapping = {k:varidx_map[v] for k,v in table.BsbMap.mapping.items()}
if table.VOrgMap:
- table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+ table.VOrgMap.mapping = {k:varidx_map[v] for k,v in table.VOrgMap.mapping.items()}
# TODO Return emptiness...
return True
diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py
index f01dfe67..274071e5 100644
--- a/Lib/fontTools/subset/cff.py
+++ b/Lib/fontTools/subset/cff.py
@@ -101,7 +101,7 @@ def prune_pre_subset(self, font, options):
# Clear useless Encoding
for fontname in cff.keys():
font = cff[fontname]
- # https://github.com/behdad/fonttools/issues/620
+ # https://github.com/fonttools/fonttools/issues/620
font.Encoding = "StandardEncoding"
return True # bool(cff.fontNames)
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index 743fa915..441242ee 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -6,7 +6,6 @@ from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
from . import DefaultTable
-import operator
import struct
@@ -44,12 +43,13 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
self.ColorLayers = colorLayerLists = {}
try:
- names = list(map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids))
+ names = [glyphOrder[gid] for gid in gids]
except IndexError:
getGlyphName = self.getGlyphName
- names = list(map(getGlyphName, gids ))
+ names = map(getGlyphName, gids)
- list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists))
+ for name, layerList in zip(names, layerLists):
+ colorLayerLists[name] = layerList
def compile(self, ttFont):
ordered = []
@@ -112,7 +112,7 @@ class table_C_O_L_R_(DefaultTable.DefaultTable):
layer = LayerRecord()
layer.fromXML(element[0], element[1], element[2], ttFont)
layers.append (layer)
- operator.setitem(self, glyphName, layers)
+ self[glyphName] = layers
elif "value" in attrs:
setattr(self, name, safeEval(attrs["value"]))
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 0c53e7d5..8cf66007 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -115,7 +115,7 @@ class table_E_B_L_C_(DefaultTable.DefaultTable):
indexSubTable.indexFormat = indexFormat
indexSubTable.imageFormat = imageFormat
indexSubTable.imageDataOffset = imageDataOffset
- indexSubTable.decompile() # https://github.com/behdad/fonttools/issues/317
+ indexSubTable.decompile() # https://github.com/fonttools/fonttools/issues/317
curStrike.indexSubTables.append(indexSubTable)
def compile(self, ttFont):
diff --git a/Lib/fontTools/ttLib/tables/G__l_a_t.py b/Lib/fontTools/ttLib/tables/G__l_a_t.py
index 7d1f7350..b7e8281b 100644
--- a/Lib/fontTools/ttLib/tables/G__l_a_t.py
+++ b/Lib/fontTools/ttLib/tables/G__l_a_t.py
@@ -7,10 +7,6 @@ from functools import partial
from . import DefaultTable
from . import grUtils
import struct, operator, warnings
-try:
- import lz4
-except:
- lz4 = None
Glat_format_0 = """
diff --git a/Lib/fontTools/ttLib/tables/TupleVariation.py b/Lib/fontTools/ttLib/tables/TupleVariation.py
index 92d07a11..e2ceba4d 100644
--- a/Lib/fontTools/ttLib/tables/TupleVariation.py
+++ b/Lib/fontTools/ttLib/tables/TupleVariation.py
@@ -71,7 +71,13 @@ class TupleVariation(object):
if minValue == defaultMinValue and maxValue == defaultMaxValue:
writer.simpletag("coord", axis=axis, value=value)
else:
- writer.simpletag("coord", axis=axis, value=value, min=minValue, max=maxValue)
+ attrs = [
+ ("axis", axis),
+ ("min", minValue),
+ ("value", value),
+ ("max", maxValue),
+ ]
+ writer.simpletag("coord", attrs)
writer.newline()
wrote_any_deltas = False
for i, delta in enumerate(self.coordinates):
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index 8b2c317a..1b752940 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -30,27 +30,27 @@ class table_V_O_R_G_(DefaultTable.DefaultTable):
self.VOriginRecords = vOrig = {}
glyphOrder = ttFont.getGlyphOrder()
try:
- names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids)
+ names = [glyphOrder[gid] for gid in gids]
except IndexError:
getGlyphName = self.getGlyphName
- names = map(getGlyphName, gids )
+ names = map(getGlyphName, gids)
- list(map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids))
+ for name, vid in zip(names, vids):
+ vOrig[name] = vid
def compile(self, ttFont):
vorgs = list(self.VOriginRecords.values())
names = list(self.VOriginRecords.keys())
nameMap = ttFont.getReverseGlyphMap()
- lenRecords = len(vorgs)
try:
- gids = map(operator.getitem, [nameMap]*lenRecords, names)
+ gids = [nameMap[name] for name in names]
except KeyError:
nameMap = ttFont.getReverseGlyphMap(rebuild=True)
- gids = map(operator.getitem, [nameMap]*lenRecords, names)
+ gids = [nameMap[name] for name in names]
vOriginTable = list(zip(gids, vorgs))
- self.numVertOriginYMetrics = lenRecords
+ self.numVertOriginYMetrics = len(vorgs)
vOriginTable.sort() # must be in ascending GID order
- dataList = [ struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
+ dataList = [struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
header = struct.pack(">HHhH", self.majorVersion, self.minorVersion, self.defaultVertOriginY, self.numVertOriginYMetrics)
dataList.insert(0, header)
data = bytesjoin(dataList)
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 1767ad4c..971a3163 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -8,7 +8,6 @@ from . import DefaultTable
import sys
import struct
import array
-import operator
import logging
@@ -442,13 +441,12 @@ class cmap_format_2(CmapSubtable):
charCodes = [item[0] for item in items]
names = [item[1] for item in items]
nameMap = ttFont.getReverseGlyphMap()
- lenCharCodes = len(charCodes)
try:
- gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+ gids = [nameMap[name] for name in names]
except KeyError:
nameMap = ttFont.getReverseGlyphMap(rebuild=True)
try:
- gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+ gids = [nameMap[name] for name in names]
except KeyError:
# allow virtual GIDs in format 2 tables
gids = []
@@ -458,7 +456,7 @@ class cmap_format_2(CmapSubtable):
except KeyError:
try:
if (name[:3] == 'gid'):
- gid = eval(name[3:])
+ gid = int(name[3:])
else:
gid = ttFont.getGlyphID(name)
except:
@@ -744,20 +742,19 @@ class cmap_format_4(CmapSubtable):
return struct.pack(">HHH", self.format, self.length, self.language) + self.data
charCodes = list(self.cmap.keys())
- lenCharCodes = len(charCodes)
- if lenCharCodes == 0:
+ if not charCodes:
startCode = [0xffff]
endCode = [0xffff]
else:
charCodes.sort()
- names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
+ names = [self.cmap[code] for code in charCodes]
nameMap = ttFont.getReverseGlyphMap()
try:
- gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+ gids = [nameMap[name] for name in names]
except KeyError:
nameMap = ttFont.getReverseGlyphMap(rebuild=True)
try:
- gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+ gids = [nameMap[name] for name in names]
except KeyError:
# allow virtual GIDs in format 4 tables
gids = []
@@ -767,7 +764,7 @@ class cmap_format_4(CmapSubtable):
except KeyError:
try:
if (name[:3] == 'gid'):
- gid = eval(name[3:])
+ gid = int(name[3:])
else:
gid = ttFont.getGlyphID(name)
except:
@@ -775,7 +772,8 @@ class cmap_format_4(CmapSubtable):
gids.append(gid)
cmap = {} # code:glyphID mapping
- list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
+ for code, gid in zip(charCodes, gids):
+ cmap[code] = gid
# Build startCode and endCode lists.
# Split the char codes in ranges of consecutive char codes, then split
@@ -955,15 +953,14 @@ class cmap_format_12_or_13(CmapSubtable):
if self.data:
return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
charCodes = list(self.cmap.keys())
- lenCharCodes = len(charCodes)
names = list(self.cmap.values())
nameMap = ttFont.getReverseGlyphMap()
try:
- gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+ gids = [nameMap[name] for name in names]
except KeyError:
nameMap = ttFont.getReverseGlyphMap(rebuild=True)
try:
- gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+ gids = [nameMap[name] for name in names]
except KeyError:
# allow virtual GIDs in format 12 tables
gids = []
@@ -973,7 +970,7 @@ class cmap_format_12_or_13(CmapSubtable):
except KeyError:
try:
if (name[:3] == 'gid'):
- gid = eval(name[3:])
+ gid = int(name[3:])
else:
gid = ttFont.getGlyphID(name)
except:
@@ -982,7 +979,8 @@ class cmap_format_12_or_13(CmapSubtable):
gids.append(gid)
cmap = {} # code:glyphID mapping
- list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
+ for code, gid in zip(charCodes, gids):
+ cmap[code] = gid
charCodes.sort()
index = 0
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 8b360504..b8020ca7 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -841,7 +841,10 @@ class Glyph(object):
allEndPts = []
for compo in self.components:
g = glyfTable[compo.glyphName]
- coordinates, endPts, flags = g.getCoordinates(glyfTable)
+ try:
+ coordinates, endPts, flags = g.getCoordinates(glyfTable)
+ except RecursionError:
+ raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName)
if hasattr(compo, "firstPt"):
# move according to two reference points
x1,y1 = allCoords[compo.firstPt]
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
index 9275d410..4235acf4 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -46,7 +46,7 @@ class table__h_e_a_d(DefaultTable.DefaultTable):
# bogus values there. Since till 2038 those bytes only can be zero,
# ignore them.
#
- # https://github.com/behdad/fonttools/issues/99#issuecomment-66776810
+ # https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810
for stamp in 'created', 'modified':
value = getattr(self, stamp)
if value > 0xFFFFFFFF:
diff --git a/Lib/fontTools/ttLib/tables/grUtils.py b/Lib/fontTools/ttLib/tables/grUtils.py
index d11ac4b1..a60df234 100644
--- a/Lib/fontTools/ttLib/tables/grUtils.py
+++ b/Lib/fontTools/ttLib/tables/grUtils.py
@@ -1,8 +1,10 @@
import struct, warnings
try:
import lz4
-except:
+except ImportError:
lz4 = None
+else:
+ import lz4.block
#old scheme for VERSION < 0.9 otherwise use lz4.block
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index c71cc8db..81ab354e 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -303,7 +303,7 @@ class OTTableWriter(object):
# Certain versions of Uniscribe reject the font if the GSUB/GPOS top-level
# arrays (ScriptList, FeatureList, LookupList) point to the same, possibly
# empty, array. So, we don't share those.
- # See: https://github.com/behdad/fonttools/issues/518
+ # See: https://github.com/fonttools/fonttools/issues/518
dontShare = hasattr(self, 'DontShare')
if isExtension:
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index 4d2bc574..af2a7c6c 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -9,7 +9,6 @@ from __future__ import print_function, division, absolute_import, unicode_litera
from fontTools.misc.py23 import *
from fontTools.misc.textTools import pad, safeEval
from .otBase import BaseTable, FormatSwitchingBaseTable, ValueRecord
-import operator
import logging
import struct
@@ -698,18 +697,19 @@ class SingleSubst(FormatSwitchingBaseTable):
def postRead(self, rawTable, font):
mapping = {}
input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
- lenMapping = len(input)
if self.Format == 1:
delta = rawTable["DeltaGlyphID"]
inputGIDS = [ font.getGlyphID(name) for name in input ]
outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ]
outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
- list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
+ for inp, out in zip(input, outNames):
+ mapping[inp] = out
elif self.Format == 2:
assert len(input) == rawTable["GlyphCount"], \
"invalid SingleSubstFormat2 table"
subst = rawTable["Substitute"]
- list(map(operator.setitem, [mapping]*lenMapping, input, subst))
+ for inp, sub in zip(input, subst):
+ mapping[inp] = sub
else:
assert 0, "unknown format: %s" % self.Format
self.mapping = mapping
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 6ec4113f..0543ee37 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -209,8 +209,40 @@ def _add_stat(font, axes):
# TODO make this user-configurable via designspace document
stat.ElidedFallbackNameID = 2
+
+def _get_phantom_points(font, glyphName, defaultVerticalOrigin=None):
+ glyf = font["glyf"]
+ glyph = glyf[glyphName]
+ horizontalAdvanceWidth, leftSideBearing = font["hmtx"].metrics[glyphName]
+ if not hasattr(glyph, 'xMin'):
+ glyph.recalcBounds(glyf)
+ leftSideX = glyph.xMin - leftSideBearing
+ rightSideX = leftSideX + horizontalAdvanceWidth
+ if "vmtx" in font:
+ verticalAdvanceWidth, topSideBearing = font["vmtx"].metrics[glyphName]
+ topSideY = topSideBearing + glyph.yMax
+ else:
+ # without vmtx, use ascent as vertical origin and UPEM as vertical advance
+ # like HarfBuzz does
+ verticalAdvanceWidth = font["head"].unitsPerEm
+ try:
+ topSideY = font["hhea"].ascent
+ except KeyError:
+ # sparse masters may not contain an hhea table; use the ascent
+ # of the default master as the vertical origin
+ assert defaultVerticalOrigin is not None
+ topSideY = defaultVerticalOrigin
+ bottomSideY = topSideY - verticalAdvanceWidth
+ return [
+ (leftSideX, 0),
+ (rightSideX, 0),
+ (0, topSideY),
+ (0, bottomSideY),
+ ]
+
+
# TODO Move to glyf or gvar table proper
-def _GetCoordinates(font, glyphName):
+def _GetCoordinates(font, glyphName, defaultVerticalOrigin=None):
"""font, glyphName --> glyph coordinates as expected by "gvar" table
The result includes four "phantom points" for the glyph metrics,
@@ -228,19 +260,9 @@ def _GetCoordinates(font, glyphName):
control = (glyph.numberOfContours,)+allData[1:]
# Add phantom points for (left, right, top, bottom) positions.
- horizontalAdvanceWidth, leftSideBearing = font["hmtx"].metrics[glyphName]
- if not hasattr(glyph, 'xMin'):
- glyph.recalcBounds(glyf)
- leftSideX = glyph.xMin - leftSideBearing
- rightSideX = leftSideX + horizontalAdvanceWidth
- # XXX these are incorrect. Load vmtx and fix.
- topSideY = glyph.yMax
- bottomSideY = -glyph.yMin
+ phantomPoints = _get_phantom_points(font, glyphName, defaultVerticalOrigin)
coord = coord.copy()
- coord.extend([(leftSideX, 0),
- (rightSideX, 0),
- (0, topSideY),
- (0, bottomSideY)])
+ coord.extend(phantomPoints)
return coord, control
@@ -297,11 +319,16 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
glyf = font['glyf']
+ # use hhea.ascent of base master as default vertical origin when vmtx is missing
+ defaultVerticalOrigin = font['hhea'].ascent
for glyph in font.getGlyphOrder():
isComposite = glyf[glyph].isComposite()
- allData = [_GetCoordinates(m, glyph) for m in master_ttfs]
+ allData = [
+ _GetCoordinates(m, glyph, defaultVerticalOrigin=defaultVerticalOrigin)
+ for m in master_ttfs
+ ]
model, allData = masterModel.getSubModel(allData)
allCoords = [d[0] for d in allData]
@@ -688,6 +715,7 @@ def load_designspace(designspace):
('width', ('wdth', {'en': u'Width'})),
('slant', ('slnt', {'en': u'Slant'})),
('optical', ('opsz', {'en': u'Optical Size'})),
+ ('italic', ('ital', {'en': u'Italic'})),
])
# Setup axes
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index 207e30f3..9a990b7b 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -189,19 +189,22 @@ class VariationModel(object):
7: 0.6666666666666667}]
"""
- def __init__(self, locations, axisOrder=[]):
+ def __init__(self, locations, axisOrder=None):
+ if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
+ raise ValueError("locations must be unique")
+
self.origLocations = locations
- self.axisOrder = axisOrder
+ self.axisOrder = axisOrder if axisOrder is not None else []
locations = [{k:v for k,v in loc.items() if v != 0.} for loc in locations]
- keyFunc = self.getMasterLocationsSortKeyFunc(locations, axisOrder=axisOrder)
- axisPoints = keyFunc.axisPoints
+ keyFunc = self.getMasterLocationsSortKeyFunc(locations, axisOrder=self.axisOrder)
self.locations = sorted(locations, key=keyFunc)
- # TODO Assert that locations are unique.
- self.mapping = [self.locations.index(l) for l in locations] # Mapping from user's master order to our master order
- self.reverseMapping = [locations.index(l) for l in self.locations] # Reverse of above
- self._computeMasterSupports(axisPoints, axisOrder)
+ # Mapping from user's master order to our master order
+ self.mapping = [self.locations.index(l) for l in locations]
+ self.reverseMapping = [locations.index(l) for l in self.locations]
+
+ self._computeMasterSupports(keyFunc.axisPoints)
self._subModels = {}
def getSubModel(self, items):
@@ -252,18 +255,6 @@ class VariationModel(object):
ret.axisPoints = axisPoints
return ret
- @staticmethod
- def lowerBound(value, lst):
- if any(v < value for v in lst):
- return max(v for v in lst if v < value)
- else:
- return value
- @staticmethod
- def upperBound(value, lst):
- if any(v > value for v in lst):
- return min(v for v in lst if v > value)
- else:
- return value
def reorderMasters(self, master_list, mapping):
# For changing the master data order without
# recomputing supports and deltaWeights.
@@ -276,7 +267,7 @@ class VariationModel(object):
self._subModels = {}
return new_list
- def _computeMasterSupports(self, axisPoints, axisOrder):
+ def _computeMasterSupports(self, axisPoints):
supports = []
deltaWeights = []
locations = self.locations
@@ -444,6 +435,7 @@ def main(args):
pprint(locs)
doc.normalize()
print("Normalized locations:")
+ locs = [s.location for s in doc.sources]
pprint(locs)
else:
axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
index 79a6f3d1..de612365 100644
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -9,7 +9,7 @@ from fontTools.misc.fixedTools import floatToFixedToFloat, otRound, floatToFixed
from fontTools.pens.boundsPen import BoundsPen
from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.tables import ttProgram
-from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates, flagOverlapSimple, OVERLAP_COMPOUND
from fontTools.varLib import _GetCoordinates, _SetCoordinates
from fontTools.varLib.models import (
supportScalar,
@@ -145,7 +145,7 @@ def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
hmtx[gname] = tuple(entry)
-def instantiateVariableFont(varfont, location, inplace=False):
+def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
""" Generate a static instance from a variable TTFont and a dictionary
defining the desired location along the variable font's axes.
The location values must be specified as user-space coordinates, e.g.:
@@ -154,6 +154,10 @@ def instantiateVariableFont(varfont, location, inplace=False):
By default, a new TTFont object is returned. If ``inplace`` is True, the
input varfont is modified and reduced to a static font.
+
+ When the overlap parameter is defined as True,
+ OVERLAP_SIMPLE and OVERLAP_COMPOUND bits are set to 1. See
+ https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
"""
if not inplace:
# make a copy to leave input varfont unmodified
@@ -308,6 +312,15 @@ def instantiateVariableFont(varfont, location, inplace=False):
addidef = any(op.startswith("GETVARIATION") for op in instructions)
if addidef:
break
+ if overlap:
+ for glyph_name in glyf.keys():
+ glyph = glyf[glyph_name]
+ # Set OVERLAP_COMPOUND bit for compound glyphs
+ if glyph.isComposite():
+ glyph.components[0].flags |= OVERLAP_COMPOUND
+ # Set OVERLAP_SIMPLE bit for simple glyphs
+ elif glyph.numberOfContours > 0:
+ glyph.flags[0] |= flagOverlapSimple
if addidef:
log.info("Adding IDEF to fpgm table for GETVARIATION opcode")
asm = []
@@ -404,6 +417,12 @@ def main(args=None):
"-v", "--verbose", action="store_true", help="Run more verbosely.")
logging_group.add_argument(
"-q", "--quiet", action="store_true", help="Turn verbosity off.")
+ parser.add_argument(
+ "--no-overlap",
+ dest="overlap",
+ action="store_false",
+ help="Don't set OVERLAP_SIMPLE/OVERLAP_COMPOUND glyf flags."
+ )
options = parser.parse_args(args)
varfilename = options.input
@@ -428,7 +447,7 @@ def main(args=None):
log.info("Loading variable font")
varfont = TTFont(varfilename)
- instantiateVariableFont(varfont, loc, inplace=True)
+ instantiateVariableFont(varfont, loc, inplace=True, overlap=options.overlap)
log.info("Saving instance font %s", outfile)
varfont.save(outfile)
diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py
index b03744e9..6b134160 100644
--- a/Lib/fontTools/varLib/plot.py
+++ b/Lib/fontTools/varLib/plot.py
@@ -21,34 +21,84 @@ def stops(support, count=10):
[b + (c - b) * i / count for i in range(count)] + \
[c]
-def plotLocations(locations, axes, axis3D, **kwargs):
- for loc,color in zip(locations, cycle(pyplot.cm.Set1.colors)):
- axis3D.plot([loc.get(axes[0], 0)],
- [loc.get(axes[1], 0)],
- [1.],
- 'o',
- color=color,
- **kwargs)
-
-def plotLocationsSurfaces(locations, fig, names=None, **kwargs):
-
- assert len(locations[0].keys()) == 2
-
- if names is None:
- names = ['']
+def _plotLocationsDots(locations, axes, subplot, **kwargs):
+ for loc, color in zip(locations, cycle(pyplot.cm.Set1.colors)):
+ if len(axes) == 1:
+ subplot.plot(
+ [loc.get(axes[0], 0)],
+ [1.],
+ 'o',
+ color=color,
+ **kwargs
+ )
+ elif len(axes) == 2:
+ subplot.plot(
+ [loc.get(axes[0], 0)],
+ [loc.get(axes[1], 0)],
+ [1.],
+ 'o',
+ color=color,
+ **kwargs
+ )
+ else:
+ raise AssertionError(len(axes))
+
+
+def plotLocations(locations, fig, names=None, **kwargs):
n = len(locations)
cols = math.ceil(n**.5)
rows = math.ceil(n / cols)
+ if names is None:
+ names = [None] * len(locations)
+
model = VariationModel(locations)
names = [names[model.reverseMapping[i]] for i in range(len(names))]
- ax1, ax2 = sorted(locations[0].keys())
- for i, (support,color, name) in enumerate(zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))):
+ axes = sorted(locations[0].keys())
+ if len(axes) == 1:
+ _plotLocations2D(
+ model, axes[0], fig, cols, rows, names=names, **kwargs
+ )
+ elif len(axes) == 2:
+ _plotLocations3D(
+ model, axes, fig, cols, rows, names=names, **kwargs
+ )
+ else:
+ raise ValueError("Only 1 or 2 axes are supported")
+
+
+def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs):
+ 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)
+ pyplot.xlim(-1.,+1.)
+
+ Xs = support.get(axis, (-1.,0.,+1.))
+ X, Y = [], []
+ for x in stops(Xs):
+ y = supportScalar({axis:x}, support)
+ X.append(x)
+ Y.append(y)
+ subplot.plot(X, Y, color=color, **kwargs)
+
+ _plotLocationsDots(model.locations, [axis], subplot)
+
+
+def _plotLocations3D(model, axes, fig, rows, cols, names, **kwargs):
+ ax1, ax2 = axes
+ 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')
- axis3D.set_title(name)
+ if name is not None:
+ axis3D.set_title(name)
axis3D.set_xlabel(ax1)
axis3D.set_ylabel(ax2)
pyplot.xlim(-1.,+1.)
@@ -73,14 +123,14 @@ def plotLocationsSurfaces(locations, fig, names=None, **kwargs):
Z.append(z)
axis3D.plot(X, Y, Z, color=color, **kwargs)
- plotLocations(model.locations, [ax1, ax2], axis3D)
+ _plotLocationsDots(model.locations, [ax1, ax2], axis3D)
def plotDocument(doc, fig, **kwargs):
doc.normalize()
locations = [s.location for s in doc.sources]
names = [s.name for s in doc.sources]
- plotLocationsSurfaces(locations, fig, names, **kwargs)
+ plotLocations(locations, fig, names, **kwargs)
def main(args=None):
@@ -101,6 +151,7 @@ def main(args=None):
sys.exit(1)
fig = pyplot.figure()
+ fig.set_tight_layout(True)
if len(args) == 1 and args[0].endswith('.designspace'):
doc = DesignSpaceDocument()
@@ -109,7 +160,7 @@ def main(args=None):
else:
axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
- plotLocationsSurfaces(locs, fig)
+ plotLocations(locs, fig)
pyplot.show()
diff --git a/Lib/fonttools.egg-info/PKG-INFO b/Lib/fonttools.egg-info/PKG-INFO
index fc57c8dc..d57498f3 100644
--- a/Lib/fonttools.egg-info/PKG-INFO
+++ b/Lib/fonttools.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: fonttools
-Version: 3.38.0
+Version: 3.39.0
Summary: Tools to manipulate font files
Home-page: http://github.com/fonttools/fonttools
Author: Just van Rossum
@@ -8,8 +8,7 @@ Author-email: just@letterror.com
Maintainer: Behdad Esfahbod
Maintainer-email: behdad@behdad.org
License: MIT
-Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
- |PyPI| |Gitter Chat|
+Description: |Travis Build Status| |Appveyor Build status| |Coverage Status| |PyPI| |Gitter Chat|
What is this?
~~~~~~~~~~~~~
@@ -147,12 +146,10 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
Other Tools
~~~~~~~~~~~
- Commands for inspecting, merging and subsetting fonts are also
- available:
+ Commands for merging and subsetting fonts are also available:
.. code:: sh
- pyftinspect
pyftmerge
pyftsubset
@@ -311,16 +308,6 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
* `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
for generating PDFs and graphics.
- - ``Lib/fontTools/inspect.py``
-
- A GUI font inspector, requires one of the following packages:
-
- * `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
- GTK  2.x (only works with Python 2).
- * `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
- Python bindings for GTK 3.x and gobject-introspection libraries (also
- compatible with Python 3).
-
Testing
~~~~~~~
@@ -417,8 +404,6 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
:target: https://travis-ci.org/fonttools/fonttools
.. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
:target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
- .. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
- :target: https://landscape.io/github/behdad/fonttools/master
.. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
:target: https://codecov.io/gh/fonttools/fonttools
.. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
@@ -430,6 +415,35 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
Changelog
~~~~~~~~~
+ 3.39.0 (released 2019-03-19)
+ ----------------------------
+
+ - [ttLib/glyf] Raise more specific error when encountering recursive
+ component references (#1545, #1546).
+ - [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+ unified-font-object/ufo-spec#84).
+ - [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+ and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+ - [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+ toXML to improve readability of TTX dump (#1527).
+ - [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+ - [designspaceLib] Use axes maps when normalizing locations in
+ DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+ - [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+ default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+ to disable this (#1518).
+ - [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).
+ Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+ option ``--retain-gids`` is used.
+ - [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).
+ Fixed double indentation of ``subtable`` statement (#1512).
+ Added support for ``subtable`` statement in more places than just PairPos
+ lookups (#1520).
+ Handle lookupflag 0 and lookupflag without a value (#1540).
+ - [varLib] In ``load_designspace``, provide a default English name for the
+ ``ital`` axis tag.
+ - Remove pyftinspect because it is unmaintained and bitrotted.
+
3.38.0 (released 2019-02-18)
----------------------------
@@ -1670,13 +1684,13 @@ Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Text Processing :: Fonts
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: woff
+Provides-Extra: symfont
Provides-Extra: type1
+Provides-Extra: lxml
+Provides-Extra: ufo
Provides-Extra: interpolatable
-Provides-Extra: symfont
-Provides-Extra: graphite
Provides-Extra: all
-Provides-Extra: ufo
-Provides-Extra: unicode
-Provides-Extra: lxml
+Provides-Extra: woff
Provides-Extra: plot
+Provides-Extra: unicode
+Provides-Extra: graphite
diff --git a/Lib/fonttools.egg-info/SOURCES.txt b/Lib/fonttools.egg-info/SOURCES.txt
index b07edaec..3bb752fa 100644
--- a/Lib/fonttools.egg-info/SOURCES.txt
+++ b/Lib/fonttools.egg-info/SOURCES.txt
@@ -29,7 +29,6 @@ Doc/source/conf.py
Doc/source/encodings.rst
Doc/source/feaLib.rst
Doc/source/index.rst
-Doc/source/inspect.rst
Doc/source/merge.rst
Doc/source/subset.rst
Doc/source/t1Lib.rst
@@ -88,7 +87,6 @@ Lib/fontTools/__main__.py
Lib/fontTools/afmLib.py
Lib/fontTools/agl.py
Lib/fontTools/fontBuilder.py
-Lib/fontTools/inspect.py
Lib/fontTools/merge.py
Lib/fontTools/ttx.py
Lib/fontTools/unicode.py
@@ -348,8 +346,14 @@ Tests/feaLib/builder_test.py
Tests/feaLib/error_test.py
Tests/feaLib/lexer_test.py
Tests/feaLib/parser_test.py
+Tests/feaLib/data/AlternateSubtable.fea
+Tests/feaLib/data/AlternateSubtable.ttx
Tests/feaLib/data/Attach.fea
Tests/feaLib/data/Attach.ttx
+Tests/feaLib/data/ChainPosSubtable.fea
+Tests/feaLib/data/ChainPosSubtable.ttx
+Tests/feaLib/data/ChainSubstSubtable.fea
+Tests/feaLib/data/ChainSubstSubtable.ttx
Tests/feaLib/data/GPOS_1.fea
Tests/feaLib/data/GPOS_1.ttx
Tests/feaLib/data/GPOS_1_zero.fea
@@ -382,8 +386,14 @@ Tests/feaLib/data/LigatureCaretByIndex.fea
Tests/feaLib/data/LigatureCaretByIndex.ttx
Tests/feaLib/data/LigatureCaretByPos.fea
Tests/feaLib/data/LigatureCaretByPos.ttx
+Tests/feaLib/data/LigatureSubtable.fea
+Tests/feaLib/data/LigatureSubtable.ttx
+Tests/feaLib/data/MultipleSubstSubtable.fea
+Tests/feaLib/data/MultipleSubstSubtable.ttx
Tests/feaLib/data/PairPosSubtable.fea
Tests/feaLib/data/PairPosSubtable.ttx
+Tests/feaLib/data/SingleSubstSubtable.fea
+Tests/feaLib/data/SingleSubstSubtable.ttx
Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.fea
Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.fea
diff --git a/Lib/fonttools.egg-info/entry_points.txt b/Lib/fonttools.egg-info/entry_points.txt
index 6d707744..2c235cff 100644
--- a/Lib/fonttools.egg-info/entry_points.txt
+++ b/Lib/fonttools.egg-info/entry_points.txt
@@ -1,6 +1,5 @@
[console_scripts]
fonttools = fontTools.__main__:main
-pyftinspect = fontTools.inspect:main
pyftmerge = fontTools.merge:main
pyftsubset = fontTools.subset:main
ttx = fontTools.ttx:main
diff --git a/METADATA b/METADATA
index e3f3b934..ea7bfaa2 100644
--- a/METADATA
+++ b/METADATA
@@ -7,12 +7,12 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://github.com/fonttools/fonttools/releases/download/3.38.0/fonttools-3.38.0.zip"
+ value: "https://github.com/fonttools/fonttools/releases/download/3.39.0/fonttools-3.39.0.zip"
}
- version: "3.38.0"
+ version: "3.39.0"
last_upgrade_date {
year: 2019
- month: 2
+ month: 3
day: 19
}
}
diff --git a/NEWS.rst b/NEWS.rst
index b3b0adb3..c48ed884 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,32 @@
+3.39.0 (released 2019-03-19)
+----------------------------
+
+- [ttLib/glyf] Raise more specific error when encountering recursive
+ component references (#1545, #1546).
+- [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+ unified-font-object/ufo-spec#84).
+- [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+ and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+- [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+ toXML to improve readability of TTX dump (#1527).
+- [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+- [designspaceLib] Use axes maps when normalizing locations in
+ DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+- [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+ default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+ to disable this (#1518).
+- [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).
+ Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+ option ``--retain-gids`` is used.
+- [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).
+ Fixed double indentation of ``subtable`` statement (#1512).
+ Added support for ``subtable`` statement in more places than just PairPos
+ lookups (#1520).
+ Handle lookupflag 0 and lookupflag without a value (#1540).
+- [varLib] In ``load_designspace``, provide a default English name for the
+ ``ital`` axis tag.
+- Remove pyftinspect because it is unmaintained and bitrotted.
+
3.38.0 (released 2019-02-18)
----------------------------
diff --git a/PKG-INFO b/PKG-INFO
index fc57c8dc..d57498f3 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: fonttools
-Version: 3.38.0
+Version: 3.39.0
Summary: Tools to manipulate font files
Home-page: http://github.com/fonttools/fonttools
Author: Just van Rossum
@@ -8,8 +8,7 @@ Author-email: just@letterror.com
Maintainer: Behdad Esfahbod
Maintainer-email: behdad@behdad.org
License: MIT
-Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
- |PyPI| |Gitter Chat|
+Description: |Travis Build Status| |Appveyor Build status| |Coverage Status| |PyPI| |Gitter Chat|
What is this?
~~~~~~~~~~~~~
@@ -147,12 +146,10 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
Other Tools
~~~~~~~~~~~
- Commands for inspecting, merging and subsetting fonts are also
- available:
+ Commands for merging and subsetting fonts are also available:
.. code:: sh
- pyftinspect
pyftmerge
pyftsubset
@@ -311,16 +308,6 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
* `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
for generating PDFs and graphics.
- - ``Lib/fontTools/inspect.py``
-
- A GUI font inspector, requires one of the following packages:
-
- * `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
- GTK  2.x (only works with Python 2).
- * `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
- Python bindings for GTK 3.x and gobject-introspection libraries (also
- compatible with Python 3).
-
Testing
~~~~~~~
@@ -417,8 +404,6 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
:target: https://travis-ci.org/fonttools/fonttools
.. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
:target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
- .. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
- :target: https://landscape.io/github/behdad/fonttools/master
.. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
:target: https://codecov.io/gh/fonttools/fonttools
.. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
@@ -430,6 +415,35 @@ Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage St
Changelog
~~~~~~~~~
+ 3.39.0 (released 2019-03-19)
+ ----------------------------
+
+ - [ttLib/glyf] Raise more specific error when encountering recursive
+ component references (#1545, #1546).
+ - [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+ unified-font-object/ufo-spec#84).
+ - [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+ and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+ - [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+ toXML to improve readability of TTX dump (#1527).
+ - [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+ - [designspaceLib] Use axes maps when normalizing locations in
+ DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+ - [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+ default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+ to disable this (#1518).
+ - [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).
+ Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+ option ``--retain-gids`` is used.
+ - [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).
+ Fixed double indentation of ``subtable`` statement (#1512).
+ Added support for ``subtable`` statement in more places than just PairPos
+ lookups (#1520).
+ Handle lookupflag 0 and lookupflag without a value (#1540).
+ - [varLib] In ``load_designspace``, provide a default English name for the
+ ``ital`` axis tag.
+ - Remove pyftinspect because it is unmaintained and bitrotted.
+
3.38.0 (released 2019-02-18)
----------------------------
@@ -1670,13 +1684,13 @@ Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Text Processing :: Fonts
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: woff
+Provides-Extra: symfont
Provides-Extra: type1
+Provides-Extra: lxml
+Provides-Extra: ufo
Provides-Extra: interpolatable
-Provides-Extra: symfont
-Provides-Extra: graphite
Provides-Extra: all
-Provides-Extra: ufo
-Provides-Extra: unicode
-Provides-Extra: lxml
+Provides-Extra: woff
Provides-Extra: plot
+Provides-Extra: unicode
+Provides-Extra: graphite
diff --git a/README.rst b/README.rst
index f36084ad..14e7c4c5 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,4 @@
-|Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
-|PyPI| |Gitter Chat|
+|Travis Build Status| |Appveyor Build status| |Coverage Status| |PyPI| |Gitter Chat|
What is this?
~~~~~~~~~~~~~
@@ -137,12 +136,10 @@ important, we maintain an ordered list of glyph names in the font.
Other Tools
~~~~~~~~~~~
-Commands for inspecting, merging and subsetting fonts are also
-available:
+Commands for merging and subsetting fonts are also available:
.. code:: sh
- pyftinspect
pyftmerge
pyftsubset
@@ -301,16 +298,6 @@ are required to unlock the extra features named "ufo", etc.
* `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
for generating PDFs and graphics.
-- ``Lib/fontTools/inspect.py``
-
- A GUI font inspector, requires one of the following packages:
-
- * `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
- GTK  2.x (only works with Python 2).
- * `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
- Python bindings for GTK 3.x and gobject-introspection libraries (also
- compatible with Python 3).
-
Testing
~~~~~~~
@@ -407,8 +394,6 @@ Have fun!
:target: https://travis-ci.org/fonttools/fonttools
.. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
:target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
-.. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
- :target: https://landscape.io/github/behdad/fonttools/master
.. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
:target: https://codecov.io/gh/fonttools/fonttools
.. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py
index 2dd64027..aed31005 100755
--- a/Snippets/svg2glif.py
+++ b/Snippets/svg2glif.py
@@ -8,8 +8,8 @@ __requires__ = ["FontTools", "ufoLib"]
from fontTools.misc.py23 import SimpleNamespace
from fontTools.svgLib import SVGPath
-from ufoLib.pointPen import SegmentToPointPen
-from ufoLib.glifLib import writeGlyphToString
+from fontTools.pens.pointPen import SegmentToPointPen
+from fontTools.ufoLib.glifLib import writeGlyphToString
__all__ = ["svg2glif"]
diff --git a/Tests/designspaceLib/data/test.designspace b/Tests/designspaceLib/data/test.designspace
index 9f97ab57..901a0ee0 100644
--- a/Tests/designspaceLib/data/test.designspace
+++ b/Tests/designspaceLib/data/test.designspace
@@ -5,9 +5,10 @@
<labelname xml:lang="en">Wéíght</labelname>
<labelname xml:lang="fa-IR">قطر</labelname>
</axis>
- <axis tag="wdth" name="width" minimum="0" maximum="1000" default="20" hidden="1">
+ <axis tag="wdth" name="width" minimum="0" maximum="1000" default="15" hidden="1">
<labelname xml:lang="fr">Chasse</labelname>
<map input="0" output="10"/>
+ <map input="15" output="20"/>
<map input="401" output="66"/>
<map input="1000" output="990"/>
</axis>
diff --git a/Tests/designspaceLib/designspace_test.py b/Tests/designspaceLib/designspace_test.py
index 2044f00b..2d01c659 100644
--- a/Tests/designspaceLib/designspace_test.py
+++ b/Tests/designspaceLib/designspace_test.py
@@ -66,10 +66,10 @@ def test_fill_document(tmpdir):
a2 = AxisDescriptor()
a2.minimum = 0
a2.maximum = 1000
- a2.default = 20
+ a2.default = 15
a2.name = "width"
a2.tag = "wdth"
- a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
+ a2.map = [(0.0, 10.0), (15.0, 20.0), (401.0, 66.0), (1000.0, 990.0)]
a2.hidden = True
a2.labelNames[u'fr'] = u"Chasse"
doc.addAxis(a2)
@@ -619,7 +619,7 @@ def test_normalise4():
for axis in doc.axes:
r.append((axis.name, axis.map))
r.sort()
- assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
+ assert r == [('ddd', [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
def test_axisMapping():
# note: because designspance lib does not do any actual
@@ -638,7 +638,7 @@ def test_axisMapping():
for axis in doc.axes:
r.append((axis.name, axis.map))
r.sort()
- assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
+ assert r == [('ddd', [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
def test_rulesConditions(tmpdir):
# tests of rules, conditionsets and conditions
@@ -847,3 +847,61 @@ def test_with_with_path_object(tmpdir):
doc = DesignSpaceDocument()
doc.write(dest)
assert dest.exists()
+
+
+def test_findDefault_axis_mapping():
+ designspace_string = """\
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+ <axes>
+ <axis tag="wght" name="Weight" minimum="100" maximum="800" default="400">
+ <map input="100" output="20"/>
+ <map input="300" output="40"/>
+ <map input="400" output="80"/>
+ <map input="700" output="126"/>
+ <map input="800" output="170"/>
+ </axis>
+ <axis tag="ital" name="Italic" minimum="0" maximum="1" default="1"/>
+ </axes>
+ <sources>
+ <source filename="Font-Light.ufo">
+ <location>
+ <dimension name="Weight" xvalue="20"/>
+ <dimension name="Italic" xvalue="0"/>
+ </location>
+ </source>
+ <source filename="Font-Regular.ufo">
+ <location>
+ <dimension name="Weight" xvalue="80"/>
+ <dimension name="Italic" xvalue="0"/>
+ </location>
+ </source>
+ <source filename="Font-Bold.ufo">
+ <location>
+ <dimension name="Weight" xvalue="170"/>
+ <dimension name="Italic" xvalue="0"/>
+ </location>
+ </source>
+ <source filename="Font-LightItalic.ufo">
+ <location>
+ <dimension name="Weight" xvalue="20"/>
+ <dimension name="Italic" xvalue="1"/>
+ </location>
+ </source>
+ <source filename="Font-Italic.ufo">
+ <location>
+ <dimension name="Weight" xvalue="80"/>
+ <dimension name="Italic" xvalue="1"/>
+ </location>
+ </source>
+ <source filename="Font-BoldItalic.ufo">
+ <location>
+ <dimension name="Weight" xvalue="170"/>
+ <dimension name="Italic" xvalue="1"/>
+ </location>
+ </source>
+ </sources>
+</designspace>
+ """
+ designspace = DesignSpaceDocument.fromstring(designspace_string)
+ assert designspace.findDefault().filename == "Font-Italic.ufo"
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
index fc1141a8..eb800782 100644
--- a/Tests/feaLib/builder_test.py
+++ b/Tests/feaLib/builder_test.py
@@ -70,7 +70,8 @@ class BuilderTest(unittest.TestCase):
ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical
ZeroValue_ChainSinglePos_horizontal ZeroValue_ChainSinglePos_vertical
- PairPosSubtable
+ PairPosSubtable ChainSubstSubtable ChainPosSubtable LigatureSubtable
+ AlternateSubtable MultipleSubstSubtable SingleSubstSubtable
""".split()
def __init__(self, methodName):
@@ -512,17 +513,20 @@ class BuilderTest(unittest.TestCase):
addOpenTypeFeatures(font, tree)
assert "GSUB" in font
+ @unittest.skipIf(sys.version_info[0:2] < (3, 4),
+ "assertLogs() was introduced in 3.4")
def test_unsupported_subtable_break(self):
- self.assertRaisesRegex(
- FeatureLibError,
- 'explicit "subtable" statement is intended for .* class kerning',
- self.build,
- "feature liga {"
- " sub f f by f_f;"
- " subtable;"
- " sub f i by f_i;"
- "} liga;"
- )
+ with self.assertLogs(level='WARNING') as logs:
+ self.build(
+ "feature test {"
+ " pos a 10;"
+ " subtable;"
+ " pos b 10;"
+ "} test;"
+ )
+ self.assertEqual(logs.output,
+ ['WARNING:fontTools.feaLib.builder:<features>:1:32: '
+ 'unsupported "subtable" statement for lookup type'])
def test_skip_featureNames_if_no_name_table(self):
features = (
diff --git a/Tests/feaLib/data/AlternateSubtable.fea b/Tests/feaLib/data/AlternateSubtable.fea
new file mode 100644
index 00000000..c4bce3cc
--- /dev/null
+++ b/Tests/feaLib/data/AlternateSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+ sub A from [A.alt1 A.alt2];
+ subtable;
+ sub B from [B.alt1 B.alt2];
+} test;
diff --git a/Tests/feaLib/data/AlternateSubtable.ttx b/Tests/feaLib/data/AlternateSubtable.ttx
new file mode 100644
index 00000000..e8119ba2
--- /dev/null
+++ b/Tests/feaLib/data/AlternateSubtable.ttx
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+ <GSUB>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="test"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=1 -->
+ <Lookup index="0">
+ <LookupType value="3"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=2 -->
+ <AlternateSubst index="0">
+ <AlternateSet glyph="A">
+ <Alternate glyph="A.alt1"/>
+ <Alternate glyph="A.alt2"/>
+ </AlternateSet>
+ </AlternateSubst>
+ <AlternateSubst index="1">
+ <AlternateSet glyph="B">
+ <Alternate glyph="B.alt1"/>
+ <Alternate glyph="B.alt2"/>
+ </AlternateSet>
+ </AlternateSubst>
+ </Lookup>
+ </LookupList>
+ </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ChainPosSubtable.fea b/Tests/feaLib/data/ChainPosSubtable.fea
new file mode 100644
index 00000000..46506229
--- /dev/null
+++ b/Tests/feaLib/data/ChainPosSubtable.fea
@@ -0,0 +1,7 @@
+feature test {
+ pos X [A-B]' -40 B' -40 A' -40 Y;
+ subtable;
+ pos X A' -111 Y;
+ subtable;
+ pos X B' -40 A' -111 [A-C]' -40 Y;
+} test;
diff --git a/Tests/feaLib/data/ChainPosSubtable.ttx b/Tests/feaLib/data/ChainPosSubtable.ttx
new file mode 100644
index 00000000..a780ccb7
--- /dev/null
+++ b/Tests/feaLib/data/ChainPosSubtable.ttx
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+ <GPOS>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="test"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=5 -->
+ <Lookup index="0">
+ <LookupType value="8"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=3 -->
+ <ChainContextPos index="0" Format="3">
+ <!-- BacktrackGlyphCount=1 -->
+ <BacktrackCoverage index="0">
+ <Glyph value="X"/>
+ </BacktrackCoverage>
+ <!-- InputGlyphCount=3 -->
+ <InputCoverage index="0">
+ <Glyph value="A"/>
+ <Glyph value="B"/>
+ </InputCoverage>
+ <InputCoverage index="1">
+ <Glyph value="B"/>
+ </InputCoverage>
+ <InputCoverage index="2">
+ <Glyph value="A"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=1 -->
+ <LookAheadCoverage index="0">
+ <Glyph value="Y"/>
+ </LookAheadCoverage>
+ <!-- PosCount=3 -->
+ <PosLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="1"/>
+ </PosLookupRecord>
+ <PosLookupRecord index="1">
+ <SequenceIndex value="1"/>
+ <LookupListIndex value="1"/>
+ </PosLookupRecord>
+ <PosLookupRecord index="2">
+ <SequenceIndex value="2"/>
+ <LookupListIndex value="1"/>
+ </PosLookupRecord>
+ </ChainContextPos>
+ <ChainContextPos index="1" Format="3">
+ <!-- BacktrackGlyphCount=1 -->
+ <BacktrackCoverage index="0">
+ <Glyph value="X"/>
+ </BacktrackCoverage>
+ <!-- InputGlyphCount=1 -->
+ <InputCoverage index="0">
+ <Glyph value="A"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=1 -->
+ <LookAheadCoverage index="0">
+ <Glyph value="Y"/>
+ </LookAheadCoverage>
+ <!-- PosCount=1 -->
+ <PosLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="2"/>
+ </PosLookupRecord>
+ </ChainContextPos>
+ <ChainContextPos index="2" Format="3">
+ <!-- BacktrackGlyphCount=1 -->
+ <BacktrackCoverage index="0">
+ <Glyph value="X"/>
+ </BacktrackCoverage>
+ <!-- InputGlyphCount=3 -->
+ <InputCoverage index="0">
+ <Glyph value="B"/>
+ </InputCoverage>
+ <InputCoverage index="1">
+ <Glyph value="A"/>
+ </InputCoverage>
+ <InputCoverage index="2">
+ <Glyph value="A"/>
+ <Glyph value="B"/>
+ <Glyph value="C"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=1 -->
+ <LookAheadCoverage index="0">
+ <Glyph value="Y"/>
+ </LookAheadCoverage>
+ <!-- PosCount=3 -->
+ <PosLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="3"/>
+ </PosLookupRecord>
+ <PosLookupRecord index="1">
+ <SequenceIndex value="1"/>
+ <LookupListIndex value="3"/>
+ </PosLookupRecord>
+ <PosLookupRecord index="2">
+ <SequenceIndex value="2"/>
+ <LookupListIndex value="4"/>
+ </PosLookupRecord>
+ </ChainContextPos>
+ </Lookup>
+ <Lookup index="1">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SinglePos index="0" Format="1">
+ <Coverage>
+ <Glyph value="A"/>
+ <Glyph value="B"/>
+ </Coverage>
+ <ValueFormat value="4"/>
+ <Value XAdvance="-40"/>
+ </SinglePos>
+ </Lookup>
+ <Lookup index="2">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SinglePos index="0" Format="1">
+ <Coverage>
+ <Glyph value="A"/>
+ </Coverage>
+ <ValueFormat value="4"/>
+ <Value XAdvance="-111"/>
+ </SinglePos>
+ </Lookup>
+ <Lookup index="3">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SinglePos index="0" Format="2">
+ <Coverage>
+ <Glyph value="A"/>
+ <Glyph value="B"/>
+ </Coverage>
+ <ValueFormat value="4"/>
+ <!-- ValueCount=2 -->
+ <Value index="0" XAdvance="-111"/>
+ <Value index="1" XAdvance="-40"/>
+ </SinglePos>
+ </Lookup>
+ <Lookup index="4">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SinglePos index="0" Format="1">
+ <Coverage>
+ <Glyph value="A"/>
+ <Glyph value="B"/>
+ <Glyph value="C"/>
+ </Coverage>
+ <ValueFormat value="4"/>
+ <Value XAdvance="-40"/>
+ </SinglePos>
+ </Lookup>
+ </LookupList>
+ </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ChainSubstSubtable.fea b/Tests/feaLib/data/ChainSubstSubtable.fea
new file mode 100644
index 00000000..b6e959a2
--- /dev/null
+++ b/Tests/feaLib/data/ChainSubstSubtable.fea
@@ -0,0 +1,9 @@
+feature test {
+ sub G' by G.swash;
+ subtable;
+ sub H' by H.swash;
+ subtable;
+ sub G' by g;
+ subtable;
+ sub H' by H.swash;
+} test;
diff --git a/Tests/feaLib/data/ChainSubstSubtable.ttx b/Tests/feaLib/data/ChainSubstSubtable.ttx
new file mode 100644
index 00000000..f7a09c7f
--- /dev/null
+++ b/Tests/feaLib/data/ChainSubstSubtable.ttx
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+ <GSUB>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="test"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=5 -->
+ <Lookup index="0">
+ <LookupType value="6"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=4 -->
+ <ChainContextSubst index="0" Format="3">
+ <!-- BacktrackGlyphCount=0 -->
+ <!-- InputGlyphCount=1 -->
+ <InputCoverage index="0">
+ <Glyph value="G"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=0 -->
+ <!-- SubstCount=1 -->
+ <SubstLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="1"/>
+ </SubstLookupRecord>
+ </ChainContextSubst>
+ <ChainContextSubst index="1" Format="3">
+ <!-- BacktrackGlyphCount=0 -->
+ <!-- InputGlyphCount=1 -->
+ <InputCoverage index="0">
+ <Glyph value="H"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=0 -->
+ <!-- SubstCount=1 -->
+ <SubstLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="2"/>
+ </SubstLookupRecord>
+ </ChainContextSubst>
+ <ChainContextSubst index="2" Format="3">
+ <!-- BacktrackGlyphCount=0 -->
+ <!-- InputGlyphCount=1 -->
+ <InputCoverage index="0">
+ <Glyph value="G"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=0 -->
+ <!-- SubstCount=1 -->
+ <SubstLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="3"/>
+ </SubstLookupRecord>
+ </ChainContextSubst>
+ <ChainContextSubst index="3" Format="3">
+ <!-- BacktrackGlyphCount=0 -->
+ <!-- InputGlyphCount=1 -->
+ <InputCoverage index="0">
+ <Glyph value="H"/>
+ </InputCoverage>
+ <!-- LookAheadGlyphCount=0 -->
+ <!-- SubstCount=1 -->
+ <SubstLookupRecord index="0">
+ <SequenceIndex value="0"/>
+ <LookupListIndex value="4"/>
+ </SubstLookupRecord>
+ </ChainContextSubst>
+ </Lookup>
+ <Lookup index="1">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SingleSubst index="0">
+ <Substitution in="G" out="G.swash"/>
+ </SingleSubst>
+ </Lookup>
+ <Lookup index="2">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SingleSubst index="0">
+ <Substitution in="H" out="H.swash"/>
+ </SingleSubst>
+ </Lookup>
+ <Lookup index="3">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SingleSubst index="0">
+ <Substitution in="G" out="g"/>
+ </SingleSubst>
+ </Lookup>
+ <Lookup index="4">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=1 -->
+ <SingleSubst index="0">
+ <Substitution in="H" out="H.swash"/>
+ </SingleSubst>
+ </Lookup>
+ </LookupList>
+ </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_1_zero.fea b/Tests/feaLib/data/GPOS_1_zero.fea
index bfb51db6..2e738e2b 100644
--- a/Tests/feaLib/data/GPOS_1_zero.fea
+++ b/Tests/feaLib/data/GPOS_1_zero.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/471
+# https://github.com/fonttools/fonttools/issues/471
feature test {
pos zero 0;
pos four 500;
diff --git a/Tests/feaLib/data/GPOS_2.fea b/Tests/feaLib/data/GPOS_2.fea
index b1f26d15..2948148b 100644
--- a/Tests/feaLib/data/GPOS_2.fea
+++ b/Tests/feaLib/data/GPOS_2.fea
@@ -1,7 +1,7 @@
languagesystem DFLT dflt;
# Mixes kerning between single glyphs, and class-based kerning.
-# https://github.com/behdad/fonttools/issues/456
+# https://github.com/fonttools/fonttools/issues/456
lookup MixedKerning {
pos v v 14;
pos [D O Q] [T V W] -26;
diff --git a/Tests/feaLib/data/LigatureSubtable.fea b/Tests/feaLib/data/LigatureSubtable.fea
new file mode 100644
index 00000000..aa4ec265
--- /dev/null
+++ b/Tests/feaLib/data/LigatureSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+ sub f f by f_f;
+ subtable;
+ sub f i by f_i;
+} test;
diff --git a/Tests/feaLib/data/LigatureSubtable.ttx b/Tests/feaLib/data/LigatureSubtable.ttx
new file mode 100644
index 00000000..e13451a2
--- /dev/null
+++ b/Tests/feaLib/data/LigatureSubtable.ttx
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+ <GSUB>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="test"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=1 -->
+ <Lookup index="0">
+ <LookupType value="4"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=2 -->
+ <LigatureSubst index="0">
+ <LigatureSet glyph="f">
+ <Ligature components="f" glyph="f_f"/>
+ </LigatureSet>
+ </LigatureSubst>
+ <LigatureSubst index="1">
+ <LigatureSet glyph="f">
+ <Ligature components="i" glyph="f_i"/>
+ </LigatureSet>
+ </LigatureSubst>
+ </Lookup>
+ </LookupList>
+ </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/MultipleSubstSubtable.fea b/Tests/feaLib/data/MultipleSubstSubtable.fea
new file mode 100644
index 00000000..4964037f
--- /dev/null
+++ b/Tests/feaLib/data/MultipleSubstSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+ sub c_t by c t;
+ subtable;
+ sub f_i by f i;
+} test;
diff --git a/Tests/feaLib/data/MultipleSubstSubtable.ttx b/Tests/feaLib/data/MultipleSubstSubtable.ttx
new file mode 100644
index 00000000..6b3afd43
--- /dev/null
+++ b/Tests/feaLib/data/MultipleSubstSubtable.ttx
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+ <GSUB>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="test"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=1 -->
+ <Lookup index="0">
+ <LookupType value="2"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=2 -->
+ <MultipleSubst index="0">
+ <Substitution in="c_t" out="c,t"/>
+ </MultipleSubst>
+ <MultipleSubst index="1">
+ <Substitution in="f_i" out="f,i"/>
+ </MultipleSubst>
+ </Lookup>
+ </LookupList>
+ </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/SingleSubstSubtable.fea b/Tests/feaLib/data/SingleSubstSubtable.fea
new file mode 100644
index 00000000..f5a6da31
--- /dev/null
+++ b/Tests/feaLib/data/SingleSubstSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+ sub a by b;
+ subtable;
+ sub c by d;
+} test;
diff --git a/Tests/feaLib/data/SingleSubstSubtable.ttx b/Tests/feaLib/data/SingleSubstSubtable.ttx
new file mode 100644
index 00000000..5030b738
--- /dev/null
+++ b/Tests/feaLib/data/SingleSubstSubtable.ttx
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+ <GSUB>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="test"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=1 -->
+ <Lookup index="0">
+ <LookupType value="1"/>
+ <LookupFlag value="0"/>
+ <!-- SubTableCount=2 -->
+ <SingleSubst index="0">
+ <Substitution in="a" out="b"/>
+ </SingleSubst>
+ <SingleSubst index="1">
+ <Substitution in="c" out="d"/>
+ </SingleSubst>
+ </Lookup>
+ </LookupList>
+ </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug453.fea b/Tests/feaLib/data/bug453.fea
index fc5072e7..486632ee 100644
--- a/Tests/feaLib/data/bug453.fea
+++ b/Tests/feaLib/data/bug453.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/453
+# https://github.com/fonttools/fonttools/issues/453
feature mark {
lookup mark1 {
markClass [acute] <anchor 150 -10> @TOP_MARKS;
diff --git a/Tests/feaLib/data/bug463.fea b/Tests/feaLib/data/bug463.fea
index e7e21af0..4b1910c1 100644
--- a/Tests/feaLib/data/bug463.fea
+++ b/Tests/feaLib/data/bug463.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/463
+# https://github.com/fonttools/fonttools/issues/463
feature ordn {
@DIGIT = [zero one two three four five six seven eight nine];
sub @DIGIT [A a]' by ordfeminine;
diff --git a/Tests/feaLib/data/bug501.fea b/Tests/feaLib/data/bug501.fea
index 58a4081f..06d91dda 100644
--- a/Tests/feaLib/data/bug501.fea
+++ b/Tests/feaLib/data/bug501.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/501
+# https://github.com/fonttools/fonttools/issues/501
languagesystem DFLT dflt;
feature test {
lookup L {
diff --git a/Tests/feaLib/data/bug502.fea b/Tests/feaLib/data/bug502.fea
index b8130c42..3fcb94e2 100644
--- a/Tests/feaLib/data/bug502.fea
+++ b/Tests/feaLib/data/bug502.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/502
+# https://github.com/fonttools/fonttools/issues/502
feature aalt {
sub A by A.alt1;
sub Eng by Eng.alt1;
diff --git a/Tests/feaLib/data/bug504.fea b/Tests/feaLib/data/bug504.fea
index 64d05baf..e6920b2b 100644
--- a/Tests/feaLib/data/bug504.fea
+++ b/Tests/feaLib/data/bug504.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/504
+# https://github.com/fonttools/fonttools/issues/504
feature test {
sub [a b c d] by [T E S T];
diff --git a/Tests/feaLib/data/bug505.fea b/Tests/feaLib/data/bug505.fea
index 016ed1f7..742ea118 100644
--- a/Tests/feaLib/data/bug505.fea
+++ b/Tests/feaLib/data/bug505.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/505
+# https://github.com/fonttools/fonttools/issues/505
languagesystem armn dflt;
languagesystem avst dflt;
diff --git a/Tests/feaLib/data/bug506.fea b/Tests/feaLib/data/bug506.fea
index 62e4bbb9..2e1e97de 100644
--- a/Tests/feaLib/data/bug506.fea
+++ b/Tests/feaLib/data/bug506.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/506
+# https://github.com/fonttools/fonttools/issues/506
feature test {
sub f' i' by f_i;
} test;
diff --git a/Tests/feaLib/data/bug568.fea b/Tests/feaLib/data/bug568.fea
index 3e879b19..d9f56f95 100644
--- a/Tests/feaLib/data/bug568.fea
+++ b/Tests/feaLib/data/bug568.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/568
+# https://github.com/fonttools/fonttools/issues/568
feature tst1 {
script latn;
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
index bf260235..98ac1014 100644
--- a/Tests/feaLib/parser_test.py
+++ b/Tests/feaLib/parser_test.py
@@ -408,7 +408,7 @@ class ParserTest(unittest.TestCase):
self.assertEqual(smcp.statements[0].glyphSet(), ("a", "b", "s"))
def test_glyphclass_scoping_bug496(self):
- # https://github.com/behdad/fonttools/issues/496
+ # https://github.com/fonttools/fonttools/issues/496
f1, f2 = self.parse(
"feature F1 { lookup L { @GLYPHCLASS = [A B C];} L; } F1;"
"feature F2 { sub @GLYPHCLASS by D; } F2;"
@@ -677,6 +677,7 @@ class ParserTest(unittest.TestCase):
self.assertEqual(flag.value, 9)
self.assertIsNone(flag.markAttachment)
self.assertIsNone(flag.markFilteringSet)
+ self.assertEqual(flag.asFea(), "lookupflag RightToLeft IgnoreMarks;")
def test_lookupflag_format_A_MarkAttachmentType(self):
flag = self.parse_lookupflag_(
@@ -688,6 +689,8 @@ class ParserTest(unittest.TestCase):
self.assertEqual(flag.markAttachment.glyphSet(),
("acute", "grave", "macron"))
self.assertIsNone(flag.markFilteringSet)
+ self.assertEqual(flag.asFea(),
+ "lookupflag RightToLeft MarkAttachmentType @TOP_MARKS;")
def test_lookupflag_format_A_UseMarkFilteringSet(self):
flag = self.parse_lookupflag_(
@@ -699,6 +702,8 @@ class ParserTest(unittest.TestCase):
self.assertIsInstance(flag.markFilteringSet, ast.GlyphClassName)
self.assertEqual(flag.markFilteringSet.glyphSet(),
("cedilla", "ogonek"))
+ self.assertEqual(flag.asFea(),
+ "lookupflag IgnoreLigatures UseMarkFilteringSet @BOTTOM_MARKS;")
def test_lookupflag_format_B(self):
flag = self.parse_lookupflag_("lookupflag 7;")
@@ -706,6 +711,23 @@ class ParserTest(unittest.TestCase):
self.assertEqual(flag.value, 7)
self.assertIsNone(flag.markAttachment)
self.assertIsNone(flag.markFilteringSet)
+ self.assertEqual(flag.asFea(),
+ "lookupflag RightToLeft IgnoreBaseGlyphs IgnoreLigatures;")
+
+ def test_lookupflag_format_B_zero(self):
+ flag = self.parse_lookupflag_("lookupflag 0;")
+ self.assertIsInstance(flag, ast.LookupFlagStatement)
+ self.assertEqual(flag.value, 0)
+ self.assertIsNone(flag.markAttachment)
+ self.assertIsNone(flag.markFilteringSet)
+ self.assertEqual(flag.asFea(), "lookupflag 0;")
+
+ def test_lookupflag_no_value(self):
+ self.assertRaisesRegex(
+ FeatureLibError,
+ 'lookupflag must have a value',
+ self.parse,
+ "feature test {lookupflag;} test;")
def test_lookupflag_repeated(self):
self.assertRaisesRegex(
@@ -1289,6 +1311,14 @@ class ParserTest(unittest.TestCase):
self.assertEqual(sub.glyph, "f_f_i")
self.assertEqual(sub.replacement, ("f", "f", "i"))
+ def test_substitute_multiple_force_chained(self):
+ doc = self.parse("lookup L {sub f_f_i' by f f i;} L;")
+ sub = doc.statements[0].statements[0]
+ self.assertIsInstance(sub, ast.MultipleSubstStatement)
+ self.assertEqual(sub.glyph, "f_f_i")
+ self.assertEqual(sub.replacement, ("f", "f", "i"))
+ self.assertEqual(sub.asFea(), "sub f_f_i' by f f i;")
+
def test_substitute_multiple_by_mutliple(self):
self.assertRaisesRegex(
FeatureLibError,
diff --git a/Tests/misc/xmlWriter_test.py b/Tests/misc/xmlWriter_test.py
index 0930e1ce..ac8a789e 100644
--- a/Tests/misc/xmlWriter_test.py
+++ b/Tests/misc/xmlWriter_test.py
@@ -26,19 +26,19 @@ class TestXMLWriter(unittest.TestCase):
writer.file.getvalue())
def test_encoding_utf8(self):
- # https://github.com/behdad/fonttools/issues/246
+ # https://github.com/fonttools/fonttools/issues/246
writer = XMLWriter(BytesIO(), encoding="utf8")
self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
writer.file.getvalue())
def test_encoding_UTF_8(self):
- # https://github.com/behdad/fonttools/issues/246
+ # https://github.com/fonttools/fonttools/issues/246
writer = XMLWriter(BytesIO(), encoding="UTF-8")
self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
writer.file.getvalue())
def test_encoding_UTF8(self):
- # https://github.com/behdad/fonttools/issues/246
+ # https://github.com/fonttools/fonttools/issues/246
writer = XMLWriter(BytesIO(), encoding="UTF8")
self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
writer.file.getvalue())
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index d78c496b..2fc3b8af 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -298,7 +298,7 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"])
def test_options(self):
- # https://github.com/behdad/fonttools/issues/413
+ # https://github.com/fonttools/fonttools/issues/413
opt1 = subset.Options()
self.assertTrue('Xyz-' not in opt1.layout_features)
opt2 = subset.Options()
diff --git a/Tests/ttLib/data/TestOTF-Regular.otx b/Tests/ttLib/data/TestOTF-Regular.otx
index 573fe24c..92e0b2f6 100644
--- a/Tests/ttLib/data/TestOTF-Regular.otx
+++ b/Tests/ttLib/data/TestOTF-Regular.otx
@@ -142,13 +142,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
<namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
Test TTF
@@ -184,13 +184,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
</name>
diff --git a/Tests/ttLib/data/TestTTF-Regular.ttx b/Tests/ttLib/data/TestTTF-Regular.ttx
index 8ffcefed..1f1dd2b4 100644
--- a/Tests/ttLib/data/TestTTF-Regular.ttx
+++ b/Tests/ttLib/data/TestTTF-Regular.ttx
@@ -462,13 +462,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
<namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
Test TTF
@@ -504,13 +504,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
</name>
diff --git a/Tests/ttLib/tables/TupleVariation_test.py b/Tests/ttLib/tables/TupleVariation_test.py
index 6986d5b6..aab4cba0 100644
--- a/Tests/ttLib/tables/TupleVariation_test.py
+++ b/Tests/ttLib/tables/TupleVariation_test.py
@@ -107,7 +107,7 @@ class TupleVariationTest(unittest.TestCase):
self.assertIn("bad delta format", [r.msg for r in captor.records])
self.assertEqual([
'<tuple>',
- '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+ '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
'<!-- bad delta #0 -->',
'</tuple>',
], TupleVariationTest.xml_lines(writer))
@@ -118,7 +118,7 @@ class TupleVariationTest(unittest.TestCase):
g.toXML(writer, ["wdth", "wght", "opsz"])
self.assertEqual([
'<tuple>',
- '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+ '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
'<coord axis="wght" value="1.0"/>',
'<coord axis="opsz" value="-0.7"/>',
'<delta cvt="0" value="42"/>',
@@ -134,7 +134,7 @@ class TupleVariationTest(unittest.TestCase):
g.toXML(writer, ["wdth", "wght", "opsz"])
self.assertEqual([
'<tuple>',
- '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+ '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
'<coord axis="wght" value="1.0"/>',
'<coord axis="opsz" value="-0.7"/>',
'<delta pt="0" x="9" y="8"/>',
@@ -370,7 +370,7 @@ class TupleVariationTest(unittest.TestCase):
self.assertEqual(({"wght": -1.0, "wdth": 0.5}, 6), decompileCoord(["wght", "wdth"], data, 2))
def test_decompileCoord_roundTrip(self):
- # Make sure we are not affected by https://github.com/behdad/fonttools/issues/286
+ # Make sure we are not affected by https://github.com/fonttools/fonttools/issues/286
data = deHexStr("7F B9 80 35")
values, _ = TupleVariation.decompileCoord_(["wght", "wdth"], data, 0)
axisValues = {axis:(val, val, val) for axis, val in values.items()}
diff --git a/Tests/ttLib/tables/_c_m_a_p_test.py b/Tests/ttLib/tables/_c_m_a_p_test.py
index 306d0489..233cd14f 100644
--- a/Tests/ttLib/tables/_c_m_a_p_test.py
+++ b/Tests/ttLib/tables/_c_m_a_p_test.py
@@ -51,6 +51,47 @@ class CmapSubtableTest(unittest.TestCase):
self.assertEqual(subtable.getEncoding("ascii"), "ascii")
self.assertEqual(subtable.getEncoding(default="xyz"), "xyz")
+ def test_compile_2(self):
+ subtable = self.makeSubtable(2, 1, 2, 0)
+ subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)}
+ font = ttLib.TTFont()
+ font.setGlyphOrder([".notdef"] + list(subtable.cmap.values()))
+ data = subtable.compile(font)
+
+ subtable2 = CmapSubtable.newSubtable(2)
+ subtable2.decompile(data, font)
+ self.assertEqual(subtable2.cmap, subtable.cmap)
+
+ def test_compile_2_rebuild_rev_glyph_order(self):
+ for fmt in [2, 4, 12]:
+ subtable = self.makeSubtable(fmt, 1, 2, 0)
+ subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)}
+ font = ttLib.TTFont()
+ font.setGlyphOrder([".notdef"] + list(subtable.cmap.values()))
+ font._reverseGlyphOrderDict = {} # force first KeyError branch in subtable.compile()
+ data = subtable.compile(font)
+ subtable2 = CmapSubtable.newSubtable(fmt)
+ subtable2.decompile(data, font)
+ self.assertEqual(subtable2.cmap, subtable.cmap, str(fmt))
+
+ def test_compile_2_gids(self):
+ for fmt in [2, 4, 12]:
+ subtable = self.makeSubtable(fmt, 1, 3, 0)
+ subtable.cmap = {0x0041:'gid001', 0x0042:'gid002'}
+ font = ttLib.TTFont()
+ font.setGlyphOrder([".notdef"])
+ data = subtable.compile(font)
+
+ def test_compile_decompile_4_empty(self):
+ subtable = self.makeSubtable(4, 3, 1, 0)
+ subtable.cmap = {}
+ font = ttLib.TTFont()
+ font.setGlyphOrder([])
+ data = subtable.compile(font)
+ subtable2 = CmapSubtable.newSubtable(4)
+ subtable2.decompile(data, font)
+ self.assertEqual(subtable2.cmap, {})
+
def test_decompile_4(self):
subtable = CmapSubtable.newSubtable(4)
font = ttLib.TTFont()
diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py
index d0786924..00c74bcf 100644
--- a/Tests/ttLib/tables/_g_l_y_f_test.py
+++ b/Tests/ttLib/tables/_g_l_y_f_test.py
@@ -1,7 +1,8 @@
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc.fixedTools import otRound
-from fontTools.ttLib import TTFont, newTable
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.ttLib import TTFont, newTable, TTLibError
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
import sys
import array
@@ -180,6 +181,13 @@ def strip_ttLibVersion(string):
class glyfTableTest(unittest.TestCase):
+ def __init__(self, methodName):
+ unittest.TestCase.__init__(self, methodName)
+ # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+ # and fires deprecation warnings if a program uses the old name.
+ if not hasattr(self, "assertRaisesRegex"):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
@classmethod
def setUpClass(cls):
with open(GLYF_BIN, 'rb') as f:
@@ -215,6 +223,23 @@ class glyfTableTest(unittest.TestCase):
glyfData = glyfTable.compile(font)
self.assertEqual(glyfData, self.glyfData)
+ def test_recursiveComponent(self):
+ glyphSet = {}
+ pen_dummy = TTGlyphPen(glyphSet)
+ glyph_dummy = pen_dummy.glyph()
+ glyphSet["A"] = glyph_dummy
+ glyphSet["B"] = glyph_dummy
+ pen_A = TTGlyphPen(glyphSet)
+ pen_A.addComponent("B", (1, 0, 0, 1, 0, 0))
+ pen_B = TTGlyphPen(glyphSet)
+ pen_B.addComponent("A", (1, 0, 0, 1, 0, 0))
+ glyph_A = pen_A.glyph()
+ glyph_B = pen_B.glyph()
+ glyphSet["A"] = glyph_A
+ glyphSet["B"] = glyph_B
+ with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"):
+ glyph_A.getCoordinates(glyphSet)
+
if __name__ == "__main__":
import sys
diff --git a/Tests/ttLib/tables/_g_v_a_r_test.py b/Tests/ttLib/tables/_g_v_a_r_test.py
index 64adbb61..ebae5806 100644
--- a/Tests/ttLib/tables/_g_v_a_r_test.py
+++ b/Tests/ttLib/tables/_g_v_a_r_test.py
@@ -93,7 +93,7 @@ GVAR_XML = [
'</glyphVariations>',
'<glyphVariations glyph="I">',
' <tuple>',
- ' <coord axis="wght" max="1.0" min="0.0" value="0.5"/>',
+ ' <coord axis="wght" min="0.0" value="0.5" max="1.0"/>',
' <delta pt="0" x="3" y="3"/>',
' <delta pt="1" x="1" y="1"/>',
' <delta pt="2" x="4" y="4"/>',
diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py
index fde14bc8..136b63f3 100644
--- a/Tests/ttLib/tables/_n_a_m_e_test.py
+++ b/Tests/ttLib/tables/_n_a_m_e_test.py
@@ -171,7 +171,7 @@ class NameTableTest(unittest.TestCase):
captor.assertRegex("cannot store language la into 'ltag' table")
def test_decompile_badOffset(self):
- # https://github.com/behdad/fonttools/issues/525
+ # https://github.com/fonttools/fonttools/issues/525
table = table__n_a_m_e()
badRecord = {
"platformID": 1,
diff --git a/Tests/ttLib/tables/otTables_test.py b/Tests/ttLib/tables/otTables_test.py
index 5a771e61..f39e27c2 100644
--- a/Tests/ttLib/tables/otTables_test.py
+++ b/Tests/ttLib/tables/otTables_test.py
@@ -166,7 +166,7 @@ class MultipleSubstTest(unittest.TestCase):
{'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
def test_fromXML_oldFormat_bug385(self):
- # https://github.com/behdad/fonttools/issues/385
+ # https://github.com/fonttools/fonttools/issues/385
table = otTables.MultipleSubst()
table.Format = 1
for name, attrs, content in parseXML(
diff --git a/Tests/ttx/data/TestOTF.ttx b/Tests/ttx/data/TestOTF.ttx
index 852dacfa..96f18449 100644
--- a/Tests/ttx/data/TestOTF.ttx
+++ b/Tests/ttx/data/TestOTF.ttx
@@ -142,13 +142,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
<namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
Test TTF
@@ -184,13 +184,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
</name>
diff --git a/Tests/ttx/data/TestTTF.ttx b/Tests/ttx/data/TestTTF.ttx
index c283a295..66caf6ce 100644
--- a/Tests/ttx/data/TestTTF.ttx
+++ b/Tests/ttx/data/TestTTF.ttx
@@ -462,13 +462,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
<namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
Test TTF
@@ -504,13 +504,13 @@
FontTools
</namerecord>
<namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools
+ https://github.com/fonttools/fonttools
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
- https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+ https://github.com/fonttools/fonttools/blob/master/LICENSE
</namerecord>
</name>
diff --git a/Tests/varLib/data/test_results/Build.ttx b/Tests/varLib/data/test_results/Build.ttx
index 5d665e35..1bc1880b 100644
--- a/Tests/varLib/data/test_results/Build.ttx
+++ b/Tests/varLib/data/test_results/Build.ttx
@@ -281,7 +281,7 @@
<delta pt="15" x="0" y="0"/>
<delta pt="16" x="0" y="0"/>
<delta pt="17" x="0" y="0"/>
- <delta pt="18" x="0" y="7"/>
+ <delta pt="18" x="0" y="0"/>
<delta pt="19" x="0" y="0"/>
</tuple>
<tuple>
@@ -304,7 +304,7 @@
<delta pt="15" x="0" y="0"/>
<delta pt="16" x="0" y="0"/>
<delta pt="17" x="0" y="0"/>
- <delta pt="18" x="0" y="-18"/>
+ <delta pt="18" x="0" y="0"/>
<delta pt="19" x="0" y="0"/>
</tuple>
<tuple>
@@ -424,7 +424,7 @@
<delta pt="23" x="-2" y="0"/>
<delta pt="24" x="0" y="0"/>
<delta pt="25" x="-10" y="0"/>
- <delta pt="26" x="0" y="9"/>
+ <delta pt="26" x="0" y="0"/>
<delta pt="27" x="0" y="0"/>
</tuple>
<tuple>
@@ -455,7 +455,7 @@
<delta pt="23" x="12" y="0"/>
<delta pt="24" x="0" y="0"/>
<delta pt="25" x="17" y="0"/>
- <delta pt="26" x="0" y="-23"/>
+ <delta pt="26" x="0" y="0"/>
<delta pt="27" x="0" y="0"/>
</tuple>
<tuple>
@@ -620,7 +620,7 @@
<delta pt="60" x="33" y="-1"/>
<delta pt="61" x="0" y="0"/>
<delta pt="62" x="-3" y="0"/>
- <delta pt="63" x="0" y="-4"/>
+ <delta pt="63" x="0" y="0"/>
<delta pt="64" x="0" y="0"/>
</tuple>
<tuple>
@@ -688,8 +688,8 @@
<delta pt="60" x="-25" y="-13"/>
<delta pt="61" x="0" y="0"/>
<delta pt="62" x="32" y="0"/>
- <delta pt="63" x="0" y="16"/>
- <delta pt="64" x="0" y="3"/>
+ <delta pt="63" x="0" y="0"/>
+ <delta pt="64" x="0" y="0"/>
</tuple>
<tuple>
<coord axis="cntr" value="1.0"/>
@@ -965,7 +965,7 @@
<delta pt="61" x="-15" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="-7" y="0"/>
- <delta pt="64" x="0" y="12"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
@@ -1034,7 +1034,7 @@
<delta pt="61" x="39" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="63" y="0"/>
- <delta pt="64" x="0" y="-19"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
@@ -1314,7 +1314,7 @@
<delta pt="61" x="-15" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="-7" y="0"/>
- <delta pt="64" x="0" y="12"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
@@ -1383,7 +1383,7 @@
<delta pt="61" x="49" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="63" y="0"/>
- <delta pt="64" x="0" y="-19"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx
index 84a75736..6875c712 100644
--- a/Tests/varLib/data/test_results/BuildMain.ttx
+++ b/Tests/varLib/data/test_results/BuildMain.ttx
@@ -927,7 +927,7 @@
<delta pt="15" x="0" y="0"/>
<delta pt="16" x="0" y="0"/>
<delta pt="17" x="0" y="0"/>
- <delta pt="18" x="0" y="7"/>
+ <delta pt="18" x="0" y="0"/>
<delta pt="19" x="0" y="0"/>
</tuple>
<tuple>
@@ -950,7 +950,7 @@
<delta pt="15" x="0" y="0"/>
<delta pt="16" x="0" y="0"/>
<delta pt="17" x="0" y="0"/>
- <delta pt="18" x="0" y="-18"/>
+ <delta pt="18" x="0" y="0"/>
<delta pt="19" x="0" y="0"/>
</tuple>
<tuple>
@@ -1070,7 +1070,7 @@
<delta pt="23" x="-2" y="0"/>
<delta pt="24" x="0" y="0"/>
<delta pt="25" x="-10" y="0"/>
- <delta pt="26" x="0" y="9"/>
+ <delta pt="26" x="0" y="0"/>
<delta pt="27" x="0" y="0"/>
</tuple>
<tuple>
@@ -1101,7 +1101,7 @@
<delta pt="23" x="12" y="0"/>
<delta pt="24" x="0" y="0"/>
<delta pt="25" x="17" y="0"/>
- <delta pt="26" x="0" y="-23"/>
+ <delta pt="26" x="0" y="0"/>
<delta pt="27" x="0" y="0"/>
</tuple>
<tuple>
@@ -1266,7 +1266,7 @@
<delta pt="60" x="33" y="-1"/>
<delta pt="61" x="0" y="0"/>
<delta pt="62" x="-3" y="0"/>
- <delta pt="63" x="0" y="-4"/>
+ <delta pt="63" x="0" y="0"/>
<delta pt="64" x="0" y="0"/>
</tuple>
<tuple>
@@ -1334,8 +1334,8 @@
<delta pt="60" x="-25" y="-13"/>
<delta pt="61" x="0" y="0"/>
<delta pt="62" x="32" y="0"/>
- <delta pt="63" x="0" y="16"/>
- <delta pt="64" x="0" y="3"/>
+ <delta pt="63" x="0" y="0"/>
+ <delta pt="64" x="0" y="0"/>
</tuple>
<tuple>
<coord axis="cntr" value="1.0"/>
@@ -1611,7 +1611,7 @@
<delta pt="61" x="-15" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="-7" y="0"/>
- <delta pt="64" x="0" y="12"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
@@ -1680,7 +1680,7 @@
<delta pt="61" x="39" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="63" y="0"/>
- <delta pt="64" x="0" y="-19"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
@@ -1960,7 +1960,7 @@
<delta pt="61" x="-15" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="-7" y="0"/>
- <delta pt="64" x="0" y="12"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
@@ -2029,7 +2029,7 @@
<delta pt="61" x="49" y="0"/>
<delta pt="62" x="0" y="0"/>
<delta pt="63" x="63" y="0"/>
- <delta pt="64" x="0" y="-19"/>
+ <delta pt="64" x="0" y="0"/>
<delta pt="65" x="0" y="0"/>
</tuple>
<tuple>
diff --git a/Tests/varLib/data/test_results/Mutator.ttx b/Tests/varLib/data/test_results/Mutator.ttx
index a106d213..fac0997a 100644
--- a/Tests/varLib/data/test_results/Mutator.ttx
+++ b/Tests/varLib/data/test_results/Mutator.ttx
@@ -154,7 +154,7 @@
<TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="666">
<contour>
- <pt x="83" y="0" on="1"/>
+ <pt x="83" y="0" on="1" overlap="1"/>
<pt x="503" y="666" on="1"/>
<pt x="557" y="666" on="1"/>
<pt x="137" y="0" on="1"/>
@@ -190,7 +190,7 @@
<TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="474" yMax="746">
<contour>
- <pt x="251" y="31" on="1"/>
+ <pt x="251" y="31" on="1" overlap="1"/>
<pt x="309" y="31" on="0"/>
<pt x="379" y="92" on="0"/>
<pt x="379" y="144" on="1"/>
@@ -262,7 +262,7 @@
<TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="474" yMax="746">
<contour>
- <pt x="251" y="31" on="1"/>
+ <pt x="251" y="31" on="1" overlap="1"/>
<pt x="308" y="31" on="0"/>
<pt x="377" y="90" on="0"/>
<pt x="377" y="142" on="1"/>
@@ -334,7 +334,7 @@
<TTGlyph name="uni0041" xMin="7" yMin="0" xMax="656" yMax="670">
<contour>
- <pt x="7" y="0" on="1"/>
+ <pt x="7" y="0" on="1" overlap="1"/>
<pt x="7" y="38" on="1"/>
<pt x="104" y="53" on="1"/>
<pt x="124" y="53" on="1"/>
@@ -370,7 +370,7 @@
<TTGlyph name="uni0061" xMin="42" yMin="-14" xMax="511" yMax="490">
<contour>
- <pt x="42" y="110" on="1"/>
+ <pt x="42" y="110" on="1" overlap="1"/>
<pt x="42" y="157" on="0"/>
<pt x="110" y="229" on="0"/>
<pt x="214" y="265" on="1"/>
diff --git a/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
index a20f0e4f..28e0766a 100755
--- a/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
+++ b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
@@ -168,7 +168,7 @@
<TTGlyph name="a" xMin="38" yMin="-12" xMax="388" yMax="468">
<contour>
- <pt x="312" y="0" on="1"/>
+ <pt x="312" y="0" on="1" overlap="1"/>
<pt x="312" y="64" on="1"/>
<pt x="244" y="-12" on="1"/>
<pt x="180" y="-12" on="1"/>
@@ -200,7 +200,7 @@
<TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
<contour>
- <pt x="218" y="468" on="1"/>
+ <pt x="218" y="468" on="1" overlap="1"/>
<pt x="284" y="468" on="1"/>
<pt x="426" y="316" on="1"/>
<pt x="426" y="140" on="1"/>
@@ -229,7 +229,7 @@
<TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
<TTGlyph name="q" xMin="38" yMin="-172" xMax="388" yMax="468">
- <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x4"/>
+ <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x404"/>
</TTGlyph>
<TTGlyph name="space"/><!-- contains no outline data -->
diff --git a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
index 1800479b..1f7b651f 100755
--- a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
+++ b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
@@ -157,7 +157,7 @@
<TTGlyph name="a" xMin="38" yMin="-12" xMax="388" yMax="468">
<contour>
- <pt x="312" y="0" on="1"/>
+ <pt x="312" y="0" on="1" overlap="1"/>
<pt x="312" y="64" on="1"/>
<pt x="244" y="-12" on="1"/>
<pt x="180" y="-12" on="1"/>
@@ -185,7 +185,7 @@
<TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
<contour>
- <pt x="218" y="468" on="1"/>
+ <pt x="218" y="468" on="1" overlap="1"/>
<pt x="284" y="468" on="1"/>
<pt x="426" y="316" on="1"/>
<pt x="426" y="140" on="1"/>
@@ -214,7 +214,7 @@
<TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
<TTGlyph name="q" xMin="38" yMin="-172" xMax="388" yMax="468">
- <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x4"/>
+ <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x404"/>
</TTGlyph>
<TTGlyph name="space"/><!-- contains no outline data -->
diff --git a/Tests/varLib/data/test_results/SparseMasters.ttx b/Tests/varLib/data/test_results/SparseMasters.ttx
index 99a80bfb..06e58c92 100644
--- a/Tests/varLib/data/test_results/SparseMasters.ttx
+++ b/Tests/varLib/data/test_results/SparseMasters.ttx
@@ -574,7 +574,7 @@
</glyphVariations>
<glyphVariations glyph="e">
<tuple>
- <coord axis="wght" max="1.0" min="0.0" value="0.36365"/>
+ <coord axis="wght" min="0.0" value="0.36365" max="1.0"/>
<delta pt="0" x="-1" y="-25"/>
<delta pt="1" x="0" y="0"/>
<delta pt="2" x="-84" y="5"/>
@@ -594,7 +594,7 @@
<delta pt="16" x="0" y="0"/>
</tuple>
<tuple>
- <coord axis="wght" max="1.0" min="0.36365" value="1.0"/>
+ <coord axis="wght" min="0.36365" value="1.0" max="1.0"/>
<delta pt="0" x="70" y="1"/>
<delta pt="1" x="70" y="1"/>
<delta pt="2" x="-76" y="1"/>
@@ -610,7 +610,7 @@
<delta pt="12" x="25" y="-1"/>
<delta pt="13" x="0" y="0"/>
<delta pt="14" x="0" y="0"/>
- <delta pt="15" x="0" y="35"/>
+ <delta pt="15" x="0" y="0"/>
<delta pt="16" x="0" y="0"/>
</tuple>
</glyphVariations>
@@ -632,7 +632,7 @@
<delta pt="12" x="0" y="0"/>
<delta pt="13" x="0" y="0"/>
<delta pt="14" x="0" y="0"/>
- <delta pt="15" x="0" y="45"/>
+ <delta pt="15" x="0" y="0"/>
</tuple>
</glyphVariations>
<glyphVariations glyph="dotabovecomb">
@@ -644,15 +644,14 @@
<delta pt="3" x="-27" y="-20"/>
<delta pt="4" x="0" y="0"/>
<delta pt="5" x="0" y="0"/>
- <delta pt="6" x="0" y="28"/>
- <delta pt="7" x="0" y="18"/>
+ <delta pt="6" x="0" y="0"/>
+ <delta pt="7" x="0" y="0"/>
</tuple>
</glyphVariations>
<glyphVariations glyph="edotabove">
<tuple>
<coord axis="wght" value="1.0"/>
<delta pt="1" x="-6" y="91"/>
- <delta pt="4" x="0" y="119"/>
</tuple>
</glyphVariations>
</gvar>
diff --git a/Tests/varLib/models_test.py b/Tests/varLib/models_test.py
index c5c2a9a6..ae67e0e9 100644
--- a/Tests/varLib/models_test.py
+++ b/Tests/varLib/models_test.py
@@ -2,6 +2,7 @@ from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.varLib.models import (
normalizeLocation, supportScalar, VariationModel)
+import pytest
def test_normalizeLocation():
@@ -35,65 +36,121 @@ def test_supportScalar():
assert supportScalar({'wght':2.5}, {'wght':(0,2,4)}) == 0.75
-def test_VariationModel():
- locations = [
- {'wght':100},
- {'wght':-100},
- {'wght':-180},
- {'wdth':+.3},
- {'wght':+120,'wdth':.3},
- {'wght':+120,'wdth':.2},
- {},
- {'wght':+180,'wdth':.3},
- {'wght':+180},
- ]
- model = VariationModel(locations, axisOrder=['wght'])
+class VariationModelTest(object):
- assert model.locations == [
- {},
- {'wght': -100},
- {'wght': -180},
- {'wght': 100},
- {'wght': 180},
- {'wdth': 0.3},
- {'wdth': 0.3, 'wght': 180},
- {'wdth': 0.3, 'wght': 120},
- {'wdth': 0.2, 'wght': 120}]
+ @pytest.mark.parametrize(
+ "locations, axisOrder, sortedLocs, supports, deltaWeights",
+ [
+ (
+ [
+ {'wght': 0.55, 'wdth': 0.0},
+ {'wght': -0.55, 'wdth': 0.0},
+ {'wght': -1.0, 'wdth': 0.0},
+ {'wght': 0.0, 'wdth': 1.0},
+ {'wght': 0.66, 'wdth': 1.0},
+ {'wght': 0.66, 'wdth': 0.66},
+ {'wght': 0.0, 'wdth': 0.0},
+ {'wght': 1.0, 'wdth': 1.0},
+ {'wght': 1.0, 'wdth': 0.0},
+ ],
+ ["wght"],
+ [
+ {},
+ {'wght': -0.55},
+ {'wght': -1.0},
+ {'wght': 0.55},
+ {'wght': 1.0},
+ {'wdth': 1.0},
+ {'wdth': 1.0, 'wght': 1.0},
+ {'wdth': 1.0, 'wght': 0.66},
+ {'wdth': 0.66, 'wght': 0.66}
+ ],
+ [
+ {},
+ {'wght': (-1.0, -0.55, 0)},
+ {'wght': (-1.0, -1.0, -0.55)},
+ {'wght': (0, 0.55, 1.0)},
+ {'wght': (0.55, 1.0, 1.0)},
+ {'wdth': (0, 1.0, 1.0)},
+ {'wdth': (0, 1.0, 1.0), 'wght': (0, 1.0, 1.0)},
+ {'wdth': (0, 1.0, 1.0), 'wght': (0, 0.66, 1.0)},
+ {'wdth': (0, 0.66, 1.0), 'wght': (0, 0.66, 1.0)}
+ ],
+ [
+ {},
+ {0: 1.0},
+ {0: 1.0},
+ {0: 1.0},
+ {0: 1.0},
+ {0: 1.0},
+ {0: 1.0,
+ 4: 1.0,
+ 5: 1.0},
+ {0: 1.0,
+ 3: 0.7555555555555555,
+ 4: 0.24444444444444444,
+ 5: 1.0,
+ 6: 0.66},
+ {0: 1.0,
+ 3: 0.7555555555555555,
+ 4: 0.24444444444444444,
+ 5: 0.66,
+ 6: 0.43560000000000006,
+ 7: 0.66}
+ ]
+ ),
+ (
+ [
+ {},
+ {'bar': 0.5},
+ {'bar': 1.0},
+ {'foo': 1.0},
+ {'bar': 0.5, 'foo': 1.0},
+ {'bar': 1.0, 'foo': 1.0},
+ ],
+ None,
+ [
+ {},
+ {'bar': 0.5},
+ {'bar': 1.0},
+ {'foo': 1.0},
+ {'bar': 0.5, 'foo': 1.0},
+ {'bar': 1.0, 'foo': 1.0},
+ ],
+ [
+ {},
+ {'bar': (0, 0.5, 1.0)},
+ {'bar': (0.5, 1.0, 1.0)},
+ {'foo': (0, 1.0, 1.0)},
+ {'bar': (0, 0.5, 1.0), 'foo': (0, 1.0, 1.0)},
+ {'bar': (0.5, 1.0, 1.0), 'foo': (0, 1.0, 1.0)},
+ ],
+ [
+ {},
+ {0: 1.0},
+ {0: 1.0},
+ {0: 1.0},
+ {0: 1.0, 1: 1.0, 3: 1.0},
+ {0: 1.0, 2: 1.0, 3: 1.0},
+ ],
+ )
+ ]
+ )
+ def test_init(
+ self, locations, axisOrder, sortedLocs, supports, deltaWeights
+ ):
+ model = VariationModel(locations, axisOrder=axisOrder)
- assert model.deltaWeights == [
- {},
- {0: 1.0},
- {0: 1.0},
- {0: 1.0},
- {0: 1.0},
- {0: 1.0},
- {0: 1.0, 4: 1.0, 5: 1.0},
- {0: 1.0, 3: 0.75, 4: 0.25, 5: 1.0, 6: 0.6666666666666666},
- {0: 1.0,
- 3: 0.75,
- 4: 0.25,
- 5: 0.6666666666666667,
- 6: 0.4444444444444445,
- 7: 0.6666666666666667}]
+ assert model.locations == sortedLocs
+ assert model.supports == supports
+ assert model.deltaWeights == deltaWeights
-def test_VariationModel():
- locations = [
- {},
- {'bar': 0.5},
- {'bar': 1.0},
- {'foo': 1.0},
- {'bar': 0.5, 'foo': 1.0},
- {'bar': 1.0, 'foo': 1.0},
- ]
- model = VariationModel(locations)
-
- assert model.locations == locations
-
- assert model.supports == [
- {},
- {'bar': (0, 0.5, 1.0)},
- {'bar': (0.5, 1.0, 1.0)},
- {'foo': (0, 1.0, 1.0)},
- {'bar': (0, 0.5, 1.0), 'foo': (0, 1.0, 1.0)},
- {'bar': (0.5, 1.0, 1.0), 'foo': (0, 1.0, 1.0)},
- ]
+ def test_init_duplicate_locations(self):
+ with pytest.raises(ValueError, match="locations must be unique"):
+ VariationModel(
+ [
+ {"foo": 0.0, "bar": 0.0},
+ {"foo": 1.0, "bar": 1.0},
+ {"bar": 1.0, "foo": 1.0},
+ ]
+ )
diff --git a/requirements.txt b/requirements.txt
index acfd0fd4..8532edd5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,6 @@ brotli==1.0.7; platform_python_implementation != "PyPy"
brotlipy==0.7.0; platform_python_implementation == "PyPy"
unicodedata2==11.0.0; python_version < '3.7' and platform_python_implementation != "PyPy"
scipy==1.2.1; platform_python_implementation != "PyPy"
-munkres==1.0.12; platform_python_implementation == "PyPy"
+munkres==1.0.12; platform_python_implementation == "PyPy" # pyup: ignore
zopfli==0.1.6
-fs==2.3.1
+fs==2.4.4
diff --git a/setup.cfg b/setup.cfg
index 9fe169ed..a3bcdd1c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 3.38.0
+current_version = 3.39.0
commit = True
tag = False
tag_name = {new_version}
diff --git a/setup.py b/setup.py
index 273c585c..0b244a49 100755
--- a/setup.py
+++ b/setup.py
@@ -352,7 +352,7 @@ def find_data_files(manpath="share/man"):
setup(
name="fonttools",
- version="3.38.0",
+ version="3.39.0",
description="Tools to manipulate font files",
author="Just van Rossum",
author_email="just@letterror.com",
@@ -374,7 +374,6 @@ setup(
"ttx = fontTools.ttx:main",
"pyftsubset = fontTools.subset:main",
"pyftmerge = fontTools.merge:main",
- "pyftinspect = fontTools.inspect:main"
]
},
cmdclass={