aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/fonttools
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-02-21 09:21:11 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-02-21 09:29:49 +0300
commit7732db443bee366753d1b056727860be4b833df0 (patch)
tree5d69ae560f055f7b9ff27ebee26440dc19eb6e86 /contrib/python/fonttools
parent705f460d33000009c49a47b51913a7f29d2d6c46 (diff)
downloadydb-7732db443bee366753d1b056727860be4b833df0.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python/fonttools')
-rw-r--r--contrib/python/fonttools/.dist-info/METADATA34
-rw-r--r--contrib/python/fonttools/README.rst2
-rw-r--r--contrib/python/fonttools/fontTools/__init__.py2
-rw-r--r--contrib/python/fonttools/fontTools/designspaceLib/__init__.py68
-rw-r--r--contrib/python/fonttools/fontTools/designspaceLib/__main__.py6
-rw-r--r--contrib/python/fonttools/fontTools/feaLib/builder.py32
-rw-r--r--contrib/python/fonttools/fontTools/feaLib/lexer.py4
-rw-r--r--contrib/python/fonttools/fontTools/feaLib/parser.py8
-rw-r--r--contrib/python/fonttools/fontTools/otlLib/builder.py54
-rw-r--r--contrib/python/fonttools/fontTools/pens/hashPointPen.py14
-rw-r--r--contrib/python/fonttools/fontTools/pens/roundingPen.py46
-rw-r--r--contrib/python/fonttools/fontTools/pens/svgPathPen.py20
-rw-r--r--contrib/python/fonttools/fontTools/subset/__init__.py3
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/otTables.py35
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/sbixGlyph.py14
-rw-r--r--contrib/python/fonttools/fontTools/varLib/__init__.py8
-rw-r--r--contrib/python/fonttools/fontTools/varLib/instancer/__init__.py2
-rw-r--r--contrib/python/fonttools/ya.make3
18 files changed, 251 insertions, 104 deletions
diff --git a/contrib/python/fonttools/.dist-info/METADATA b/contrib/python/fonttools/.dist-info/METADATA
index f9e01c388f..b335b936d7 100644
--- a/contrib/python/fonttools/.dist-info/METADATA
+++ b/contrib/python/fonttools/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: fonttools
-Version: 4.47.2
+Version: 4.48.1
Summary: Tools to manipulate font files
Home-page: http://github.com/fonttools/fonttools
Author: Just van Rossum
@@ -31,7 +31,7 @@ Requires-Python: >=3.8
License-File: LICENSE
Provides-Extra: all
Requires-Dist: fs <3,>=2.2.0 ; extra == 'all'
-Requires-Dist: lxml <5,>=4.0 ; extra == 'all'
+Requires-Dist: lxml >=4.0 ; extra == 'all'
Requires-Dist: zopfli >=0.1.4 ; extra == 'all'
Requires-Dist: lz4 >=1.7.4.2 ; extra == 'all'
Requires-Dist: pycairo ; extra == 'all'
@@ -52,7 +52,7 @@ Requires-Dist: pycairo ; extra == 'interpolatable'
Requires-Dist: scipy ; (platform_python_implementation != "PyPy") and extra == 'interpolatable'
Requires-Dist: munkres ; (platform_python_implementation == "PyPy") and extra == 'interpolatable'
Provides-Extra: lxml
-Requires-Dist: lxml <5,>=4.0 ; extra == 'lxml'
+Requires-Dist: lxml >=4.0 ; extra == 'lxml'
Provides-Extra: pathops
Requires-Dist: skia-pathops >=0.5.0 ; extra == 'pathops'
Provides-Extra: plot
@@ -118,7 +118,7 @@ Python 3 `venv <https://docs.python.org/3/library/venv.html>`__ module.
# create new virtual environment called e.g. 'fonttools-venv', or anything you like
python -m virtualenv fonttools-venv
- # source the `activate` shell script to enter the environment (Un*x); to exit, just type `deactivate`
+ # source the `activate` shell script to enter the environment (Unix-like); to exit, just type `deactivate`
. fonttools-venv/bin/activate
# to activate the virtual environment in Windows `cmd.exe`, do
@@ -375,6 +375,32 @@ Have fun!
Changelog
~~~~~~~~~
+4.48.1 (released 2024-02-06)
+----------------------------
+
+- Fixed uploading wheels to PyPI, no code changes since v4.48.0.
+
+4.48.0 (released 2024-02-06)
+----------------------------
+
+- [varLib] Do not log when there are no OTL tables to be merged.
+- [setup.py] Do not restrict lxml<5 any more, tests pass just fine with lxml>=5.
+- [feaLib] Remove glyph and class names length restrictions in FEA (#3424).
+- [roundingPens] Added ``transformRoundFunc`` parameter to the rounding pens to allow
+ for custom rounding of the components' transforms (#3426).
+- [feaLib] Keep declaration order of ligature components within a ligature set, instead
+ of sorting by glyph name (#3429).
+- [feaLib] Fixed ordering of alternates in ``aalt`` lookups, following the declaration
+ order of feature references within the ``aalt`` feature block (#3430).
+- [varLib.instancer] Fixed a bug in the instancer's IUP optimization (#3432).
+- [sbix] Support sbix glyphs with new graphicType "flip" (#3433).
+- [svgPathPen] Added ``--glyphs`` option to dump the SVG paths for the named glyphs
+ in the font (0572f78).
+- [designspaceLib] Added "description" attribute to ``<mappings>`` and ``<mapping>``
+ elements, and allow multiple ``<mappings>`` elements to group ``<mapping>`` elements
+ that are logically related (#3435, #3437).
+- [otlLib] Correctly choose the most compact GSUB contextual lookup format (#3439).
+
4.47.2 (released 2024-01-11)
----------------------------
diff --git a/contrib/python/fonttools/README.rst b/contrib/python/fonttools/README.rst
index d84282fc76..2274fbdc69 100644
--- a/contrib/python/fonttools/README.rst
+++ b/contrib/python/fonttools/README.rst
@@ -44,7 +44,7 @@ Python 3 `venv <https://docs.python.org/3/library/venv.html>`__ module.
# create new virtual environment called e.g. 'fonttools-venv', or anything you like
python -m virtualenv fonttools-venv
- # source the `activate` shell script to enter the environment (Un*x); to exit, just type `deactivate`
+ # source the `activate` shell script to enter the environment (Unix-like); to exit, just type `deactivate`
. fonttools-venv/bin/activate
# to activate the virtual environment in Windows `cmd.exe`, do
diff --git a/contrib/python/fonttools/fontTools/__init__.py b/contrib/python/fonttools/fontTools/__init__.py
index 7410d3c7fe..feb093929e 100644
--- a/contrib/python/fonttools/fontTools/__init__.py
+++ b/contrib/python/fonttools/fontTools/__init__.py
@@ -3,6 +3,6 @@ from fontTools.misc.loggingTools import configLogger
log = logging.getLogger(__name__)
-version = __version__ = "4.47.2"
+version = __version__ = "4.48.1"
__all__ = ["version", "log", "configLogger"]
diff --git a/contrib/python/fonttools/fontTools/designspaceLib/__init__.py b/contrib/python/fonttools/fontTools/designspaceLib/__init__.py
index 69d4912c09..d6789f5f61 100644
--- a/contrib/python/fonttools/fontTools/designspaceLib/__init__.py
+++ b/contrib/python/fonttools/fontTools/designspaceLib/__init__.py
@@ -476,7 +476,14 @@ class AxisMappingDescriptor(SimpleDescriptor):
_attrs = ["inputLocation", "outputLocation"]
- def __init__(self, *, inputLocation=None, outputLocation=None):
+ def __init__(
+ self,
+ *,
+ inputLocation=None,
+ outputLocation=None,
+ description=None,
+ groupDescription=None,
+ ):
self.inputLocation: SimpleLocationDict = inputLocation or {}
"""dict. Axis values for the input of the mapping, in design space coordinates.
@@ -491,6 +498,20 @@ class AxisMappingDescriptor(SimpleDescriptor):
.. versionadded:: 5.1
"""
+ self.description = description
+ """string. A description of the mapping.
+
+ varLib.
+
+ .. versionadded:: 5.2
+ """
+ self.groupDescription = groupDescription
+ """string. A description of the group of mappings.
+
+ varLib.
+
+ .. versionadded:: 5.2
+ """
class InstanceDescriptor(SimpleDescriptor):
@@ -1421,10 +1442,19 @@ class BaseDocWriter(object):
self._addAxis(axisObject)
if self.documentObject.axisMappings:
- mappingsElement = ET.Element("mappings")
- self.root.findall(".axes")[0].append(mappingsElement)
+ mappingsElement = None
+ lastGroup = object()
for mappingObject in self.documentObject.axisMappings:
+ if getattr(mappingObject, "groupDescription", None) != lastGroup:
+ if mappingsElement is not None:
+ self.root.findall(".axes")[0].append(mappingsElement)
+ lastGroup = getattr(mappingObject, "groupDescription", None)
+ mappingsElement = ET.Element("mappings")
+ if lastGroup is not None:
+ mappingsElement.attrib["description"] = lastGroup
self._addAxisMapping(mappingsElement, mappingObject)
+ if mappingsElement is not None:
+ self.root.findall(".axes")[0].append(mappingsElement)
if self.documentObject.locationLabels:
labelsElement = ET.Element("labels")
@@ -1586,6 +1616,8 @@ class BaseDocWriter(object):
def _addAxisMapping(self, mappingsElement, mappingObject):
mappingElement = ET.Element("mapping")
+ if getattr(mappingObject, "description", None) is not None:
+ mappingElement.attrib["description"] = mappingObject.description
for what in ("inputLocation", "outputLocation"):
whatObject = getattr(mappingObject, what, None)
if whatObject is None:
@@ -2081,10 +2113,11 @@ class BaseDocReader(LogMixin):
self.documentObject.axes.append(axisObject)
self.axisDefaults[axisObject.name] = axisObject.default
- mappingsElement = self.root.find(".axes/mappings")
self.documentObject.axisMappings = []
- if mappingsElement is not None:
+ for mappingsElement in self.root.findall(".axes/mappings"):
+ groupDescription = mappingsElement.attrib.get("description")
for mappingElement in mappingsElement.findall("mapping"):
+ description = mappingElement.attrib.get("description")
inputElement = mappingElement.find("input")
outputElement = mappingElement.find("output")
inputLoc = {}
@@ -2098,7 +2131,10 @@ class BaseDocReader(LogMixin):
value = float(dimElement.attrib["xvalue"])
outputLoc[name] = value
axisMappingObject = self.axisMappingDescriptorClass(
- inputLocation=inputLoc, outputLocation=outputLoc
+ inputLocation=inputLoc,
+ outputLocation=outputLoc,
+ description=description,
+ groupDescription=groupDescription,
)
self.documentObject.axisMappings.append(axisMappingObject)
@@ -3279,3 +3315,23 @@ class DesignSpaceDocument(LogMixin, AsDictMixin):
finally:
for source, font in zip(self.sources, fonts):
source.font = font
+
+
+def main(args=None):
+ """Roundtrip .designspace file through the DesignSpaceDocument class"""
+
+ if args is None:
+ import sys
+
+ args = sys.argv[1:]
+
+ from argparse import ArgumentParser
+
+ parser = ArgumentParser(prog="designspaceLib", description=main.__doc__)
+ parser.add_argument("input")
+ parser.add_argument("output")
+
+ options = parser.parse_args(args)
+
+ ds = DesignSpaceDocument.fromfile(options.input)
+ ds.write(options.output)
diff --git a/contrib/python/fonttools/fontTools/designspaceLib/__main__.py b/contrib/python/fonttools/fontTools/designspaceLib/__main__.py
new file mode 100644
index 0000000000..8f5e44ea9e
--- /dev/null
+++ b/contrib/python/fonttools/fontTools/designspaceLib/__main__.py
@@ -0,0 +1,6 @@
+import sys
+from fontTools.designspaceLib import main
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/contrib/python/fonttools/fontTools/feaLib/builder.py b/contrib/python/fonttools/fontTools/feaLib/builder.py
index 36eed95148..7921a3f179 100644
--- a/contrib/python/fonttools/fontTools/feaLib/builder.py
+++ b/contrib/python/fonttools/fontTools/feaLib/builder.py
@@ -285,7 +285,11 @@ class Builder(object):
def build_feature_aalt_(self):
if not self.aalt_features_ and not self.aalt_alternates_:
return
- alternates = {g: set(a) for g, a in self.aalt_alternates_.items()}
+ # > alternate glyphs will be sorted in the order that the source features
+ # > are named in the aalt definition, not the order of the feature definitions
+ # > in the file. Alternates defined explicitly ... will precede all others.
+ # https://github.com/fonttools/fonttools/issues/836
+ alternates = {g: list(a) for g, a in self.aalt_alternates_.items()}
for location, name in self.aalt_features_ + [(None, "aalt")]:
feature = [
(script, lang, feature, lookups)
@@ -302,17 +306,14 @@ class Builder(object):
lookuplist = [lookuplist]
for lookup in lookuplist:
for glyph, alts in lookup.getAlternateGlyphs().items():
- alternates.setdefault(glyph, set()).update(alts)
+ alts_for_glyph = alternates.setdefault(glyph, [])
+ alts_for_glyph.extend(
+ g for g in alts if g not in alts_for_glyph
+ )
single = {
- glyph: list(repl)[0] for glyph, repl in alternates.items() if len(repl) == 1
- }
- # TODO: Figure out the glyph alternate ordering used by makeotf.
- # https://github.com/fonttools/fonttools/issues/836
- multi = {
- glyph: sorted(repl, key=self.font.getGlyphID)
- for glyph, repl in alternates.items()
- if len(repl) > 1
+ glyph: repl[0] for glyph, repl in alternates.items() if len(repl) == 1
}
+ multi = {glyph: repl for glyph, repl in alternates.items() if len(repl) > 1}
if not single and not multi:
return
self.features_ = {
@@ -1249,8 +1250,9 @@ class Builder(object):
def add_single_subst(self, location, prefix, suffix, mapping, forceChain):
if self.cur_feature_name_ == "aalt":
for from_glyph, to_glyph in mapping.items():
- alts = self.aalt_alternates_.setdefault(from_glyph, set())
- alts.add(to_glyph)
+ alts = self.aalt_alternates_.setdefault(from_glyph, [])
+ if to_glyph not in alts:
+ alts.append(to_glyph)
return
if prefix or suffix or forceChain:
self.add_single_subst_chained_(location, prefix, suffix, mapping)
@@ -1303,8 +1305,8 @@ class Builder(object):
# GSUB 3
def add_alternate_subst(self, location, prefix, glyph, suffix, replacement):
if self.cur_feature_name_ == "aalt":
- alts = self.aalt_alternates_.setdefault(glyph, set())
- alts.update(replacement)
+ alts = self.aalt_alternates_.setdefault(glyph, [])
+ alts.extend(g for g in replacement if g not in alts)
return
if prefix or suffix:
chain = self.get_lookup_(location, ChainContextSubstBuilder)
@@ -1338,7 +1340,7 @@ class Builder(object):
# substitutions to be specified on target sequences that contain
# glyph classes, the implementation software will enumerate
# all specific glyph sequences if glyph classes are detected"
- for g in sorted(itertools.product(*glyphs)):
+ for g in itertools.product(*glyphs):
lookup.ligatures[g] = replacement
# GSUB 5/6
diff --git a/contrib/python/fonttools/fontTools/feaLib/lexer.py b/contrib/python/fonttools/fontTools/feaLib/lexer.py
index e0ae0aefee..5867f70b38 100644
--- a/contrib/python/fonttools/fontTools/feaLib/lexer.py
+++ b/contrib/python/fonttools/fontTools/feaLib/lexer.py
@@ -111,10 +111,6 @@ class Lexer(object):
glyphclass = text[start + 1 : self.pos_]
if len(glyphclass) < 1:
raise FeatureLibError("Expected glyph class name", location)
- if len(glyphclass) > 63:
- raise FeatureLibError(
- "Glyph class names must not be longer than 63 characters", location
- )
if not Lexer.RE_GLYPHCLASS.match(glyphclass):
raise FeatureLibError(
"Glyph class names must consist of letters, digits, "
diff --git a/contrib/python/fonttools/fontTools/feaLib/parser.py b/contrib/python/fonttools/fontTools/feaLib/parser.py
index 8ffdf644c3..8cbe79592b 100644
--- a/contrib/python/fonttools/fontTools/feaLib/parser.py
+++ b/contrib/python/fonttools/fontTools/feaLib/parser.py
@@ -2071,13 +2071,7 @@ class Parser(object):
def expect_glyph_(self):
self.advance_lexer_()
if self.cur_token_type_ is Lexer.NAME:
- self.cur_token_ = self.cur_token_.lstrip("\\")
- if len(self.cur_token_) > 63:
- raise FeatureLibError(
- "Glyph names must not be longer than 63 characters",
- self.cur_token_location_,
- )
- return self.cur_token_
+ return self.cur_token_.lstrip("\\")
elif self.cur_token_type_ is Lexer.CID:
return "cid%05d" % self.cur_token_
raise FeatureLibError("Expected a glyph name or CID", self.cur_token_location_)
diff --git a/contrib/python/fonttools/fontTools/otlLib/builder.py b/contrib/python/fonttools/fontTools/otlLib/builder.py
index 4b457f4d9f..c8b14fc666 100644
--- a/contrib/python/fonttools/fontTools/otlLib/builder.py
+++ b/contrib/python/fonttools/fontTools/otlLib/builder.py
@@ -6,6 +6,7 @@ from fontTools.ttLib.tables import otTables as ot
from fontTools.ttLib.tables.otBase import (
ValueRecord,
valueRecordFormatDict,
+ OTLOffsetOverflowError,
OTTableWriter,
CountReference,
)
@@ -350,16 +351,14 @@ class ChainContextualBuilder(LookupBuilder):
return [x for x in ruleset if len(x.rules) > 0]
def getCompiledSize_(self, subtables):
- size = 0
- for st in subtables:
- w = OTTableWriter()
- w["LookupType"] = CountReference(
- {"LookupType": st.LookupType}, "LookupType"
- )
- # We need to make a copy here because compiling
- # modifies the subtable (finalizing formats etc.)
- copy.deepcopy(st).compile(w, self.font)
- size += len(w.getAllData())
+ if not subtables:
+ return 0
+ # We need to make a copy here because compiling
+ # modifies the subtable (finalizing formats etc.)
+ table = self.buildLookup_(copy.deepcopy(subtables))
+ w = OTTableWriter()
+ table.compile(w, self.font)
+ size = len(w.getAllData())
return size
def build(self):
@@ -410,22 +409,23 @@ class ChainContextualBuilder(LookupBuilder):
if not ruleset.hasAnyGlyphClasses:
candidates[1] = [self.buildFormat1Subtable(ruleset, chaining)]
+ candidates_by_size = []
for i in [1, 2, 3]:
if candidates[i]:
try:
- self.getCompiledSize_(candidates[i])
- except Exception as e:
+ size = self.getCompiledSize_(candidates[i])
+ except OTLOffsetOverflowError as e:
log.warning(
"Contextual format %i at %s overflowed (%s)"
% (i, str(self.location), e)
)
- candidates[i] = None
+ else:
+ candidates_by_size.append((size, candidates[i]))
- candidates = [x for x in candidates if x is not None]
- if not candidates:
+ if not candidates_by_size:
raise OpenTypeLibError("All candidates overflowed", self.location)
- winner = min(candidates, key=self.getCompiledSize_)
+ _min_size, winner = min(candidates_by_size, key=lambda x: x[0])
subtables.extend(winner)
# If we are not chaining, lookup type will be automatically fixed by
@@ -774,7 +774,10 @@ class ChainContextSubstBuilder(ChainContextualBuilder):
if lookup is not None:
alts = lookup.getAlternateGlyphs()
for glyph, replacements in alts.items():
- result.setdefault(glyph, set()).update(replacements)
+ alts_for_glyph = result.setdefault(glyph, [])
+ alts_for_glyph.extend(
+ g for g in replacements if g not in alts_for_glyph
+ )
return result
def find_chainable_single_subst(self, mapping):
@@ -1238,7 +1241,7 @@ class SingleSubstBuilder(LookupBuilder):
return self.buildLookup_(subtables)
def getAlternateGlyphs(self):
- return {glyph: set([repl]) for glyph, repl in self.mapping.items()}
+ return {glyph: [repl] for glyph, repl in self.mapping.items()}
def add_subtable_break(self, location):
self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
@@ -1567,19 +1570,6 @@ def buildAlternateSubstSubtable(mapping):
return self
-def _getLigatureKey(components):
- # Computes a key for ordering ligatures in a GSUB Type-4 lookup.
-
- # When building the OpenType lookup, we need to make sure that
- # the longest sequence of components is listed first, so we
- # use the negative length as the primary key for sorting.
- # To make buildLigatureSubstSubtable() deterministic, we use the
- # component sequence as the secondary key.
-
- # For example, this will sort (f,f,f) < (f,f,i) < (f,f) < (f,i) < (f,l).
- return (-len(components), components)
-
-
def buildLigatureSubstSubtable(mapping):
"""Builds a ligature substitution (GSUB4) subtable.
@@ -1613,7 +1603,7 @@ def buildLigatureSubstSubtable(mapping):
# with fontTools >= 3.1:
# self.ligatures = dict(mapping)
self.ligatures = {}
- for components in sorted(mapping.keys(), key=_getLigatureKey):
+ for components in sorted(mapping.keys(), key=self._getLigatureSortKey):
ligature = ot.Ligature()
ligature.Component = components[1:]
ligature.CompCount = len(ligature.Component) + 1
diff --git a/contrib/python/fonttools/fontTools/pens/hashPointPen.py b/contrib/python/fonttools/fontTools/pens/hashPointPen.py
index b82468ec9c..f15dcabbfd 100644
--- a/contrib/python/fonttools/fontTools/pens/hashPointPen.py
+++ b/contrib/python/fonttools/fontTools/pens/hashPointPen.py
@@ -31,6 +31,20 @@ class HashPointPen(AbstractPointPen):
> # The hash values are identical, the outline has not changed.
> # Compile the hinting code ...
> pass
+
+ If you want to compare a glyph from a source format which supports floating point
+ coordinates and transformations against a glyph from a format which has restrictions
+ on the precision of floats, e.g. UFO vs. TTF, you must use an appropriate rounding
+ function to make the values comparable. For TTF fonts with composites, this
+ construct can be used to make the transform values conform to F2Dot14:
+
+ > ttf_hash_pen = HashPointPen(ttf_glyph_width, ttFont.getGlyphSet())
+ > ttf_round_pen = RoundingPointPen(ttf_hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14))
+ > ufo_hash_pen = HashPointPen(ufo_glyph.width, ufo)
+ > ttf_glyph.drawPoints(ttf_round_pen, ttFont["glyf"])
+ > ufo_round_pen = RoundingPointPen(ufo_hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14))
+ > ufo_glyph.drawPoints(ufo_round_pen)
+ > assert ttf_hash_pen.hash == ufo_hash_pen.hash
"""
def __init__(self, glyphWidth=0, glyphSet=None):
diff --git a/contrib/python/fonttools/fontTools/pens/roundingPen.py b/contrib/python/fonttools/fontTools/pens/roundingPen.py
index 2a7c476c36..176bcc7a55 100644
--- a/contrib/python/fonttools/fontTools/pens/roundingPen.py
+++ b/contrib/python/fonttools/fontTools/pens/roundingPen.py
@@ -1,4 +1,4 @@
-from fontTools.misc.roundTools import otRound
+from fontTools.misc.roundTools import noRound, otRound
from fontTools.misc.transform import Transform
from fontTools.pens.filterPen import FilterPen, FilterPointPen
@@ -8,7 +8,9 @@ __all__ = ["RoundingPen", "RoundingPointPen"]
class RoundingPen(FilterPen):
"""
- Filter pen that rounds point coordinates and component XY offsets to integer.
+ Filter pen that rounds point coordinates and component XY offsets to integer. For
+ rounding the component transform values, a separate round function can be passed to
+ the pen.
>>> from fontTools.pens.recordingPen import RecordingPen
>>> recpen = RecordingPen()
@@ -28,9 +30,10 @@ class RoundingPen(FilterPen):
True
"""
- def __init__(self, outPen, roundFunc=otRound):
+ def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
super().__init__(outPen)
self.roundFunc = roundFunc
+ self.transformRoundFunc = transformRoundFunc
def moveTo(self, pt):
self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
@@ -49,12 +52,16 @@ class RoundingPen(FilterPen):
)
def addComponent(self, glyphName, transformation):
+ xx, xy, yx, yy, dx, dy = transformation
self._outPen.addComponent(
glyphName,
Transform(
- *transformation[:4],
- self.roundFunc(transformation[4]),
- self.roundFunc(transformation[5]),
+ self.transformRoundFunc(xx),
+ self.transformRoundFunc(xy),
+ self.transformRoundFunc(yx),
+ self.transformRoundFunc(yy),
+ self.roundFunc(dx),
+ self.roundFunc(dy),
),
)
@@ -62,6 +69,8 @@ class RoundingPen(FilterPen):
class RoundingPointPen(FilterPointPen):
"""
Filter point pen that rounds point coordinates and component XY offsets to integer.
+ For rounding the component scale values, a separate round function can be passed to
+ the pen.
>>> from fontTools.pens.recordingPen import RecordingPointPen
>>> recpen = RecordingPointPen()
@@ -87,26 +96,35 @@ class RoundingPointPen(FilterPointPen):
True
"""
- def __init__(self, outPen, roundFunc=otRound):
+ def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
super().__init__(outPen)
self.roundFunc = roundFunc
+ self.transformRoundFunc = transformRoundFunc
- def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
+ def addPoint(
+ self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
+ ):
self._outPen.addPoint(
(self.roundFunc(pt[0]), self.roundFunc(pt[1])),
segmentType=segmentType,
smooth=smooth,
name=name,
+ identifier=identifier,
**kwargs,
)
- def addComponent(self, baseGlyphName, transformation, **kwargs):
+ def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
+ xx, xy, yx, yy, dx, dy = transformation
self._outPen.addComponent(
- baseGlyphName,
- Transform(
- *transformation[:4],
- self.roundFunc(transformation[4]),
- self.roundFunc(transformation[5]),
+ baseGlyphName=baseGlyphName,
+ transformation=Transform(
+ self.transformRoundFunc(xx),
+ self.transformRoundFunc(xy),
+ self.transformRoundFunc(yx),
+ self.transformRoundFunc(yy),
+ self.roundFunc(dx),
+ self.roundFunc(dy),
),
+ identifier=identifier,
**kwargs,
)
diff --git a/contrib/python/fonttools/fontTools/pens/svgPathPen.py b/contrib/python/fonttools/fontTools/pens/svgPathPen.py
index 53b3683f2d..29d41a8029 100644
--- a/contrib/python/fonttools/fontTools/pens/svgPathPen.py
+++ b/contrib/python/fonttools/fontTools/pens/svgPathPen.py
@@ -220,13 +220,19 @@ def main(args=None):
"fonttools pens.svgPathPen", description="Generate SVG from text"
)
parser.add_argument("font", metavar="font.ttf", help="Font file.")
- parser.add_argument("text", metavar="text", help="Text string.")
+ parser.add_argument("text", metavar="text", nargs="?", help="Text string.")
parser.add_argument(
"-y",
metavar="<number>",
help="Face index into a collection to open. Zero based.",
)
parser.add_argument(
+ "--glyphs",
+ metavar="whitespace-separated list of glyph names",
+ type=str,
+ help="Glyphs to show. Exclusive with text option",
+ )
+ parser.add_argument(
"--variations",
metavar="AXIS=LOC",
default="",
@@ -241,6 +247,7 @@ def main(args=None):
font = TTFont(options.font, fontNumber=fontNumber)
text = options.text
+ glyphs = options.glyphs
location = {}
for tag_v in options.variations.split():
@@ -255,10 +262,17 @@ def main(args=None):
glyphset = font.getGlyphSet(location=location)
cmap = font["cmap"].getBestCmap()
+ if glyphs is not None and text is not None:
+ raise ValueError("Options --glyphs and --text are exclusive")
+
+ if glyphs is None:
+ glyphs = " ".join(cmap[ord(u)] for u in text)
+
+ glyphs = glyphs.split()
+
s = ""
width = 0
- for u in text:
- g = cmap[ord(u)]
+ for g in glyphs:
glyph = glyphset[g]
pen = SVGPathPen(glyphset)
diff --git a/contrib/python/fonttools/fontTools/subset/__init__.py b/contrib/python/fonttools/fontTools/subset/__init__.py
index 9b1758435c..250a07ef1a 100644
--- a/contrib/python/fonttools/fontTools/subset/__init__.py
+++ b/contrib/python/fonttools/fontTools/subset/__init__.py
@@ -3733,6 +3733,3 @@ __all__ = [
"parse_unicodes",
"main",
]
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py b/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py
index 262f8d4187..3505f42337 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py
@@ -1123,6 +1123,35 @@ class LigatureSubst(FormatSwitchingBaseTable):
self.ligatures = ligatures
del self.Format # Don't need this anymore
+ @staticmethod
+ def _getLigatureSortKey(components):
+ # Computes a key for ordering ligatures in a GSUB Type-4 lookup.
+
+ # When building the OpenType lookup, we need to make sure that
+ # the longest sequence of components is listed first, so we
+ # use the negative length as the key for sorting.
+ # Note, we no longer need to worry about deterministic order because the
+ # ligature mapping `dict` remembers the insertion order, and this in
+ # turn depends on the order in which the ligatures are written in the FEA.
+ # Since python sort algorithm is stable, the ligatures of equal length
+ # will keep the relative order in which they appear in the feature file.
+ # For example, given the following ligatures (all starting with 'f' and
+ # thus belonging to the same LigatureSet):
+ #
+ # feature liga {
+ # sub f i by f_i;
+ # sub f f f by f_f_f;
+ # sub f f by f_f;
+ # sub f f i by f_f_i;
+ # } liga;
+ #
+ # this should sort to: f_f_f, f_f_i, f_i, f_f
+ # This is also what fea-rs does, see:
+ # https://github.com/adobe-type-tools/afdko/issues/1727
+ # https://github.com/fonttools/fonttools/issues/3428
+ # https://github.com/googlefonts/fontc/pull/680
+ return -len(components)
+
def preWrite(self, font):
self.Format = 1
ligatures = getattr(self, "ligatures", None)
@@ -1135,13 +1164,11 @@ class LigatureSubst(FormatSwitchingBaseTable):
# ligatures is map from components-sequence to lig-glyph
newLigatures = dict()
- for comps, lig in sorted(
- ligatures.items(), key=lambda item: (-len(item[0]), item[0])
- ):
+ for comps in sorted(ligatures.keys(), key=self._getLigatureSortKey):
ligature = Ligature()
ligature.Component = comps[1:]
ligature.CompCount = len(comps)
- ligature.LigGlyph = lig
+ ligature.LigGlyph = ligatures[comps]
newLigatures.setdefault(comps[0], []).append(ligature)
ligatures = newLigatures
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/sbixGlyph.py b/contrib/python/fonttools/fontTools/ttLib/tables/sbixGlyph.py
index fd687a1880..b744a2a3bc 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/sbixGlyph.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/sbixGlyph.py
@@ -54,6 +54,10 @@ class Glyph(object):
# pad with spaces
self.graphicType += " "[: (4 - len(self.graphicType))]
+ def is_reference_type(self):
+ """Returns True if this glyph is a reference to another glyph's image data."""
+ return self.graphicType == "dupe" or self.graphicType == "flip"
+
def decompile(self, ttFont):
self.glyphName = ttFont.getGlyphName(self.gid)
if self.rawdata is None:
@@ -71,7 +75,7 @@ class Glyph(object):
sbixGlyphHeaderFormat, self.rawdata[:sbixGlyphHeaderFormatSize], self
)
- if self.graphicType == "dupe":
+ if self.is_reference_type():
# this glyph is a reference to another glyph's image data
(gid,) = struct.unpack(">H", self.rawdata[sbixGlyphHeaderFormatSize:])
self.referenceGlyphName = ttFont.getGlyphName(gid)
@@ -94,7 +98,7 @@ class Glyph(object):
rawdata = b""
else:
rawdata = sstruct.pack(sbixGlyphHeaderFormat, self)
- if self.graphicType == "dupe":
+ if self.is_reference_type():
rawdata += struct.pack(">H", ttFont.getGlyphID(self.referenceGlyphName))
else:
assert self.imageData is not None
@@ -117,8 +121,8 @@ class Glyph(object):
originOffsetY=self.originOffsetY,
)
xmlWriter.newline()
- if self.graphicType == "dupe":
- # graphicType == "dupe" is a reference to another glyph id.
+ if self.is_reference_type():
+ # this glyph is a reference to another glyph id.
xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
else:
xmlWriter.begintag("hexdata")
@@ -131,7 +135,7 @@ class Glyph(object):
def fromXML(self, name, attrs, content, ttFont):
if name == "ref":
- # glyph is a "dupe", i.e. a reference to another glyph's image data.
+ # this glyph i.e. a reference to another glyph's image data.
# in this case imageData contains the glyph id of the reference glyph
# get glyph id from glyphname
glyphname = safeEval("'''" + attrs["glyphname"] + "'''")
diff --git a/contrib/python/fonttools/fontTools/varLib/__init__.py b/contrib/python/fonttools/fontTools/varLib/__init__.py
index 46834f6433..b9af6dacbf 100644
--- a/contrib/python/fonttools/fontTools/varLib/__init__.py
+++ b/contrib/python/fonttools/fontTools/varLib/__init__.py
@@ -216,8 +216,6 @@ def _add_avar(font, axes, mappings, axisTags):
if mappings:
interesting = True
- hiddenAxes = [axis for axis in axes.values() if axis.hidden]
-
inputLocations = [
{
axes[name].tag: models.normalizeValue(v, vals_triples[axes[name].tag])
@@ -752,10 +750,14 @@ def _add_BASE(font, masterModel, master_ttfs, axisTags):
def _merge_OTL(font, model, master_fonts, axisTags):
+ otl_tags = ["GSUB", "GDEF", "GPOS"]
+ if not any(tag in font for tag in otl_tags):
+ return
+
log.info("Merging OpenType Layout tables")
merger = VariationMerger(model, axisTags, font)
- merger.mergeTables(font, master_fonts, ["GSUB", "GDEF", "GPOS"])
+ merger.mergeTables(font, master_fonts, otl_tags)
store = merger.store_builder.finish()
if not store:
return
diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
index d1cde0df7a..df55f0414d 100644
--- a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
+++ b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
@@ -614,7 +614,7 @@ def _instantiateGvarGlyph(
if optimize:
isComposite = glyf[glyphname].isComposite()
for var in tupleVarStore:
- var.optimize(coordinates, endPts, isComposite)
+ var.optimize(coordinates, endPts, isComposite=isComposite)
def instantiateGvarGlyph(varfont, glyphname, axisLimits, optimize=True):
diff --git a/contrib/python/fonttools/ya.make b/contrib/python/fonttools/ya.make
index 4d380b556c..35b91fddb1 100644
--- a/contrib/python/fonttools/ya.make
+++ b/contrib/python/fonttools/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(4.47.2)
+VERSION(4.48.1)
LICENSE(MIT)
@@ -41,6 +41,7 @@ PY_SRCS(
fontTools/cu2qu/errors.py
fontTools/cu2qu/ufo.py
fontTools/designspaceLib/__init__.py
+ fontTools/designspaceLib/__main__.py
fontTools/designspaceLib/split.py
fontTools/designspaceLib/statNames.py
fontTools/designspaceLib/types.py