Merge pull request #3473 from fonttools/avar2-modules
[avar2] Implement avar2 support in ttGlyphSet.
This commit is contained in:
commit
705acc994f
@ -6,6 +6,9 @@ from fontTools.misc.fixedTools import (
|
||||
strToFixedToFloat as str2fl,
|
||||
)
|
||||
from fontTools.misc.textTools import bytesjoin, safeEval
|
||||
from fontTools.misc.roundTools import otRound
|
||||
from fontTools.varLib.models import piecewiseLinearMap
|
||||
from fontTools.varLib.varStore import VarStoreInstancer, NO_VARIATION_INDEX
|
||||
from fontTools.ttLib import TTLibError
|
||||
from . import DefaultTable
|
||||
from . import otTables
|
||||
@ -74,9 +77,10 @@ class table__a_v_a_r(BaseTTXConverter):
|
||||
|
||||
def decompile(self, data, ttFont):
|
||||
super().decompile(data, ttFont)
|
||||
assert self.table.Version >= 0x00010000
|
||||
self.majorVersion = self.table.Version >> 16
|
||||
self.minorVersion = self.table.Version & 0xFFFF
|
||||
if self.majorVersion not in (1, 2):
|
||||
raise NotImplementedError("Unknown avar table version")
|
||||
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
||||
for axis in axisTags:
|
||||
self.segments[axis] = {}
|
||||
@ -136,3 +140,48 @@ class table__a_v_a_r(BaseTTXConverter):
|
||||
segment[fromValue] = toValue
|
||||
else:
|
||||
super().fromXML(name, attrs, content, ttFont)
|
||||
|
||||
def renormalizeLocation(self, location, font):
|
||||
|
||||
if self.majorVersion not in (1, 2):
|
||||
raise NotImplementedError("Unknown avar table version")
|
||||
|
||||
avarSegments = self.segments
|
||||
mappedLocation = {}
|
||||
for axisTag, value in location.items():
|
||||
avarMapping = avarSegments.get(axisTag, None)
|
||||
if avarMapping is not None:
|
||||
value = piecewiseLinearMap(value, avarMapping)
|
||||
mappedLocation[axisTag] = value
|
||||
|
||||
if self.majorVersion < 2:
|
||||
return mappedLocation
|
||||
|
||||
# Version 2
|
||||
|
||||
varIdxMap = self.table.VarIdxMap
|
||||
varStore = self.table.VarStore
|
||||
axes = font["fvar"].axes
|
||||
if varStore is not None:
|
||||
instancer = VarStoreInstancer(varStore, axes, mappedLocation)
|
||||
|
||||
coords = list(fl2fi(mappedLocation.get(axis.axisTag, 0), 14) for axis in axes)
|
||||
|
||||
out = []
|
||||
for varIdx, v in enumerate(coords):
|
||||
|
||||
if varIdxMap is not None:
|
||||
varIdx = varIdxMap[varIdx]
|
||||
|
||||
if varStore is not None:
|
||||
delta = instancer[varIdx]
|
||||
v += otRound(delta)
|
||||
v = min(max(v, -(1 << 14)), +(1 << 14))
|
||||
|
||||
out.append(v)
|
||||
|
||||
mappedLocation = {
|
||||
axis.axisTag: fi2fl(v, 14) for v, axis in zip(out, axes) if v != 0
|
||||
}
|
||||
|
||||
return mappedLocation
|
||||
|
@ -110,6 +110,9 @@ class table__f_v_a_r(DefaultTable.DefaultTable):
|
||||
instance.fromXML(name, attrs, content, ttFont)
|
||||
self.instances.append(instance)
|
||||
|
||||
def getAxes(self):
|
||||
return {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in self.axes}
|
||||
|
||||
|
||||
class Axis(object):
|
||||
def __init__(self):
|
||||
|
@ -703,6 +703,9 @@ class DeltaSetIndexMap(getFormatSwitchingBaseTableClass("uint8")):
|
||||
assert inner <= 0xFFFF
|
||||
mapping.insert(index, (outer << 16) | inner)
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.mapping[i] if i < len(self.mapping) else NO_VARIATION_INDEX
|
||||
|
||||
|
||||
class VarIdxMap(BaseTable):
|
||||
def populateDefaults(self, propagator=None):
|
||||
@ -755,6 +758,9 @@ class VarIdxMap(BaseTable):
|
||||
assert inner <= 0xFFFF
|
||||
mapping[glyph] = (outer << 16) | inner
|
||||
|
||||
def __getitem__(self, glyphName):
|
||||
return self.mapping.get(glyphName, NO_VARIATION_INDEX)
|
||||
|
||||
|
||||
class VarRegionList(BaseTable):
|
||||
def preWrite(self, font):
|
||||
|
@ -781,26 +781,15 @@ class TTFont(object):
|
||||
|
||||
Raises ``TTLibError`` if the font is not a variable font.
|
||||
"""
|
||||
from fontTools.varLib.models import normalizeLocation, piecewiseLinearMap
|
||||
from fontTools.varLib.models import normalizeLocation
|
||||
|
||||
if "fvar" not in self:
|
||||
raise TTLibError("Not a variable font")
|
||||
|
||||
axes = {
|
||||
a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
|
||||
for a in self["fvar"].axes
|
||||
}
|
||||
axes = self["fvar"].getAxes()
|
||||
location = normalizeLocation(location, axes)
|
||||
if "avar" in self:
|
||||
avar = self["avar"]
|
||||
avarSegments = avar.segments
|
||||
mappedLocation = {}
|
||||
for axisTag, value in location.items():
|
||||
avarMapping = avarSegments.get(axisTag, None)
|
||||
if avarMapping is not None:
|
||||
value = piecewiseLinearMap(value, avarMapping)
|
||||
mappedLocation[axisTag] = value
|
||||
location = mappedLocation
|
||||
location = self["avar"].renormalizeLocation(location, self)
|
||||
return location
|
||||
|
||||
def getBestCmap(
|
||||
|
@ -749,22 +749,27 @@ def main(args=None):
|
||||
if "gvar" in font:
|
||||
# Is variable font
|
||||
|
||||
axisMapping = {}
|
||||
fvar = font["fvar"]
|
||||
axisMapping = {}
|
||||
for axis in fvar.axes:
|
||||
axisMapping[axis.axisTag] = {
|
||||
-1: axis.minValue,
|
||||
0: axis.defaultValue,
|
||||
1: axis.maxValue,
|
||||
}
|
||||
normalized = False
|
||||
if "avar" in font:
|
||||
avar = font["avar"]
|
||||
for axisTag, segments in avar.segments.items():
|
||||
fvarMapping = axisMapping[axisTag].copy()
|
||||
for location, value in segments.items():
|
||||
axisMapping[axisTag][value] = piecewiseLinearMap(
|
||||
location, fvarMapping
|
||||
)
|
||||
if getattr(avar.table, "VarStore", None):
|
||||
axisMapping = {tag: {-1: -1, 0: 0, 1: 1} for tag in axisMapping}
|
||||
normalized = True
|
||||
else:
|
||||
for axisTag, segments in avar.segments.items():
|
||||
fvarMapping = axisMapping[axisTag].copy()
|
||||
for location, value in segments.items():
|
||||
axisMapping[axisTag][value] = piecewiseLinearMap(
|
||||
location, fvarMapping
|
||||
)
|
||||
|
||||
gvar = font["gvar"]
|
||||
glyf = font["glyf"]
|
||||
@ -811,6 +816,8 @@ def main(args=None):
|
||||
)
|
||||
+ "'"
|
||||
)
|
||||
if normalized:
|
||||
name += " (normalized)"
|
||||
names.append(name)
|
||||
fonts.append(glyphsets[locTuple])
|
||||
locations.append(dict(locTuple))
|
||||
|
@ -2,6 +2,7 @@ from fontTools.misc.testTools import parseXML
|
||||
from fontTools.misc.textTools import deHexStr
|
||||
from fontTools.misc.xmlWriter import XMLWriter
|
||||
from fontTools.misc.fixedTools import floatToFixed as fl2fi
|
||||
from fontTools.pens.statisticsPen import StatisticsPen
|
||||
from fontTools.ttLib import TTFont, TTLibError
|
||||
import fontTools.ttLib.tables.otTables as otTables
|
||||
from fontTools.ttLib.tables._a_v_a_r import table__a_v_a_r
|
||||
@ -9,9 +10,12 @@ from fontTools.ttLib.tables._f_v_a_r import table__f_v_a_r, Axis
|
||||
import fontTools.varLib.models as models
|
||||
import fontTools.varLib.varStore as varStore
|
||||
from io import BytesIO
|
||||
import os
|
||||
import unittest
|
||||
|
||||
|
||||
DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data")
|
||||
|
||||
TEST_DATA = deHexStr(
|
||||
"00 01 00 00 00 00 00 02 "
|
||||
"00 04 C0 00 C0 00 00 00 00 00 13 33 33 33 40 00 40 00 "
|
||||
@ -100,7 +104,7 @@ class AxisVariationTableTest(unittest.TestCase):
|
||||
|
||||
|
||||
class Avar2Test(unittest.TestCase):
|
||||
def test(self):
|
||||
def test_roundtrip(self):
|
||||
axisTags = ["wght", "wdth"]
|
||||
fvar = table__f_v_a_r()
|
||||
for tag in axisTags:
|
||||
@ -173,6 +177,28 @@ class Avar2Test(unittest.TestCase):
|
||||
assert avar.table.VarStore.VarRegionList.RegionAxisCount == 2
|
||||
assert avar.table.VarStore.VarRegionList.RegionCount == 1
|
||||
|
||||
def test_ttGlyphSet(self):
|
||||
ttf = os.path.join(DATA_DIR, "Amstelvar-avar2.subset.ttf")
|
||||
font = TTFont(ttf)
|
||||
|
||||
regular = font.getGlyphSet()
|
||||
black = font.getGlyphSet(location={"wght": 900})
|
||||
expanded = font.getGlyphSet(location={"wdth": 125})
|
||||
|
||||
regularStats = StatisticsPen()
|
||||
blackStats = StatisticsPen()
|
||||
expandedStats = StatisticsPen()
|
||||
|
||||
for glyph in "Test":
|
||||
regular[glyph].draw(regularStats)
|
||||
black[glyph].draw(blackStats)
|
||||
expanded[glyph].draw(expandedStats)
|
||||
|
||||
assert abs(regularStats.area) < abs(blackStats.area)
|
||||
assert abs(expandedStats.area) < abs(blackStats.area)
|
||||
|
||||
assert regularStats.meanX < expandedStats.meanX
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
BIN
Tests/ttLib/tables/data/Amstelvar-avar2.subset.ttf
Normal file
BIN
Tests/ttLib/tables/data/Amstelvar-avar2.subset.ttf
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user