Merge branch 'fonttools:main' into ttf2otf
This commit is contained in:
commit
9f7025af8f
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
@ -118,7 +118,7 @@ jobs:
|
|||||||
# so that all artifacts are downloaded in the same directory specified by 'path'
|
# so that all artifacts are downloaded in the same directory specified by 'path'
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: dist
|
path: dist
|
||||||
- uses: pypa/gh-action-pypi-publish@v1.9.0
|
- uses: pypa/gh-action-pypi-publish@v1.10.1
|
||||||
with:
|
with:
|
||||||
user: __token__
|
user: __token__
|
||||||
password: ${{ secrets.PYPI_PASSWORD }}
|
password: ${{ secrets.PYPI_PASSWORD }}
|
||||||
|
@ -3,3 +3,4 @@ colorLib.builder: Build COLR/CPAL tables from scratch
|
|||||||
#####################################################
|
#####################################################
|
||||||
|
|
||||||
.. automodule:: fontTools.colorLib.builder
|
.. automodule:: fontTools.colorLib.builder
|
||||||
|
:no-inherited-members:
|
||||||
|
@ -31,16 +31,20 @@ needs_sphinx = "1.3"
|
|||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
|
"sphinx.ext.napoleon",
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinx.ext.viewcode",
|
"sphinx.ext.viewcode",
|
||||||
"sphinx.ext.napoleon",
|
|
||||||
"sphinx.ext.coverage",
|
"sphinx.ext.coverage",
|
||||||
"sphinx.ext.autosectionlabel",
|
"sphinx.ext.autosectionlabel",
|
||||||
]
|
]
|
||||||
|
|
||||||
autodoc_mock_imports = ["gtk", "reportlab"]
|
autodoc_mock_imports = ["gtk", "reportlab"]
|
||||||
|
|
||||||
autodoc_default_options = {"members": True, "inherited-members": True}
|
autodoc_default_options = {
|
||||||
|
"members": True,
|
||||||
|
"inherited-members": True,
|
||||||
|
"show-inheritance": True,
|
||||||
|
}
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["_templates"]
|
templates_path = ["_templates"]
|
||||||
@ -78,7 +82,7 @@ release = "4.0"
|
|||||||
#
|
#
|
||||||
# This is also used if you do content translation via gettext catalogs.
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
# Usually you set "language" from the command line for these cases.
|
# Usually you set "language" from the command line for these cases.
|
||||||
language = None
|
language = "en"
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
|
@ -297,7 +297,7 @@ Example of all axis elements together
|
|||||||
|
|
||||||
|
|
||||||
``<output>`` element
|
``<output>`` element
|
||||||
...................
|
....................
|
||||||
|
|
||||||
- Defines the output location of an axis mapping.
|
- Defines the output location of an axis mapping.
|
||||||
- Child element of ``<mapping>``
|
- Child element of ``<mapping>``
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
:orphan:
|
:orphan:
|
||||||
|
|
||||||
.. _developerinfo:
|
.. _developerinfo:
|
||||||
.. image:: ../../Icons/FontToolsIconGreenCircle.png
|
.. image:: ../../Icons/FontToolsIconGreenCircle.png
|
||||||
:width: 200px
|
:width: 200px
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
|
|
||||||
---fontTools Documentation---
|
---fontTools Documentation---
|
||||||
=======
|
=============================
|
||||||
About
|
About
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -14,4 +14,4 @@ ttFont: Read/write OpenType and TrueType fonts
|
|||||||
|
|
||||||
.. automodule:: fontTools.ttLib.ttFont
|
.. automodule:: fontTools.ttLib.ttFont
|
||||||
:members: getTableModule, registerCustomTableClass, unregisterCustomTableClass, getCustomTableClass, getClassTag, newTable, tagToIdentifier, identifierToTag, tagToXML, xmlToTag, sortedTagList, reorderFontTables
|
:members: getTableModule, registerCustomTableClass, unregisterCustomTableClass, getCustomTableClass, getClassTag, newTable, tagToIdentifier, identifierToTag, tagToXML, xmlToTag, sortedTagList, reorderFontTables
|
||||||
|
:exclude-members: TTFont, GlyphOrder
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
"""
|
||||||
|
designSpaceDocument
|
||||||
|
|
||||||
|
- Read and write designspace files
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
@ -15,11 +21,6 @@ from fontTools.misc import plistlib
|
|||||||
from fontTools.misc.loggingTools import LogMixin
|
from fontTools.misc.loggingTools import LogMixin
|
||||||
from fontTools.misc.textTools import tobytes, tostr
|
from fontTools.misc.textTools import tobytes, tostr
|
||||||
|
|
||||||
"""
|
|
||||||
designSpaceDocument
|
|
||||||
|
|
||||||
- read and write designspace files
|
|
||||||
"""
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AxisDescriptor",
|
"AxisDescriptor",
|
||||||
|
@ -122,15 +122,16 @@ Other options
|
|||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
For the other options listed below, to see the current value of the option,
|
For the other options listed below, to see the current value of the option,
|
||||||
pass a value of '?' to it, with or without a '='.
|
pass a value of '?' to it, with or without a '='. In some environments,
|
||||||
|
you might need to escape the question mark, like this: '--glyph-names\?'.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
$ pyftsubset --glyph-names?
|
$ pyftsubset --glyph-names?
|
||||||
Current setting for 'glyph-names' is: False
|
Current setting for 'glyph-names' is: False
|
||||||
$ ./pyftsubset --name-IDs=?
|
$ pyftsubset --name-IDs=?
|
||||||
Current setting for 'name-IDs' is: [0, 1, 2, 3, 4, 5, 6]
|
Current setting for 'name-IDs' is: [0, 1, 2, 3, 4, 5, 6]
|
||||||
$ ./pyftsubset --hinting? --no-hinting --hinting?
|
$ pyftsubset --hinting? --no-hinting --hinting?
|
||||||
Current setting for 'hinting' is: True
|
Current setting for 'hinting' is: True
|
||||||
Current setting for 'hinting' is: False
|
Current setting for 'hinting' is: False
|
||||||
|
|
||||||
|
@ -1175,17 +1175,8 @@ class NameRecordVisitor(TTVisitor):
|
|||||||
|
|
||||||
@NameRecordVisitor.register_attrs(
|
@NameRecordVisitor.register_attrs(
|
||||||
(
|
(
|
||||||
(otTables.FeatureParamsSize, ("SubfamilyID", "SubfamilyNameID")),
|
(otTables.FeatureParamsSize, ("SubfamilyNameID",)),
|
||||||
(otTables.FeatureParamsStylisticSet, ("UINameID",)),
|
(otTables.FeatureParamsStylisticSet, ("UINameID",)),
|
||||||
(
|
|
||||||
otTables.FeatureParamsCharacterVariants,
|
|
||||||
(
|
|
||||||
"FeatUILabelNameID",
|
|
||||||
"FeatUITooltipTextNameID",
|
|
||||||
"SampleTextNameID",
|
|
||||||
"FirstParamUILabelNameID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(otTables.STAT, ("ElidedFallbackNameID",)),
|
(otTables.STAT, ("ElidedFallbackNameID",)),
|
||||||
(otTables.AxisRecord, ("AxisNameID",)),
|
(otTables.AxisRecord, ("AxisNameID",)),
|
||||||
(otTables.AxisValue, ("ValueNameID",)),
|
(otTables.AxisValue, ("ValueNameID",)),
|
||||||
@ -1197,6 +1188,22 @@ def visit(visitor, obj, attr, value):
|
|||||||
visitor.seen.add(value)
|
visitor.seen.add(value)
|
||||||
|
|
||||||
|
|
||||||
|
@NameRecordVisitor.register(otTables.FeatureParamsCharacterVariants)
|
||||||
|
def visit(visitor, obj):
|
||||||
|
for attr in ("FeatUILabelNameID", "FeatUITooltipTextNameID", "SampleTextNameID"):
|
||||||
|
value = getattr(obj, attr)
|
||||||
|
visitor.seen.add(value)
|
||||||
|
# also include the sequence of UI strings for individual variants, if any
|
||||||
|
if obj.FirstParamUILabelNameID == 0 or obj.NumNamedParameters == 0:
|
||||||
|
return
|
||||||
|
visitor.seen.update(
|
||||||
|
range(
|
||||||
|
obj.FirstParamUILabelNameID,
|
||||||
|
obj.FirstParamUILabelNameID + obj.NumNamedParameters,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@NameRecordVisitor.register(ttLib.getTableClass("fvar"))
|
@NameRecordVisitor.register(ttLib.getTableClass("fvar"))
|
||||||
def visit(visitor, obj):
|
def visit(visitor, obj):
|
||||||
for inst in obj.instances:
|
for inst in obj.instances:
|
||||||
|
@ -1,26 +1,3 @@
|
|||||||
import os
|
|
||||||
from copy import deepcopy
|
|
||||||
from os import fsdecode
|
|
||||||
import logging
|
|
||||||
import zipfile
|
|
||||||
import enum
|
|
||||||
from collections import OrderedDict
|
|
||||||
import fs
|
|
||||||
import fs.base
|
|
||||||
import fs.subfs
|
|
||||||
import fs.errors
|
|
||||||
import fs.copy
|
|
||||||
import fs.osfs
|
|
||||||
import fs.zipfs
|
|
||||||
import fs.tempfs
|
|
||||||
import fs.tools
|
|
||||||
from fontTools.misc import plistlib
|
|
||||||
from fontTools.ufoLib.validators import *
|
|
||||||
from fontTools.ufoLib.filenames import userNameToFileName
|
|
||||||
from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
|
|
||||||
from fontTools.ufoLib.errors import UFOLibError
|
|
||||||
from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A library for importing .ufo files and their descendants.
|
A library for importing .ufo files and their descendants.
|
||||||
Refer to http://unifiedfontobject.com for the UFO specification.
|
Refer to http://unifiedfontobject.com for the UFO specification.
|
||||||
@ -51,6 +28,29 @@ fontinfo.plist values between the possible format versions.
|
|||||||
convertFontInfoValueForAttributeFromVersion3ToVersion2
|
convertFontInfoValueForAttributeFromVersion3ToVersion2
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from copy import deepcopy
|
||||||
|
from os import fsdecode
|
||||||
|
import logging
|
||||||
|
import zipfile
|
||||||
|
import enum
|
||||||
|
from collections import OrderedDict
|
||||||
|
import fs
|
||||||
|
import fs.base
|
||||||
|
import fs.subfs
|
||||||
|
import fs.errors
|
||||||
|
import fs.copy
|
||||||
|
import fs.osfs
|
||||||
|
import fs.zipfs
|
||||||
|
import fs.tempfs
|
||||||
|
import fs.tools
|
||||||
|
from fontTools.misc import plistlib
|
||||||
|
from fontTools.ufoLib.validators import *
|
||||||
|
from fontTools.ufoLib.filenames import userNameToFileName
|
||||||
|
from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
|
||||||
|
from fontTools.ufoLib.errors import UFOLibError
|
||||||
|
from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"makeUFOPath",
|
"makeUFOPath",
|
||||||
"UFOLibError",
|
"UFOLibError",
|
||||||
|
@ -869,7 +869,7 @@ def _add_COLR(font, model, master_fonts, axisTags, colr_layer_reuse=True):
|
|||||||
colr.VarIndexMap = builder.buildDeltaSetIndexMap(varIdxes)
|
colr.VarIndexMap = builder.buildDeltaSetIndexMap(varIdxes)
|
||||||
|
|
||||||
|
|
||||||
def load_designspace(designspace, log_enabled=True):
|
def load_designspace(designspace, log_enabled=True, *, require_sources=True):
|
||||||
# TODO: remove this and always assume 'designspace' is a DesignSpaceDocument,
|
# TODO: remove this and always assume 'designspace' is a DesignSpaceDocument,
|
||||||
# never a file path, as that's already handled by caller
|
# never a file path, as that's already handled by caller
|
||||||
if hasattr(designspace, "sources"): # Assume a DesignspaceDocument
|
if hasattr(designspace, "sources"): # Assume a DesignspaceDocument
|
||||||
@ -878,7 +878,7 @@ def load_designspace(designspace, log_enabled=True):
|
|||||||
ds = DesignSpaceDocument.fromfile(designspace)
|
ds = DesignSpaceDocument.fromfile(designspace)
|
||||||
|
|
||||||
masters = ds.sources
|
masters = ds.sources
|
||||||
if not masters:
|
if require_sources and not masters:
|
||||||
raise VarLibValidationError("Designspace must have at least one source.")
|
raise VarLibValidationError("Designspace must have at least one source.")
|
||||||
instances = ds.instances
|
instances = ds.instances
|
||||||
|
|
||||||
@ -978,7 +978,7 @@ def load_designspace(designspace, log_enabled=True):
|
|||||||
"More than one base master found in Designspace."
|
"More than one base master found in Designspace."
|
||||||
)
|
)
|
||||||
base_idx = i
|
base_idx = i
|
||||||
if base_idx is None:
|
if require_sources and base_idx is None:
|
||||||
raise VarLibValidationError(
|
raise VarLibValidationError(
|
||||||
"Base master not found; no master at default location?"
|
"Base master not found; no master at default location?"
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,187 @@
|
|||||||
from fontTools.varLib import _add_avar, load_designspace
|
from fontTools.varLib import _add_avar, load_designspace
|
||||||
|
from fontTools.varLib.models import VariationModel
|
||||||
|
from fontTools.varLib.varStore import VarStoreInstancer
|
||||||
|
from fontTools.misc.fixedTools import fixedToFloat as fi2fl
|
||||||
from fontTools.misc.cliTools import makeOutputFileName
|
from fontTools.misc.cliTools import makeOutputFileName
|
||||||
|
from itertools import product
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
log = logging.getLogger("fontTools.varLib.avar")
|
log = logging.getLogger("fontTools.varLib.avar")
|
||||||
|
|
||||||
|
|
||||||
|
def _denormalize(v, axis):
|
||||||
|
if v >= 0:
|
||||||
|
return axis.defaultValue + v * (axis.maxValue - axis.defaultValue)
|
||||||
|
else:
|
||||||
|
return axis.defaultValue + v * (axis.defaultValue - axis.minValue)
|
||||||
|
|
||||||
|
|
||||||
|
def _pruneLocations(locations, poles, axisTags):
|
||||||
|
# Now we have all the input locations, find which ones are
|
||||||
|
# not needed and remove them.
|
||||||
|
|
||||||
|
# Note: This algorithm is heavily tied to how VariationModel
|
||||||
|
# is implemented. It assumes that input was extracted from
|
||||||
|
# VariationModel-generated object, like an ItemVariationStore
|
||||||
|
# created by fontmake using varLib.models.VariationModel.
|
||||||
|
# Some CoPilot blabbering:
|
||||||
|
# I *think* I can prove that this algorithm is correct, but
|
||||||
|
# I'm not 100% sure. It's possible that there are edge cases
|
||||||
|
# where this algorithm will fail. I'm not sure how to prove
|
||||||
|
# that it's correct, but I'm also not sure how to prove that
|
||||||
|
# it's incorrect. I'm not sure how to write a test case that
|
||||||
|
# would prove that it's incorrect. I'm not sure how to write
|
||||||
|
# a test case that would prove that it's correct.
|
||||||
|
|
||||||
|
model = VariationModel(locations, axisTags)
|
||||||
|
modelMapping = model.mapping
|
||||||
|
modelSupports = model.supports
|
||||||
|
pins = {tuple(k.items()): None for k in poles}
|
||||||
|
for location in poles:
|
||||||
|
i = locations.index(location)
|
||||||
|
i = modelMapping[i]
|
||||||
|
support = modelSupports[i]
|
||||||
|
supportAxes = set(support.keys())
|
||||||
|
for axisTag, (minV, _, maxV) in support.items():
|
||||||
|
for v in (minV, maxV):
|
||||||
|
if v in (-1, 0, 1):
|
||||||
|
continue
|
||||||
|
for pin in pins.keys():
|
||||||
|
pinLocation = dict(pin)
|
||||||
|
pinAxes = set(pinLocation.keys())
|
||||||
|
if pinAxes != supportAxes:
|
||||||
|
continue
|
||||||
|
if axisTag not in pinAxes:
|
||||||
|
continue
|
||||||
|
if pinLocation[axisTag] == v:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# No pin found. Go through the previous masters
|
||||||
|
# and find a suitable pin. Going backwards is
|
||||||
|
# better because it can find a pin that is close
|
||||||
|
# to the pole in more dimensions, and reducing
|
||||||
|
# the total number of pins needed.
|
||||||
|
for candidateIdx in range(i - 1, -1, -1):
|
||||||
|
candidate = modelSupports[candidateIdx]
|
||||||
|
candidateAxes = set(candidate.keys())
|
||||||
|
if candidateAxes != supportAxes:
|
||||||
|
continue
|
||||||
|
if axisTag not in candidateAxes:
|
||||||
|
continue
|
||||||
|
candidate = {
|
||||||
|
k: defaultV for k, (_, defaultV, _) in candidate.items()
|
||||||
|
}
|
||||||
|
if candidate[axisTag] == v:
|
||||||
|
pins[tuple(candidate.items())] = None
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False, "No pin found"
|
||||||
|
return [dict(t) for t in pins.keys()]
|
||||||
|
|
||||||
|
|
||||||
|
def mappings_from_avar(font, denormalize=True):
|
||||||
|
fvarAxes = font["fvar"].axes
|
||||||
|
axisMap = {a.axisTag: a for a in fvarAxes}
|
||||||
|
axisTags = [a.axisTag for a in fvarAxes]
|
||||||
|
axisIndexes = {a.axisTag: i for i, a in enumerate(fvarAxes)}
|
||||||
|
if "avar" not in font:
|
||||||
|
return {}, {}
|
||||||
|
avar = font["avar"]
|
||||||
|
axisMaps = {
|
||||||
|
tag: seg
|
||||||
|
for tag, seg in avar.segments.items()
|
||||||
|
if seg and seg != {-1: -1, 0: 0, 1: 1}
|
||||||
|
}
|
||||||
|
mappings = []
|
||||||
|
|
||||||
|
if getattr(avar, "majorVersion", 1) == 2:
|
||||||
|
varStore = avar.table.VarStore
|
||||||
|
regions = varStore.VarRegionList.Region
|
||||||
|
|
||||||
|
# Find all the input locations; this finds "poles", that are
|
||||||
|
# locations of the peaks, and "corners", that are locations
|
||||||
|
# of the corners of the regions. These two sets of locations
|
||||||
|
# together constitute inputLocations to consider.
|
||||||
|
|
||||||
|
poles = {(): None} # Just using it as an ordered set
|
||||||
|
inputLocations = set({()})
|
||||||
|
for varData in varStore.VarData:
|
||||||
|
regionIndices = varData.VarRegionIndex
|
||||||
|
for regionIndex in regionIndices:
|
||||||
|
peakLocation = []
|
||||||
|
corners = []
|
||||||
|
region = regions[regionIndex]
|
||||||
|
for axisIndex, axis in enumerate(region.VarRegionAxis):
|
||||||
|
if axis.PeakCoord == 0:
|
||||||
|
continue
|
||||||
|
axisTag = axisTags[axisIndex]
|
||||||
|
peakLocation.append((axisTag, axis.PeakCoord))
|
||||||
|
corner = []
|
||||||
|
if axis.StartCoord != 0:
|
||||||
|
corner.append((axisTag, axis.StartCoord))
|
||||||
|
if axis.EndCoord != 0:
|
||||||
|
corner.append((axisTag, axis.EndCoord))
|
||||||
|
corners.append(corner)
|
||||||
|
corners = set(product(*corners))
|
||||||
|
peakLocation = tuple(peakLocation)
|
||||||
|
poles[peakLocation] = None
|
||||||
|
inputLocations.add(peakLocation)
|
||||||
|
inputLocations.update(corners)
|
||||||
|
|
||||||
|
# Sort them by number of axes, then by axis order
|
||||||
|
inputLocations = [
|
||||||
|
dict(t)
|
||||||
|
for t in sorted(
|
||||||
|
inputLocations,
|
||||||
|
key=lambda t: (len(t), tuple(axisIndexes[tag] for tag, _ in t)),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
poles = [dict(t) for t in poles.keys()]
|
||||||
|
inputLocations = _pruneLocations(inputLocations, list(poles), axisTags)
|
||||||
|
|
||||||
|
# Find the output locations, at input locations
|
||||||
|
varIdxMap = avar.table.VarIdxMap
|
||||||
|
instancer = VarStoreInstancer(varStore, fvarAxes)
|
||||||
|
for location in inputLocations:
|
||||||
|
instancer.setLocation(location)
|
||||||
|
outputLocation = {}
|
||||||
|
for axisIndex, axisTag in enumerate(axisTags):
|
||||||
|
varIdx = axisIndex
|
||||||
|
if varIdxMap is not None:
|
||||||
|
varIdx = varIdxMap[varIdx]
|
||||||
|
delta = instancer[varIdx]
|
||||||
|
if delta != 0:
|
||||||
|
v = location.get(axisTag, 0)
|
||||||
|
v = v + fi2fl(delta, 14)
|
||||||
|
# See https://github.com/fonttools/fonttools/pull/3598#issuecomment-2266082009
|
||||||
|
# v = max(-1, min(1, v))
|
||||||
|
outputLocation[axisTag] = v
|
||||||
|
mappings.append((location, outputLocation))
|
||||||
|
|
||||||
|
# Remove base master we added, if it maps to the default location
|
||||||
|
assert mappings[0][0] == {}
|
||||||
|
if mappings[0][1] == {}:
|
||||||
|
mappings.pop(0)
|
||||||
|
|
||||||
|
if denormalize:
|
||||||
|
for tag, seg in axisMaps.items():
|
||||||
|
if tag not in axisMap:
|
||||||
|
raise ValueError(f"Unknown axis tag {tag}")
|
||||||
|
denorm = lambda v: _denormalize(v, axisMap[tag])
|
||||||
|
axisMaps[tag] = {denorm(k): denorm(v) for k, v in seg.items()}
|
||||||
|
|
||||||
|
for i, (inputLoc, outputLoc) in enumerate(mappings):
|
||||||
|
inputLoc = {
|
||||||
|
tag: _denormalize(val, axisMap[tag]) for tag, val in inputLoc.items()
|
||||||
|
}
|
||||||
|
outputLoc = {
|
||||||
|
tag: _denormalize(val, axisMap[tag]) for tag, val in outputLoc.items()
|
||||||
|
}
|
||||||
|
mappings[i] = (inputLoc, outputLoc)
|
||||||
|
|
||||||
|
return axisMaps, mappings
|
||||||
|
|
||||||
|
|
||||||
def main(args=None):
|
def main(args=None):
|
||||||
"""Add `avar` table from designspace file to variable font."""
|
"""Add `avar` table from designspace file to variable font."""
|
||||||
|
|
||||||
@ -24,7 +201,11 @@ def main(args=None):
|
|||||||
)
|
)
|
||||||
parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
|
parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"designspace", metavar="family.designspace", help="Designspace file."
|
"designspace",
|
||||||
|
metavar="family.designspace",
|
||||||
|
help="Designspace file.",
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-o",
|
"-o",
|
||||||
@ -45,9 +226,18 @@ def main(args=None):
|
|||||||
log.error("Not a variable font.")
|
log.error("Not a variable font.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
if options.designspace is None:
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
segments, mappings = mappings_from_avar(font)
|
||||||
|
pprint(segments)
|
||||||
|
pprint(mappings)
|
||||||
|
print(len(mappings), "mappings")
|
||||||
|
return
|
||||||
|
|
||||||
axisTags = [a.axisTag for a in font["fvar"].axes]
|
axisTags = [a.axisTag for a in font["fvar"].axes]
|
||||||
|
|
||||||
ds = load_designspace(options.designspace)
|
ds = load_designspace(options.designspace, require_sources=False)
|
||||||
|
|
||||||
if "avar" in font:
|
if "avar" in font:
|
||||||
log.warning("avar table already present, overwriting.")
|
log.warning("avar table already present, overwriting.")
|
||||||
|
@ -135,6 +135,7 @@ def test_gen(
|
|||||||
kinkiness=DEFAULT_KINKINESS,
|
kinkiness=DEFAULT_KINKINESS,
|
||||||
upem=DEFAULT_UPEM,
|
upem=DEFAULT_UPEM,
|
||||||
show_all=False,
|
show_all=False,
|
||||||
|
discrete_axes=[],
|
||||||
):
|
):
|
||||||
if tolerance >= 10:
|
if tolerance >= 10:
|
||||||
tolerance *= 0.01
|
tolerance *= 0.01
|
||||||
@ -150,7 +151,9 @@ def test_gen(
|
|||||||
# ... risks the sparse master being the first one, and only processing a subset of the glyphs
|
# ... risks the sparse master being the first one, and only processing a subset of the glyphs
|
||||||
glyphs = {g for glyphset in glyphsets for g in glyphset.keys()}
|
glyphs = {g for glyphset in glyphsets for g in glyphset.keys()}
|
||||||
|
|
||||||
parents, order = find_parents_and_order(glyphsets, locations)
|
parents, order = find_parents_and_order(
|
||||||
|
glyphsets, locations, discrete_axes=discrete_axes
|
||||||
|
)
|
||||||
|
|
||||||
def grand_parent(i, glyphname):
|
def grand_parent(i, glyphname):
|
||||||
if i is None:
|
if i is None:
|
||||||
@ -701,6 +704,7 @@ def main(args=None):
|
|||||||
fonts = []
|
fonts = []
|
||||||
names = []
|
names = []
|
||||||
locations = []
|
locations = []
|
||||||
|
discrete_axes = set()
|
||||||
upem = DEFAULT_UPEM
|
upem = DEFAULT_UPEM
|
||||||
|
|
||||||
original_args_inputs = tuple(args.inputs)
|
original_args_inputs = tuple(args.inputs)
|
||||||
@ -713,8 +717,13 @@ def main(args=None):
|
|||||||
designspace = DesignSpaceDocument.fromfile(args.inputs[0])
|
designspace = DesignSpaceDocument.fromfile(args.inputs[0])
|
||||||
args.inputs = [master.path for master in designspace.sources]
|
args.inputs = [master.path for master in designspace.sources]
|
||||||
locations = [master.location for master in designspace.sources]
|
locations = [master.location for master in designspace.sources]
|
||||||
|
discrete_axes = {
|
||||||
|
a.name for a in designspace.axes if not hasattr(a, "minimum")
|
||||||
|
}
|
||||||
axis_triples = {
|
axis_triples = {
|
||||||
a.name: (a.minimum, a.default, a.maximum) for a in designspace.axes
|
a.name: (a.minimum, a.default, a.maximum)
|
||||||
|
for a in designspace.axes
|
||||||
|
if a.name not in discrete_axes
|
||||||
}
|
}
|
||||||
axis_mappings = {a.name: a.map for a in designspace.axes}
|
axis_mappings = {a.name: a.map for a in designspace.axes}
|
||||||
axis_triples = {
|
axis_triples = {
|
||||||
@ -879,7 +888,13 @@ def main(args=None):
|
|||||||
glyphset[gn] = None
|
glyphset[gn] = None
|
||||||
|
|
||||||
# Normalize locations
|
# Normalize locations
|
||||||
locations = [normalizeLocation(loc, axis_triples) for loc in locations]
|
locations = [
|
||||||
|
{
|
||||||
|
**normalizeLocation(loc, axis_triples),
|
||||||
|
**{k: v for k, v in loc.items() if k in discrete_axes},
|
||||||
|
}
|
||||||
|
for loc in locations
|
||||||
|
]
|
||||||
tolerance = args.tolerance or DEFAULT_TOLERANCE
|
tolerance = args.tolerance or DEFAULT_TOLERANCE
|
||||||
kinkiness = args.kinkiness if args.kinkiness is not None else DEFAULT_KINKINESS
|
kinkiness = args.kinkiness if args.kinkiness is not None else DEFAULT_KINKINESS
|
||||||
|
|
||||||
@ -896,6 +911,7 @@ def main(args=None):
|
|||||||
tolerance=tolerance,
|
tolerance=tolerance,
|
||||||
kinkiness=kinkiness,
|
kinkiness=kinkiness,
|
||||||
show_all=args.show_all,
|
show_all=args.show_all,
|
||||||
|
discrete_axes=discrete_axes,
|
||||||
)
|
)
|
||||||
problems = defaultdict(list)
|
problems = defaultdict(list)
|
||||||
|
|
||||||
|
@ -293,17 +293,19 @@ def add_isomorphisms(points, isomorphisms, reverse):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def find_parents_and_order(glyphsets, locations):
|
def find_parents_and_order(glyphsets, locations, *, discrete_axes=set()):
|
||||||
parents = [None] + list(range(len(glyphsets) - 1))
|
parents = [None] + list(range(len(glyphsets) - 1))
|
||||||
order = list(range(len(glyphsets)))
|
order = list(range(len(glyphsets)))
|
||||||
if locations:
|
if locations:
|
||||||
# Order base master first
|
# Order base master first
|
||||||
bases = (i for i, l in enumerate(locations) if all(v == 0 for v in l.values()))
|
bases = [
|
||||||
|
i
|
||||||
|
for i, l in enumerate(locations)
|
||||||
|
if all(v == 0 for k, v in l.items() if k not in discrete_axes)
|
||||||
|
]
|
||||||
if bases:
|
if bases:
|
||||||
base = next(bases)
|
logging.info("Found %s base masters: %s", len(bases), bases)
|
||||||
logging.info("Base master index %s, location %s", base, locations[base])
|
|
||||||
else:
|
else:
|
||||||
base = 0
|
|
||||||
logging.warning("No base master location found")
|
logging.warning("No base master location found")
|
||||||
|
|
||||||
# Form a minimum spanning tree of the locations
|
# Form a minimum spanning tree of the locations
|
||||||
@ -317,9 +319,17 @@ def find_parents_and_order(glyphsets, locations):
|
|||||||
axes = sorted(axes)
|
axes = sorted(axes)
|
||||||
vectors = [tuple(l.get(k, 0) for k in axes) for l in locations]
|
vectors = [tuple(l.get(k, 0) for k in axes) for l in locations]
|
||||||
for i, j in itertools.combinations(range(len(locations)), 2):
|
for i, j in itertools.combinations(range(len(locations)), 2):
|
||||||
|
i_discrete_location = {
|
||||||
|
k: v for k, v in zip(axes, vectors[i]) if k in discrete_axes
|
||||||
|
}
|
||||||
|
j_discrete_location = {
|
||||||
|
k: v for k, v in zip(axes, vectors[j]) if k in discrete_axes
|
||||||
|
}
|
||||||
|
if i_discrete_location != j_discrete_location:
|
||||||
|
continue
|
||||||
graph[i][j] = vdiff_hypot2(vectors[i], vectors[j])
|
graph[i][j] = vdiff_hypot2(vectors[i], vectors[j])
|
||||||
|
|
||||||
tree = minimum_spanning_tree(graph)
|
tree = minimum_spanning_tree(graph, overwrite=True)
|
||||||
rows, cols = tree.nonzero()
|
rows, cols = tree.nonzero()
|
||||||
graph = defaultdict(set)
|
graph = defaultdict(set)
|
||||||
for row, col in zip(rows, cols):
|
for row, col in zip(rows, cols):
|
||||||
@ -330,7 +340,7 @@ def find_parents_and_order(glyphsets, locations):
|
|||||||
parents = [None] * len(locations)
|
parents = [None] * len(locations)
|
||||||
order = []
|
order = []
|
||||||
visited = set()
|
visited = set()
|
||||||
queue = deque([base])
|
queue = deque(bases)
|
||||||
while queue:
|
while queue:
|
||||||
i = queue.popleft()
|
i = queue.popleft()
|
||||||
visited.add(i)
|
visited.add(i)
|
||||||
@ -339,6 +349,9 @@ def find_parents_and_order(glyphsets, locations):
|
|||||||
if j not in visited:
|
if j not in visited:
|
||||||
parents[j] = i
|
parents[j] = i
|
||||||
queue.append(j)
|
queue.append(j)
|
||||||
|
assert len(order) == len(
|
||||||
|
parents
|
||||||
|
), "Not all masters are reachable; report an issue"
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
@ -209,10 +209,14 @@ def supportScalar(location, support, ot=True, extrapolate=False, axisRanges=None
|
|||||||
class VariationModel(object):
|
class VariationModel(object):
|
||||||
"""Locations must have the base master at the origin (ie. 0).
|
"""Locations must have the base master at the origin (ie. 0).
|
||||||
|
|
||||||
|
If axis-ranges are not provided, values are assumed to be normalized to
|
||||||
|
the range [-1, 1].
|
||||||
|
|
||||||
If the extrapolate argument is set to True, then values are extrapolated
|
If the extrapolate argument is set to True, then values are extrapolated
|
||||||
outside the axis range.
|
outside the axis range.
|
||||||
|
|
||||||
>>> from pprint import pprint
|
>>> from pprint import pprint
|
||||||
|
>>> axisRanges = {'wght': (-180, +180), 'wdth': (-1, +1)}
|
||||||
>>> locations = [ \
|
>>> locations = [ \
|
||||||
{'wght':100}, \
|
{'wght':100}, \
|
||||||
{'wght':-100}, \
|
{'wght':-100}, \
|
||||||
@ -224,7 +228,7 @@ class VariationModel(object):
|
|||||||
{'wght':+180,'wdth':.3}, \
|
{'wght':+180,'wdth':.3}, \
|
||||||
{'wght':+180}, \
|
{'wght':+180}, \
|
||||||
]
|
]
|
||||||
>>> model = VariationModel(locations, axisOrder=['wght'])
|
>>> model = VariationModel(locations, axisOrder=['wght'], axisRanges=axisRanges)
|
||||||
>>> pprint(model.locations)
|
>>> pprint(model.locations)
|
||||||
[{},
|
[{},
|
||||||
{'wght': -100},
|
{'wght': -100},
|
||||||
@ -252,14 +256,22 @@ class VariationModel(object):
|
|||||||
7: 0.6666666666666667}]
|
7: 0.6666666666666667}]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, locations, axisOrder=None, extrapolate=False):
|
def __init__(
|
||||||
|
self, locations, axisOrder=None, extrapolate=False, *, axisRanges=None
|
||||||
|
):
|
||||||
if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
|
if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
|
||||||
raise VariationModelError("Locations must be unique.")
|
raise VariationModelError("Locations must be unique.")
|
||||||
|
|
||||||
self.origLocations = locations
|
self.origLocations = locations
|
||||||
self.axisOrder = axisOrder if axisOrder is not None else []
|
self.axisOrder = axisOrder if axisOrder is not None else []
|
||||||
self.extrapolate = extrapolate
|
self.extrapolate = extrapolate
|
||||||
self.axisRanges = self.computeAxisRanges(locations) if extrapolate else None
|
if axisRanges is None:
|
||||||
|
if extrapolate:
|
||||||
|
axisRanges = self.computeAxisRanges(locations)
|
||||||
|
else:
|
||||||
|
allAxes = {axis for loc in locations for axis in loc.keys()}
|
||||||
|
axisRanges = {axis: (-1, 1) for axis in allAxes}
|
||||||
|
self.axisRanges = axisRanges
|
||||||
|
|
||||||
locations = [{k: v for k, v in loc.items() if v != 0.0} for loc in locations]
|
locations = [{k: v for k, v in loc.items() if v != 0.0} for loc in locations]
|
||||||
keyFunc = self.getMasterLocationsSortKeyFunc(
|
keyFunc = self.getMasterLocationsSortKeyFunc(
|
||||||
@ -374,7 +386,7 @@ class VariationModel(object):
|
|||||||
locAxes = set(region.keys())
|
locAxes = set(region.keys())
|
||||||
# Walk over previous masters now
|
# Walk over previous masters now
|
||||||
for prev_region in regions[:i]:
|
for prev_region in regions[:i]:
|
||||||
# Master with extra axes do not participte
|
# Master with different axes do not participte
|
||||||
if set(prev_region.keys()) != locAxes:
|
if set(prev_region.keys()) != locAxes:
|
||||||
continue
|
continue
|
||||||
# If it's NOT in the current box, it does not participate
|
# If it's NOT in the current box, it does not participate
|
||||||
@ -425,23 +437,16 @@ class VariationModel(object):
|
|||||||
|
|
||||||
def _locationsToRegions(self):
|
def _locationsToRegions(self):
|
||||||
locations = self.locations
|
locations = self.locations
|
||||||
# Compute min/max across each axis, use it as total range.
|
axisRanges = self.axisRanges
|
||||||
# TODO Take this as input from outside?
|
|
||||||
minV = {}
|
|
||||||
maxV = {}
|
|
||||||
for l in locations:
|
|
||||||
for k, v in l.items():
|
|
||||||
minV[k] = min(v, minV.get(k, v))
|
|
||||||
maxV[k] = max(v, maxV.get(k, v))
|
|
||||||
|
|
||||||
regions = []
|
regions = []
|
||||||
for loc in locations:
|
for loc in locations:
|
||||||
region = {}
|
region = {}
|
||||||
for axis, locV in loc.items():
|
for axis, locV in loc.items():
|
||||||
if locV > 0:
|
if locV > 0:
|
||||||
region[axis] = (0, locV, maxV[axis])
|
region[axis] = (0, locV, axisRanges[axis][1])
|
||||||
else:
|
else:
|
||||||
region[axis] = (minV[axis], locV, 0)
|
region[axis] = (axisRanges[axis][0], locV, 0)
|
||||||
regions.append(region)
|
regions.append(region)
|
||||||
return regions
|
return regions
|
||||||
|
|
||||||
|
@ -1112,12 +1112,12 @@ class BuilderTest(unittest.TestCase):
|
|||||||
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
||||||
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
||||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.875, 0.875)
|
assert self.get_region(var_region_axis_wght) == (0.0, 0.875, 1.0)
|
||||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
assert self.get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
||||||
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
||||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.875, 0.875)
|
assert self.get_region(var_region_axis_wght) == (0.0, 0.875, 1.0)
|
||||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.5, 0.5)
|
assert self.get_region(var_region_axis_wdth) == (0.0, 0.5, 1.0)
|
||||||
|
|
||||||
# With `avar`, shifting the wght axis' positive midpoint 0.5 a bit to
|
# With `avar`, shifting the wght axis' positive midpoint 0.5 a bit to
|
||||||
# the right, but leaving the wdth axis alone:
|
# the right, but leaving the wdth axis alone:
|
||||||
@ -1129,12 +1129,12 @@ class BuilderTest(unittest.TestCase):
|
|||||||
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
var_region_list = font.tables["GDEF"].table.VarStore.VarRegionList
|
||||||
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[0].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
var_region_axis_wdth = var_region_list.Region[0].VarRegionAxis[1]
|
||||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.90625, 0.90625)
|
assert self.get_region(var_region_axis_wght) == (0.0, 0.90625, 1.0)
|
||||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
assert self.get_region(var_region_axis_wdth) == (0.0, 0.0, 0.0)
|
||||||
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
var_region_axis_wght = var_region_list.Region[1].VarRegionAxis[0]
|
||||||
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
var_region_axis_wdth = var_region_list.Region[1].VarRegionAxis[1]
|
||||||
assert self.get_region(var_region_axis_wght) == (0.0, 0.90625, 0.90625)
|
assert self.get_region(var_region_axis_wght) == (0.0, 0.90625, 1.0)
|
||||||
assert self.get_region(var_region_axis_wdth) == (0.0, 0.5, 0.5)
|
assert self.get_region(var_region_axis_wdth) == (0.0, 0.5, 1.0)
|
||||||
|
|
||||||
def test_ligatureCaretByPos_variable_scalar(self):
|
def test_ligatureCaretByPos_variable_scalar(self):
|
||||||
"""Test that the `avar` table is consulted when normalizing user-space
|
"""Test that the `avar` table is consulted when normalizing user-space
|
||||||
@ -1158,7 +1158,7 @@ class BuilderTest(unittest.TestCase):
|
|||||||
|
|
||||||
var_region_list = table.VarStore.VarRegionList
|
var_region_list = table.VarStore.VarRegionList
|
||||||
var_region_axis = var_region_list.Region[0].VarRegionAxis[0]
|
var_region_axis = var_region_list.Region[0].VarRegionAxis[0]
|
||||||
assert self.get_region(var_region_axis) == (0.0, 0.875, 0.875)
|
assert self.get_region(var_region_axis) == (0.0, 0.875, 1.0)
|
||||||
|
|
||||||
|
|
||||||
def generate_feature_file_test(name):
|
def generate_feature_file_test(name):
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<VarRegionAxis index="0">
|
<VarRegionAxis index="0">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.875"/>
|
<PeakCoord value="0.875"/>
|
||||||
<EndCoord value="0.875"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
<VarRegionAxis index="1">
|
<VarRegionAxis index="1">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<VarRegionAxis index="0">
|
<VarRegionAxis index="0">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.875"/>
|
<PeakCoord value="0.875"/>
|
||||||
<EndCoord value="0.875"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
<VarRegionAxis index="1">
|
<VarRegionAxis index="1">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
@ -24,12 +24,12 @@
|
|||||||
<VarRegionAxis index="0">
|
<VarRegionAxis index="0">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.875"/>
|
<PeakCoord value="0.875"/>
|
||||||
<EndCoord value="0.875"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
<VarRegionAxis index="1">
|
<VarRegionAxis index="1">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.5"/>
|
<PeakCoord value="0.5"/>
|
||||||
<EndCoord value="0.5"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
</Region>
|
</Region>
|
||||||
</VarRegionList>
|
</VarRegionList>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<VarRegionAxis index="0">
|
<VarRegionAxis index="0">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.875"/>
|
<PeakCoord value="0.875"/>
|
||||||
<EndCoord value="0.875"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
<VarRegionAxis index="1">
|
<VarRegionAxis index="1">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
@ -24,12 +24,12 @@
|
|||||||
<VarRegionAxis index="0">
|
<VarRegionAxis index="0">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.875"/>
|
<PeakCoord value="0.875"/>
|
||||||
<EndCoord value="0.875"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
<VarRegionAxis index="1">
|
<VarRegionAxis index="1">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.5"/>
|
<PeakCoord value="0.5"/>
|
||||||
<EndCoord value="0.5"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
</Region>
|
</Region>
|
||||||
</VarRegionList>
|
</VarRegionList>
|
||||||
|
@ -2,7 +2,6 @@ import contextlib
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import run
|
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -28,18 +27,17 @@ def test_main(tmpdir: Path):
|
|||||||
input = tmpdir / "in.ttf"
|
input = tmpdir / "in.ttf"
|
||||||
fb.save(str(input))
|
fb.save(str(input))
|
||||||
output = tmpdir / "out.ttf"
|
output = tmpdir / "out.ttf"
|
||||||
run(
|
args = [
|
||||||
[
|
"--gpos-compression-level",
|
||||||
"fonttools",
|
"5",
|
||||||
"otlLib.optimize",
|
str(input),
|
||||||
"--gpos-compression-level",
|
"-o",
|
||||||
"5",
|
str(output),
|
||||||
str(input),
|
]
|
||||||
"-o",
|
from fontTools.otlLib.optimize import main
|
||||||
str(output),
|
|
||||||
],
|
ret = main(args)
|
||||||
check=True,
|
assert ret in (0, None)
|
||||||
)
|
|
||||||
assert output.exists()
|
assert output.exists()
|
||||||
|
|
||||||
|
|
||||||
|
733
Tests/subset/data/Andika-Regular.subset.ttx
Normal file
733
Tests/subset/data/Andika-Regular.subset.ttx
Normal file
@ -0,0 +1,733 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.53">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="space"/>
|
||||||
|
<GlyphID id="2" name="eng"/>
|
||||||
|
<GlyphID id="3" name="Eng.UCStyle"/>
|
||||||
|
<GlyphID id="4" name="Eng.BaselineHook"/>
|
||||||
|
<GlyphID id="5" name="Eng"/>
|
||||||
|
<GlyphID id="6" name="Eng.Kom"/>
|
||||||
|
<GlyphID id="7" name="eng.BaselineHook.sc"/>
|
||||||
|
<GlyphID id="8" name="eng.UCStyle.sc"/>
|
||||||
|
<GlyphID id="9" name="eng.Kom.sc"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<!-- Most of this table will be recalculated by the compiler -->
|
||||||
|
<tableVersion value="1.0"/>
|
||||||
|
<fontRevision value="6.101"/>
|
||||||
|
<checkSumAdjustment value="0x60016654"/>
|
||||||
|
<magicNumber value="0x5f0f3cf5"/>
|
||||||
|
<flags value="00000000 00000111"/>
|
||||||
|
<unitsPerEm value="2048"/>
|
||||||
|
<created value="Wed Feb 2 15:47:19 2022"/>
|
||||||
|
<modified value="Wed Feb 9 14:12:57 2022"/>
|
||||||
|
<xMin value="-1387"/>
|
||||||
|
<yMin value="-1148"/>
|
||||||
|
<xMax value="4805"/>
|
||||||
|
<yMax value="2620"/>
|
||||||
|
<macStyle value="00000000 00000000"/>
|
||||||
|
<lowestRecPPEM value="6"/>
|
||||||
|
<fontDirectionHint value="2"/>
|
||||||
|
<indexToLocFormat value="0"/>
|
||||||
|
<glyphDataFormat value="0"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<hhea>
|
||||||
|
<tableVersion value="0x00010000"/>
|
||||||
|
<ascent value="2500"/>
|
||||||
|
<descent value="-800"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceWidthMax value="4885"/>
|
||||||
|
<minLeftSideBearing value="-1387"/>
|
||||||
|
<minRightSideBearing value="-1060"/>
|
||||||
|
<xMaxExtent value="4805"/>
|
||||||
|
<caretSlopeRise value="1"/>
|
||||||
|
<caretSlopeRun value="0"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved0 value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfHMetrics value="10"/>
|
||||||
|
</hhea>
|
||||||
|
|
||||||
|
<maxp>
|
||||||
|
<!-- Most of this table will be recalculated by the compiler -->
|
||||||
|
<tableVersion value="0x10000"/>
|
||||||
|
<numGlyphs value="10"/>
|
||||||
|
<maxPoints value="191"/>
|
||||||
|
<maxContours value="24"/>
|
||||||
|
<maxCompositePoints value="116"/>
|
||||||
|
<maxCompositeContours value="7"/>
|
||||||
|
<maxZones value="1"/>
|
||||||
|
<maxTwilightPoints value="0"/>
|
||||||
|
<maxStorage value="0"/>
|
||||||
|
<maxFunctionDefs value="0"/>
|
||||||
|
<maxInstructionDefs value="0"/>
|
||||||
|
<maxStackElements value="0"/>
|
||||||
|
<maxSizeOfInstructions value="0"/>
|
||||||
|
<maxComponentElements value="5"/>
|
||||||
|
<maxComponentDepth value="1"/>
|
||||||
|
</maxp>
|
||||||
|
|
||||||
|
<OS_2>
|
||||||
|
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
|
||||||
|
will be recalculated by the compiler -->
|
||||||
|
<version value="4"/>
|
||||||
|
<xAvgCharWidth value="1146"/>
|
||||||
|
<usWeightClass value="400"/>
|
||||||
|
<usWidthClass value="5"/>
|
||||||
|
<fsType value="00000000 00000000"/>
|
||||||
|
<ySubscriptXSize value="1433"/>
|
||||||
|
<ySubscriptYSize value="1331"/>
|
||||||
|
<ySubscriptXOffset value="0"/>
|
||||||
|
<ySubscriptYOffset value="286"/>
|
||||||
|
<ySuperscriptXSize value="1433"/>
|
||||||
|
<ySuperscriptYSize value="1331"/>
|
||||||
|
<ySuperscriptXOffset value="0"/>
|
||||||
|
<ySuperscriptYOffset value="976"/>
|
||||||
|
<yStrikeoutSize value="100"/>
|
||||||
|
<yStrikeoutPosition value="700"/>
|
||||||
|
<sFamilyClass value="0"/>
|
||||||
|
<panose>
|
||||||
|
<bFamilyType value="2"/>
|
||||||
|
<bSerifStyle value="0"/>
|
||||||
|
<bWeight value="0"/>
|
||||||
|
<bProportion value="0"/>
|
||||||
|
<bContrast value="0"/>
|
||||||
|
<bStrokeVariation value="0"/>
|
||||||
|
<bArmStyle value="0"/>
|
||||||
|
<bLetterForm value="0"/>
|
||||||
|
<bMidline value="0"/>
|
||||||
|
<bXHeight value="0"/>
|
||||||
|
</panose>
|
||||||
|
<ulUnicodeRange1 value="00000000 00000000 00000000 00000101"/>
|
||||||
|
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<achVendID value="SIL "/>
|
||||||
|
<fsSelection value="00000000 11000000"/>
|
||||||
|
<usFirstCharIndex value="32"/>
|
||||||
|
<usLastCharIndex value="331"/>
|
||||||
|
<sTypoAscender value="2500"/>
|
||||||
|
<sTypoDescender value="-800"/>
|
||||||
|
<sTypoLineGap value="0"/>
|
||||||
|
<usWinAscent value="2500"/>
|
||||||
|
<usWinDescent value="800"/>
|
||||||
|
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
|
||||||
|
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<sxHeight value="1040"/>
|
||||||
|
<sCapHeight value="1485"/>
|
||||||
|
<usDefaultChar value="0"/>
|
||||||
|
<usBreakChar value="32"/>
|
||||||
|
<usMaxContext value="7"/>
|
||||||
|
</OS_2>
|
||||||
|
|
||||||
|
<hmtx>
|
||||||
|
<mtx name=".notdef" width="1400" lsb="100"/>
|
||||||
|
<mtx name="Eng" width="1460" lsb="135"/>
|
||||||
|
<mtx name="Eng.BaselineHook" width="1496" lsb="135"/>
|
||||||
|
<mtx name="Eng.Kom" width="1496" lsb="135"/>
|
||||||
|
<mtx name="Eng.UCStyle" width="1500" lsb="160"/>
|
||||||
|
<mtx name="eng" width="1185" lsb="105"/>
|
||||||
|
<mtx name="eng.BaselineHook.sc" width="1396" lsb="129"/>
|
||||||
|
<mtx name="eng.Kom.sc" width="1396" lsb="129"/>
|
||||||
|
<mtx name="eng.UCStyle.sc" width="1400" lsb="153"/>
|
||||||
|
<mtx name="space" width="550" lsb="0"/>
|
||||||
|
</hmtx>
|
||||||
|
|
||||||
|
<cmap>
|
||||||
|
<tableVersion version="0"/>
|
||||||
|
<cmap_format_4 platformID="0" platEncID="3" language="0">
|
||||||
|
<map code="0x20" name="space"/><!-- SPACE -->
|
||||||
|
<map code="0x14a" name="Eng"/><!-- LATIN CAPITAL LETTER ENG -->
|
||||||
|
<map code="0x14b" name="eng"/><!-- LATIN SMALL LETTER ENG -->
|
||||||
|
</cmap_format_4>
|
||||||
|
<cmap_format_4 platformID="3" platEncID="1" language="0">
|
||||||
|
<map code="0x20" name="space"/><!-- SPACE -->
|
||||||
|
<map code="0x14a" name="Eng"/><!-- LATIN CAPITAL LETTER ENG -->
|
||||||
|
<map code="0x14b" name="eng"/><!-- LATIN SMALL LETTER ENG -->
|
||||||
|
</cmap_format_4>
|
||||||
|
</cmap>
|
||||||
|
|
||||||
|
<loca>
|
||||||
|
<!-- The 'loca' table will be calculated by the compiler -->
|
||||||
|
</loca>
|
||||||
|
|
||||||
|
<glyf>
|
||||||
|
|
||||||
|
<!-- The xMin, yMin, xMax and yMax values
|
||||||
|
will be recalculated by the compiler. -->
|
||||||
|
|
||||||
|
<TTGlyph name=".notdef"/><!-- contains no outline data -->
|
||||||
|
|
||||||
|
<TTGlyph name="Eng" xMin="135" yMin="-470" xMax="1275" yMax="1485">
|
||||||
|
<contour>
|
||||||
|
<pt x="135" y="1455" on="1"/>
|
||||||
|
<pt x="320" y="1455" on="1"/>
|
||||||
|
<pt x="330" y="1425" on="0"/>
|
||||||
|
<pt x="349" y="1321" on="0"/>
|
||||||
|
<pt x="366" y="1215" on="0"/>
|
||||||
|
<pt x="370" y="1180" on="1"/>
|
||||||
|
<pt x="490" y="1343" on="0"/>
|
||||||
|
<pt x="747" y="1485" on="0"/>
|
||||||
|
<pt x="880" y="1485" on="1"/>
|
||||||
|
<pt x="1066" y="1485" on="0"/>
|
||||||
|
<pt x="1275" y="1186" on="0"/>
|
||||||
|
<pt x="1275" y="892" on="1"/>
|
||||||
|
<pt x="1275" y="145" on="1"/>
|
||||||
|
<pt x="1275" y="-174" on="0"/>
|
||||||
|
<pt x="1044" y="-470" on="0"/>
|
||||||
|
<pt x="845" y="-470" on="1"/>
|
||||||
|
<pt x="784" y="-470" on="0"/>
|
||||||
|
<pt x="674" y="-436" on="0"/>
|
||||||
|
<pt x="650" y="-420" on="1"/>
|
||||||
|
<pt x="715" y="-265" on="1"/>
|
||||||
|
<pt x="740" y="-284" on="0"/>
|
||||||
|
<pt x="824" y="-300" on="0"/>
|
||||||
|
<pt x="870" y="-300" on="1"/>
|
||||||
|
<pt x="924" y="-300" on="0"/>
|
||||||
|
<pt x="1022" y="-223" on="0"/>
|
||||||
|
<pt x="1085" y="-55" on="0"/>
|
||||||
|
<pt x="1085" y="80" on="1"/>
|
||||||
|
<pt x="1085" y="785" on="1"/>
|
||||||
|
<pt x="1085" y="964" on="0"/>
|
||||||
|
<pt x="1040" y="1184" on="0"/>
|
||||||
|
<pt x="923" y="1285" on="0"/>
|
||||||
|
<pt x="815" y="1285" on="1"/>
|
||||||
|
<pt x="743" y="1285" on="0"/>
|
||||||
|
<pt x="609" y="1202" on="0"/>
|
||||||
|
<pt x="494" y="1068" on="0"/>
|
||||||
|
<pt x="411" y="913" on="0"/>
|
||||||
|
<pt x="390" y="840" on="1"/>
|
||||||
|
<pt x="390" y="0" on="1"/>
|
||||||
|
<pt x="185" y="0" on="1"/>
|
||||||
|
<pt x="196" y="60" on="0"/>
|
||||||
|
<pt x="200" y="282" on="0"/>
|
||||||
|
<pt x="200" y="430" on="1"/>
|
||||||
|
<pt x="200" y="880" on="1"/>
|
||||||
|
<pt x="200" y="1089" on="0"/>
|
||||||
|
<pt x="156" y="1381" on="0"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="Eng.BaselineHook" xMin="135" yMin="-25" xMax="1305" yMax="1485">
|
||||||
|
<contour>
|
||||||
|
<pt x="135" y="1455" on="1"/>
|
||||||
|
<pt x="320" y="1455" on="1"/>
|
||||||
|
<pt x="330" y="1425" on="0"/>
|
||||||
|
<pt x="349" y="1321" on="0"/>
|
||||||
|
<pt x="366" y="1215" on="0"/>
|
||||||
|
<pt x="370" y="1180" on="1"/>
|
||||||
|
<pt x="490" y="1343" on="0"/>
|
||||||
|
<pt x="770" y="1485" on="0"/>
|
||||||
|
<pt x="910" y="1485" on="1"/>
|
||||||
|
<pt x="1096" y="1485" on="0"/>
|
||||||
|
<pt x="1305" y="1186" on="0"/>
|
||||||
|
<pt x="1305" y="892" on="1"/>
|
||||||
|
<pt x="1305" y="714" on="0"/>
|
||||||
|
<pt x="1305" y="590" on="0"/>
|
||||||
|
<pt x="1305" y="590" on="1"/>
|
||||||
|
<pt x="1305" y="271" on="0"/>
|
||||||
|
<pt x="1089" y="-25" on="0"/>
|
||||||
|
<pt x="905" y="-25" on="1"/>
|
||||||
|
<pt x="844" y="-25" on="0"/>
|
||||||
|
<pt x="734" y="9" on="0"/>
|
||||||
|
<pt x="710" y="25" on="1"/>
|
||||||
|
<pt x="775" y="180" on="1"/>
|
||||||
|
<pt x="800" y="161" on="0"/>
|
||||||
|
<pt x="884" y="145" on="0"/>
|
||||||
|
<pt x="930" y="145" on="1"/>
|
||||||
|
<pt x="974" y="145" on="0"/>
|
||||||
|
<pt x="1059" y="222" on="0"/>
|
||||||
|
<pt x="1115" y="390" on="0"/>
|
||||||
|
<pt x="1115" y="525" on="1"/>
|
||||||
|
<pt x="1115" y="785" on="1"/>
|
||||||
|
<pt x="1115" y="964" on="0"/>
|
||||||
|
<pt x="1070" y="1184" on="0"/>
|
||||||
|
<pt x="953" y="1285" on="0"/>
|
||||||
|
<pt x="845" y="1285" on="1"/>
|
||||||
|
<pt x="770" y="1285" on="0"/>
|
||||||
|
<pt x="625" y="1202" on="0"/>
|
||||||
|
<pt x="501" y="1068" on="0"/>
|
||||||
|
<pt x="411" y="913" on="0"/>
|
||||||
|
<pt x="390" y="840" on="1"/>
|
||||||
|
<pt x="390" y="0" on="1"/>
|
||||||
|
<pt x="185" y="0" on="1"/>
|
||||||
|
<pt x="196" y="60" on="0"/>
|
||||||
|
<pt x="200" y="282" on="0"/>
|
||||||
|
<pt x="200" y="430" on="1"/>
|
||||||
|
<pt x="200" y="880" on="1"/>
|
||||||
|
<pt x="200" y="1089" on="0"/>
|
||||||
|
<pt x="156" y="1381" on="0"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="Eng.Kom" xMin="135" yMin="-25" xMax="1305" yMax="1485">
|
||||||
|
<contour>
|
||||||
|
<pt x="135" y="1455" on="1"/>
|
||||||
|
<pt x="320" y="1455" on="1"/>
|
||||||
|
<pt x="330" y="1425" on="0"/>
|
||||||
|
<pt x="347" y="1331" on="0"/>
|
||||||
|
<pt x="362" y="1235" on="0"/>
|
||||||
|
<pt x="366" y="1200" on="1"/>
|
||||||
|
<pt x="482" y="1350" on="0"/>
|
||||||
|
<pt x="770" y="1485" on="0"/>
|
||||||
|
<pt x="910" y="1485" on="1"/>
|
||||||
|
<pt x="1096" y="1485" on="0"/>
|
||||||
|
<pt x="1305" y="1186" on="0"/>
|
||||||
|
<pt x="1305" y="892" on="1"/>
|
||||||
|
<pt x="1305" y="752" on="0"/>
|
||||||
|
<pt x="1305" y="640" on="0"/>
|
||||||
|
<pt x="1305" y="640" on="1"/>
|
||||||
|
<pt x="1305" y="403" on="0"/>
|
||||||
|
<pt x="1175" y="110" on="0"/>
|
||||||
|
<pt x="958" y="-25" on="0"/>
|
||||||
|
<pt x="825" y="-25" on="1"/>
|
||||||
|
<pt x="756" y="-25" on="0"/>
|
||||||
|
<pt x="634" y="9" on="0"/>
|
||||||
|
<pt x="610" y="25" on="1"/>
|
||||||
|
<pt x="675" y="180" on="1"/>
|
||||||
|
<pt x="700" y="161" on="0"/>
|
||||||
|
<pt x="796" y="145" on="0"/>
|
||||||
|
<pt x="850" y="145" on="1"/>
|
||||||
|
<pt x="904" y="145" on="0"/>
|
||||||
|
<pt x="1027" y="226" on="0"/>
|
||||||
|
<pt x="1115" y="415" on="0"/>
|
||||||
|
<pt x="1115" y="575" on="1"/>
|
||||||
|
<pt x="1115" y="785" on="1"/>
|
||||||
|
<pt x="1115" y="964" on="0"/>
|
||||||
|
<pt x="1070" y="1184" on="0"/>
|
||||||
|
<pt x="953" y="1285" on="0"/>
|
||||||
|
<pt x="845" y="1285" on="1"/>
|
||||||
|
<pt x="770" y="1285" on="0"/>
|
||||||
|
<pt x="626" y="1209" on="0"/>
|
||||||
|
<pt x="504" y="1085" on="0"/>
|
||||||
|
<pt x="413" y="940" on="0"/>
|
||||||
|
<pt x="390" y="870" on="1"/>
|
||||||
|
<pt x="390" y="480" on="1"/>
|
||||||
|
<pt x="185" y="480" on="1"/>
|
||||||
|
<pt x="196" y="540" on="0"/>
|
||||||
|
<pt x="200" y="750" on="0"/>
|
||||||
|
<pt x="200" y="860" on="1"/>
|
||||||
|
<pt x="200" y="880" on="1"/>
|
||||||
|
<pt x="200" y="1089" on="0"/>
|
||||||
|
<pt x="156" y="1381" on="0"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="Eng.UCStyle" xMin="160" yMin="-470" xMax="1315" yMax="1460">
|
||||||
|
<contour>
|
||||||
|
<pt x="200" y="1355" on="1"/>
|
||||||
|
<pt x="340" y="1460" on="1"/>
|
||||||
|
<pt x="1275" y="100" on="1"/>
|
||||||
|
<pt x="1135" y="0" on="1"/>
|
||||||
|
</contour>
|
||||||
|
<contour>
|
||||||
|
<pt x="340" y="1460" on="1"/>
|
||||||
|
<pt x="340" y="0" on="1"/>
|
||||||
|
<pt x="160" y="0" on="1"/>
|
||||||
|
<pt x="171" y="60" on="0"/>
|
||||||
|
<pt x="175" y="287" on="0"/>
|
||||||
|
<pt x="175" y="435" on="1"/>
|
||||||
|
<pt x="175" y="1025" on="1"/>
|
||||||
|
<pt x="175" y="1173" on="0"/>
|
||||||
|
<pt x="171" y="1400" on="0"/>
|
||||||
|
<pt x="160" y="1460" on="1"/>
|
||||||
|
</contour>
|
||||||
|
<contour>
|
||||||
|
<pt x="1135" y="1460" on="1"/>
|
||||||
|
<pt x="1315" y="1460" on="1"/>
|
||||||
|
<pt x="1305" y="1400" on="0"/>
|
||||||
|
<pt x="1300" y="1173" on="0"/>
|
||||||
|
<pt x="1300" y="1025" on="1"/>
|
||||||
|
<pt x="1300" y="25" on="1"/>
|
||||||
|
<pt x="1300" y="-161" on="0"/>
|
||||||
|
<pt x="1208" y="-378" on="0"/>
|
||||||
|
<pt x="1040" y="-470" on="0"/>
|
||||||
|
<pt x="926" y="-470" on="1"/>
|
||||||
|
<pt x="869" y="-470" on="0"/>
|
||||||
|
<pt x="734" y="-431" on="0"/>
|
||||||
|
<pt x="687" y="-395" on="1"/>
|
||||||
|
<pt x="722" y="-230" on="1"/>
|
||||||
|
<pt x="758" y="-257" on="0"/>
|
||||||
|
<pt x="875" y="-306" on="0"/>
|
||||||
|
<pt x="936" y="-306" on="1"/>
|
||||||
|
<pt x="1020" y="-306" on="0"/>
|
||||||
|
<pt x="1135" y="-165" on="0"/>
|
||||||
|
<pt x="1135" y="-25" on="1"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="eng" xMin="105" yMin="-470" xMax="1050" yMax="1040">
|
||||||
|
<contour>
|
||||||
|
<pt x="105" y="1020" on="1"/>
|
||||||
|
<pt x="285" y="1020" on="1"/>
|
||||||
|
<pt x="293" y="999" on="0"/>
|
||||||
|
<pt x="308" y="925" on="0"/>
|
||||||
|
<pt x="322" y="849" on="0"/>
|
||||||
|
<pt x="325" y="825" on="1"/>
|
||||||
|
<pt x="421" y="939" on="0"/>
|
||||||
|
<pt x="615" y="1040" on="0"/>
|
||||||
|
<pt x="720" y="1040" on="1"/>
|
||||||
|
<pt x="880" y="1040" on="0"/>
|
||||||
|
<pt x="1050" y="819" on="0"/>
|
||||||
|
<pt x="1050" y="560" on="1"/>
|
||||||
|
<pt x="1050" y="110" on="1"/>
|
||||||
|
<pt x="1050" y="-115" on="0"/>
|
||||||
|
<pt x="948" y="-367" on="0"/>
|
||||||
|
<pt x="766" y="-470" on="0"/>
|
||||||
|
<pt x="645" y="-470" on="1"/>
|
||||||
|
<pt x="600" y="-470" on="0"/>
|
||||||
|
<pt x="496" y="-440" on="0"/>
|
||||||
|
<pt x="455" y="-415" on="1"/>
|
||||||
|
<pt x="520" y="-265" on="1"/>
|
||||||
|
<pt x="572" y="-305" on="0"/>
|
||||||
|
<pt x="660" y="-305" on="1"/>
|
||||||
|
<pt x="724" y="-305" on="0"/>
|
||||||
|
<pt x="815" y="-247" on="0"/>
|
||||||
|
<pt x="865" y="-95" on="0"/>
|
||||||
|
<pt x="865" y="45" on="1"/>
|
||||||
|
<pt x="865" y="485" on="1"/>
|
||||||
|
<pt x="865" y="646" on="0"/>
|
||||||
|
<pt x="821" y="809" on="0"/>
|
||||||
|
<pt x="732" y="865" on="0"/>
|
||||||
|
<pt x="665" y="865" on="1"/>
|
||||||
|
<pt x="593" y="865" on="0"/>
|
||||||
|
<pt x="461" y="779" on="0"/>
|
||||||
|
<pt x="362" y="648" on="0"/>
|
||||||
|
<pt x="340" y="580" on="1"/>
|
||||||
|
<pt x="340" y="0" on="1"/>
|
||||||
|
<pt x="155" y="0" on="1"/>
|
||||||
|
<pt x="155" y="615" on="1"/>
|
||||||
|
<pt x="155" y="762" on="0"/>
|
||||||
|
<pt x="121" y="968" on="0"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="eng.BaselineHook.sc" xMin="129" yMin="-21" xMax="1202" yMax="1241">
|
||||||
|
<contour>
|
||||||
|
<pt x="129" y="1216" on="1"/>
|
||||||
|
<pt x="310" y="1216" on="1"/>
|
||||||
|
<pt x="322" y="1180" on="0"/>
|
||||||
|
<pt x="348" y="1044" on="0"/>
|
||||||
|
<pt x="355" y="997" on="1"/>
|
||||||
|
<pt x="463" y="1127" on="0"/>
|
||||||
|
<pt x="713" y="1241" on="0"/>
|
||||||
|
<pt x="839" y="1241" on="1"/>
|
||||||
|
<pt x="1009" y="1241" on="0"/>
|
||||||
|
<pt x="1202" y="991" on="0"/>
|
||||||
|
<pt x="1202" y="744" on="1"/>
|
||||||
|
<pt x="1202" y="495" on="1"/>
|
||||||
|
<pt x="1202" y="228" on="0"/>
|
||||||
|
<pt x="1003" y="-21" on="0"/>
|
||||||
|
<pt x="834" y="-21" on="1"/>
|
||||||
|
<pt x="777" y="-21" on="0"/>
|
||||||
|
<pt x="674" y="10" on="0"/>
|
||||||
|
<pt x="649" y="24" on="1"/>
|
||||||
|
<pt x="715" y="166" on="1"/>
|
||||||
|
<pt x="739" y="150" on="0"/>
|
||||||
|
<pt x="815" y="134" on="0"/>
|
||||||
|
<pt x="856" y="134" on="1"/>
|
||||||
|
<pt x="914" y="134" on="0"/>
|
||||||
|
<pt x="1017" y="277" on="0"/>
|
||||||
|
<pt x="1017" y="441" on="1"/>
|
||||||
|
<pt x="1017" y="655" on="1"/>
|
||||||
|
<pt x="1017" y="801" on="0"/>
|
||||||
|
<pt x="978" y="979" on="0"/>
|
||||||
|
<pt x="875" y="1061" on="0"/>
|
||||||
|
<pt x="780" y="1061" on="1"/>
|
||||||
|
<pt x="713" y="1061" on="0"/>
|
||||||
|
<pt x="585" y="993" on="0"/>
|
||||||
|
<pt x="475" y="885" on="0"/>
|
||||||
|
<pt x="394" y="759" on="0"/>
|
||||||
|
<pt x="374" y="700" on="1"/>
|
||||||
|
<pt x="374" y="0" on="1"/>
|
||||||
|
<pt x="175" y="0" on="1"/>
|
||||||
|
<pt x="186" y="56" on="0"/>
|
||||||
|
<pt x="190" y="242" on="0"/>
|
||||||
|
<pt x="190" y="363" on="1"/>
|
||||||
|
<pt x="190" y="734" on="1"/>
|
||||||
|
<pt x="190" y="906" on="0"/>
|
||||||
|
<pt x="148" y="1152" on="0"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="eng.Kom.sc" xMin="129" yMin="-21" xMax="1202" yMax="1241">
|
||||||
|
<contour>
|
||||||
|
<pt x="129" y="1216" on="1"/>
|
||||||
|
<pt x="310" y="1216" on="1"/>
|
||||||
|
<pt x="322" y="1179" on="0"/>
|
||||||
|
<pt x="344" y="1054" on="0"/>
|
||||||
|
<pt x="351" y="1009" on="1"/>
|
||||||
|
<pt x="457" y="1132" on="0"/>
|
||||||
|
<pt x="713" y="1241" on="0"/>
|
||||||
|
<pt x="839" y="1241" on="1"/>
|
||||||
|
<pt x="1009" y="1241" on="0"/>
|
||||||
|
<pt x="1202" y="991" on="0"/>
|
||||||
|
<pt x="1202" y="744" on="1"/>
|
||||||
|
<pt x="1202" y="536" on="1"/>
|
||||||
|
<pt x="1202" y="338" on="0"/>
|
||||||
|
<pt x="1083" y="92" on="0"/>
|
||||||
|
<pt x="884" y="-21" on="0"/>
|
||||||
|
<pt x="762" y="-21" on="1"/>
|
||||||
|
<pt x="698" y="-21" on="0"/>
|
||||||
|
<pt x="583" y="10" on="0"/>
|
||||||
|
<pt x="559" y="24" on="1"/>
|
||||||
|
<pt x="625" y="166" on="1"/>
|
||||||
|
<pt x="649" y="150" on="0"/>
|
||||||
|
<pt x="736" y="134" on="0"/>
|
||||||
|
<pt x="785" y="134" on="1"/>
|
||||||
|
<pt x="832" y="134" on="0"/>
|
||||||
|
<pt x="940" y="200" on="0"/>
|
||||||
|
<pt x="1017" y="353" on="0"/>
|
||||||
|
<pt x="1017" y="482" on="1"/>
|
||||||
|
<pt x="1017" y="655" on="1"/>
|
||||||
|
<pt x="1017" y="801" on="0"/>
|
||||||
|
<pt x="978" y="979" on="0"/>
|
||||||
|
<pt x="875" y="1061" on="0"/>
|
||||||
|
<pt x="780" y="1061" on="1"/>
|
||||||
|
<pt x="693" y="1061" on="0"/>
|
||||||
|
<pt x="529" y="954" on="0"/>
|
||||||
|
<pt x="404" y="798" on="0"/>
|
||||||
|
<pt x="374" y="723" on="1"/>
|
||||||
|
<pt x="374" y="396" on="1"/>
|
||||||
|
<pt x="175" y="396" on="1"/>
|
||||||
|
<pt x="186" y="452" on="0"/>
|
||||||
|
<pt x="190" y="627" on="0"/>
|
||||||
|
<pt x="190" y="717" on="1"/>
|
||||||
|
<pt x="190" y="734" on="1"/>
|
||||||
|
<pt x="190" y="906" on="0"/>
|
||||||
|
<pt x="148" y="1152" on="0"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="eng.UCStyle.sc" xMin="153" yMin="-388" xMax="1213" yMax="1222">
|
||||||
|
<contour>
|
||||||
|
<pt x="187" y="1128" on="1"/>
|
||||||
|
<pt x="325" y="1222" on="1"/>
|
||||||
|
<pt x="1178" y="88" on="1"/>
|
||||||
|
<pt x="1040" y="-2" on="1"/>
|
||||||
|
</contour>
|
||||||
|
<contour>
|
||||||
|
<pt x="329" y="1221" on="1"/>
|
||||||
|
<pt x="329" y="0" on="1"/>
|
||||||
|
<pt x="153" y="0" on="1"/>
|
||||||
|
<pt x="164" y="56" on="0"/>
|
||||||
|
<pt x="167" y="245" on="0"/>
|
||||||
|
<pt x="167" y="366" on="1"/>
|
||||||
|
<pt x="167" y="853" on="1"/>
|
||||||
|
<pt x="167" y="975" on="0"/>
|
||||||
|
<pt x="164" y="1164" on="0"/>
|
||||||
|
<pt x="153" y="1221" on="1"/>
|
||||||
|
</contour>
|
||||||
|
<contour>
|
||||||
|
<pt x="1036" y="1221" on="1"/>
|
||||||
|
<pt x="1213" y="1221" on="1"/>
|
||||||
|
<pt x="1203" y="1165" on="0"/>
|
||||||
|
<pt x="1198" y="975" on="0"/>
|
||||||
|
<pt x="1198" y="853" on="1"/>
|
||||||
|
<pt x="1198" y="28" on="1"/>
|
||||||
|
<pt x="1198" y="-129" on="0"/>
|
||||||
|
<pt x="1113" y="-311" on="0"/>
|
||||||
|
<pt x="959" y="-388" on="0"/>
|
||||||
|
<pt x="854" y="-388" on="1"/>
|
||||||
|
<pt x="802" y="-388" on="0"/>
|
||||||
|
<pt x="675" y="-353" on="0"/>
|
||||||
|
<pt x="630" y="-322" on="1"/>
|
||||||
|
<pt x="664" y="-171" on="1"/>
|
||||||
|
<pt x="699" y="-194" on="0"/>
|
||||||
|
<pt x="808" y="-238" on="0"/>
|
||||||
|
<pt x="862" y="-238" on="1"/>
|
||||||
|
<pt x="936" y="-238" on="0"/>
|
||||||
|
<pt x="1036" y="-125" on="0"/>
|
||||||
|
<pt x="1036" y="-13" on="1"/>
|
||||||
|
</contour>
|
||||||
|
<instructions/>
|
||||||
|
</TTGlyph>
|
||||||
|
|
||||||
|
<TTGlyph name="space"/><!-- contains no outline data -->
|
||||||
|
|
||||||
|
</glyf>
|
||||||
|
|
||||||
|
<name>
|
||||||
|
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Copyright (c) 2004-2022 SIL International
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Andika
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Regular
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
SIL International: Andika Regular: 2022
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Andika
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Version 6.101
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Andika
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="321" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Capital Eng
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="322" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Alternate forms of capital Eng
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="323" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Ŋ
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="324" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Lowercase no descender
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="325" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Capital form
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="326" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Lowercase short stem
|
||||||
|
</namerecord>
|
||||||
|
</name>
|
||||||
|
|
||||||
|
<post>
|
||||||
|
<formatType value="2.0"/>
|
||||||
|
<italicAngle value="0.0"/>
|
||||||
|
<underlinePosition value="-110"/>
|
||||||
|
<underlineThickness value="80"/>
|
||||||
|
<isFixedPitch value="0"/>
|
||||||
|
<minMemType42 value="0"/>
|
||||||
|
<maxMemType42 value="0"/>
|
||||||
|
<minMemType1 value="0"/>
|
||||||
|
<maxMemType1 value="0"/>
|
||||||
|
<psNames>
|
||||||
|
<!-- This file uses unique glyph names based on the information
|
||||||
|
found in the 'post' table. Since these names might not be unique,
|
||||||
|
we have to invent artificial names in case of clashes. In order to
|
||||||
|
be able to retain the original information, we need a name to
|
||||||
|
ps name mapping for those cases where they differ. That's what
|
||||||
|
you see below.
|
||||||
|
-->
|
||||||
|
</psNames>
|
||||||
|
<extraNames>
|
||||||
|
<!-- following are the name that are not taken from the standard Mac glyph order -->
|
||||||
|
<psName name="eng"/>
|
||||||
|
<psName name="Eng.UCStyle"/>
|
||||||
|
<psName name="Eng.BaselineHook"/>
|
||||||
|
<psName name="Eng"/>
|
||||||
|
<psName name="Eng.Kom"/>
|
||||||
|
<psName name="eng.BaselineHook.sc"/>
|
||||||
|
<psName name="eng.UCStyle.sc"/>
|
||||||
|
<psName name="eng.Kom.sc"/>
|
||||||
|
</extraNames>
|
||||||
|
</post>
|
||||||
|
|
||||||
|
<gasp>
|
||||||
|
<gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
|
||||||
|
</gasp>
|
||||||
|
|
||||||
|
<GDEF>
|
||||||
|
<Version value="0x00010000"/>
|
||||||
|
<GlyphClassDef>
|
||||||
|
<ClassDef glyph=".notdef" class="1"/>
|
||||||
|
<ClassDef glyph="Eng" class="1"/>
|
||||||
|
<ClassDef glyph="Eng.BaselineHook" class="1"/>
|
||||||
|
<ClassDef glyph="Eng.Kom" class="1"/>
|
||||||
|
<ClassDef glyph="Eng.UCStyle" class="1"/>
|
||||||
|
<ClassDef glyph="eng" class="1"/>
|
||||||
|
<ClassDef glyph="eng.BaselineHook.sc" class="1"/>
|
||||||
|
<ClassDef glyph="eng.Kom.sc" class="1"/>
|
||||||
|
<ClassDef glyph="eng.UCStyle.sc" class="1"/>
|
||||||
|
<ClassDef glyph="space" class="1"/>
|
||||||
|
</GlyphClassDef>
|
||||||
|
</GDEF>
|
||||||
|
|
||||||
|
<GSUB>
|
||||||
|
<Version value="0x00010000"/>
|
||||||
|
<ScriptList>
|
||||||
|
<!-- ScriptCount=3 -->
|
||||||
|
<ScriptRecord index="0">
|
||||||
|
<ScriptTag value="DFLT"/>
|
||||||
|
<Script>
|
||||||
|
<DefaultLangSys>
|
||||||
|
<ReqFeatureIndex value="65535"/>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureIndex index="0" value="0"/>
|
||||||
|
</DefaultLangSys>
|
||||||
|
<!-- LangSysCount=0 -->
|
||||||
|
</Script>
|
||||||
|
</ScriptRecord>
|
||||||
|
<ScriptRecord index="1">
|
||||||
|
<ScriptTag value="cyrl"/>
|
||||||
|
<Script>
|
||||||
|
<DefaultLangSys>
|
||||||
|
<ReqFeatureIndex value="65535"/>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureIndex index="0" value="0"/>
|
||||||
|
</DefaultLangSys>
|
||||||
|
<!-- LangSysCount=0 -->
|
||||||
|
</Script>
|
||||||
|
</ScriptRecord>
|
||||||
|
<ScriptRecord index="2">
|
||||||
|
<ScriptTag value="latn"/>
|
||||||
|
<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="cv43"/>
|
||||||
|
<Feature>
|
||||||
|
<FeatureParamsCharacterVariants Format="0">
|
||||||
|
<Format value="0"/>
|
||||||
|
<FeatUILabelNameID value="321"/> <!-- Capital Eng -->
|
||||||
|
<FeatUITooltipTextNameID value="322"/> <!-- Alternate forms of capital Eng -->
|
||||||
|
<SampleTextNameID value="323"/> <!-- Ŋ -->
|
||||||
|
<NumNamedParameters value="3"/>
|
||||||
|
<FirstParamUILabelNameID value="324"/> <!-- Lowercase no descender -->
|
||||||
|
<!-- CharCount=0 -->
|
||||||
|
</FeatureParamsCharacterVariants>
|
||||||
|
<!-- LookupCount=1 -->
|
||||||
|
<LookupListIndex index="0" value="0"/>
|
||||||
|
</Feature>
|
||||||
|
</FeatureRecord>
|
||||||
|
</FeatureList>
|
||||||
|
<LookupList>
|
||||||
|
<!-- LookupCount=1 -->
|
||||||
|
<Lookup index="0">
|
||||||
|
<LookupType value="3"/>
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<AlternateSubst index="0">
|
||||||
|
<AlternateSet glyph="Eng">
|
||||||
|
<Alternate glyph="Eng.BaselineHook"/>
|
||||||
|
<Alternate glyph="Eng.UCStyle"/>
|
||||||
|
<Alternate glyph="Eng.Kom"/>
|
||||||
|
</AlternateSet>
|
||||||
|
</AlternateSubst>
|
||||||
|
</Lookup>
|
||||||
|
</LookupList>
|
||||||
|
</GSUB>
|
||||||
|
|
||||||
|
</ttFont>
|
@ -2071,5 +2071,28 @@ def test_prune_unused_user_name_IDs_with_keep_all(ttf_path):
|
|||||||
assert nameIDs == keepNameIDs
|
assert nameIDs == keepNameIDs
|
||||||
|
|
||||||
|
|
||||||
|
def test_cvXX_feature_params_nameIDs_are_retained():
|
||||||
|
# https://github.com/fonttools/fonttools/issues/3616
|
||||||
|
font = TTFont()
|
||||||
|
ttx = pathlib.Path(__file__).parent / "data" / "Andika-Regular.subset.ttx"
|
||||||
|
font.importXML(ttx)
|
||||||
|
|
||||||
|
keepNameIDs = {n.nameID for n in font["name"].names}
|
||||||
|
|
||||||
|
options = subset.Options()
|
||||||
|
options.glyph_names = True
|
||||||
|
# that's where the FeatureParamsCharacteVariants are stored
|
||||||
|
options.layout_features.append("cv43")
|
||||||
|
|
||||||
|
subsetter = subset.Subsetter(options)
|
||||||
|
subsetter.populate(glyphs=font.getGlyphOrder())
|
||||||
|
subsetter.subset(font)
|
||||||
|
|
||||||
|
# we expect that all nameIDs are retained, including all the nameIDs
|
||||||
|
# used by the FeatureParamsCharacterVariants
|
||||||
|
nameIDs = {n.nameID for n in font["name"].names}
|
||||||
|
assert nameIDs == keepNameIDs
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(unittest.main())
|
sys.exit(unittest.main())
|
||||||
|
94
Tests/varLib/avar_test.py
Normal file
94
Tests/varLib/avar_test.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
from fontTools.ttLib import TTFont
|
||||||
|
from fontTools.varLib.models import VariationModel
|
||||||
|
from fontTools.varLib.avar import _pruneLocations, mappings_from_avar
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
TESTS = [
|
||||||
|
(
|
||||||
|
[
|
||||||
|
{"wght": 1},
|
||||||
|
{"wght": 0.5},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"wght": 0.5},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"wght": 0.5},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
{"wght": 1, "wdth": 1},
|
||||||
|
{"wght": 0.5, "wdth": 1},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"wght": 1, "wdth": 1},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"wght": 1, "wdth": 1},
|
||||||
|
{"wght": 0.5, "wdth": 1},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
{"wght": 1},
|
||||||
|
{"wdth": 1},
|
||||||
|
{"wght": 0.5, "wdth": 0.5},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"wght": 0.5, "wdth": 0.5},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{"wght": 0.5, "wdth": 0.5},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("locations, poles, expected", TESTS)
|
||||||
|
def test_pruneLocations(locations, poles, expected):
|
||||||
|
axisTags = set()
|
||||||
|
for location in locations:
|
||||||
|
axisTags.update(location.keys())
|
||||||
|
axisTags = sorted(axisTags)
|
||||||
|
|
||||||
|
locations = [{}] + locations
|
||||||
|
|
||||||
|
pruned = _pruneLocations(locations, poles, axisTags)
|
||||||
|
|
||||||
|
assert pruned == expected, (pruned, expected)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("locations, poles, expected", TESTS)
|
||||||
|
def test_roundtrip(locations, poles, expected):
|
||||||
|
axisTags = set()
|
||||||
|
for location in locations:
|
||||||
|
axisTags.update(location.keys())
|
||||||
|
axisTags = sorted(axisTags)
|
||||||
|
|
||||||
|
locations = [{}] + locations
|
||||||
|
expected = [{}] + expected
|
||||||
|
|
||||||
|
model1 = VariationModel(locations, axisTags)
|
||||||
|
model2 = VariationModel(expected, axisTags)
|
||||||
|
|
||||||
|
for location in poles:
|
||||||
|
i = model1.locations.index(location)
|
||||||
|
support1 = model1.supports[i]
|
||||||
|
|
||||||
|
i = model2.locations.index(location)
|
||||||
|
support2 = model2.supports[i]
|
||||||
|
|
||||||
|
assert support1 == support2, (support1, support2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_mappings_from_avar():
|
||||||
|
CWD = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
DATADIR = os.path.join(CWD, "..", "ttLib", "tables", "data")
|
||||||
|
varfont_path = os.path.join(DATADIR, "Amstelvar-avar2.subset.ttf")
|
||||||
|
font = TTFont(varfont_path)
|
||||||
|
mappings = mappings_from_avar(font)
|
||||||
|
|
||||||
|
assert len(mappings) == 2, mappings
|
@ -23,7 +23,7 @@
|
|||||||
<VarRegionAxis index="0">
|
<VarRegionAxis index="0">
|
||||||
<StartCoord value="0.0"/>
|
<StartCoord value="0.0"/>
|
||||||
<PeakCoord value="0.38"/>
|
<PeakCoord value="0.38"/>
|
||||||
<EndCoord value="0.38"/>
|
<EndCoord value="1.0"/>
|
||||||
</VarRegionAxis>
|
</VarRegionAxis>
|
||||||
</Region>
|
</Region>
|
||||||
</VarRegionList>
|
</VarRegionList>
|
||||||
|
@ -6,4 +6,4 @@ mypy>=0.782
|
|||||||
readme_renderer[md]>=43.0
|
readme_renderer[md]>=43.0
|
||||||
|
|
||||||
# Pin black as each version could change formatting, breaking CI randomly.
|
# Pin black as each version could change formatting, breaking CI randomly.
|
||||||
black==24.4.2
|
black==24.8.0
|
||||||
|
@ -11,10 +11,10 @@ fs==2.4.16
|
|||||||
skia-pathops==0.8.0.post1; platform_python_implementation != "PyPy"
|
skia-pathops==0.8.0.post1; platform_python_implementation != "PyPy"
|
||||||
# this is only required to run Tests/cu2qu/{ufo,cli}_test.py
|
# this is only required to run Tests/cu2qu/{ufo,cli}_test.py
|
||||||
ufoLib2==0.16.0
|
ufoLib2==0.16.0
|
||||||
ufo2ft==3.2.5
|
ufo2ft==3.2.7
|
||||||
pyobjc==10.3.1; sys_platform == "darwin"
|
pyobjc==10.3.1; sys_platform == "darwin"
|
||||||
freetype-py==2.4.0
|
freetype-py==2.4.0
|
||||||
uharfbuzz==0.39.3
|
uharfbuzz==0.39.3
|
||||||
glyphsLib==6.7.1 # this is only required to run Tests/varLib/interpolatable_test.py
|
glyphsLib==6.8.0 # this is only required to run Tests/varLib/interpolatable_test.py
|
||||||
lxml==5.2.2
|
lxml==5.3.0
|
||||||
sympy==1.13.0
|
sympy==1.13.2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user