commit
05872d6878
@ -5,29 +5,20 @@ from fontTools.misc.fixedTools import (
|
||||
floatToFixedToStr as fl2str,
|
||||
strToFixedToFloat as str2fl,
|
||||
)
|
||||
from fontTools.misc.textTools import bytesjoin
|
||||
from fontTools.misc.textTools import bytesjoin, safeEval
|
||||
from fontTools.ttLib import TTLibError
|
||||
from . import DefaultTable
|
||||
from . import otTables
|
||||
import struct
|
||||
import logging
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Apple's documentation of 'avar':
|
||||
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html
|
||||
|
||||
AVAR_HEADER_FORMAT = """
|
||||
> # big endian
|
||||
majorVersion: H
|
||||
minorVersion: H
|
||||
reserved: H
|
||||
axisCount: H
|
||||
"""
|
||||
assert sstruct.calcsize(AVAR_HEADER_FORMAT) == 8, sstruct.calcsize(AVAR_HEADER_FORMAT)
|
||||
from .otBase import BaseTTXConverter
|
||||
|
||||
|
||||
class table__a_v_a_r(DefaultTable.DefaultTable):
|
||||
class table__a_v_a_r(BaseTTXConverter):
|
||||
"""Axis Variations Table
|
||||
|
||||
This class represents the ``avar`` table of a variable font. The object has one
|
||||
@ -54,46 +45,53 @@ class table__a_v_a_r(DefaultTable.DefaultTable):
|
||||
dependencies = ["fvar"]
|
||||
|
||||
def __init__(self, tag=None):
|
||||
DefaultTable.DefaultTable.__init__(self, tag)
|
||||
super().__init__(tag)
|
||||
self.segments = {}
|
||||
|
||||
def compile(self, ttFont):
|
||||
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
||||
header = {
|
||||
"majorVersion": 1,
|
||||
"minorVersion": 0,
|
||||
"reserved": 0,
|
||||
"axisCount": len(axisTags),
|
||||
}
|
||||
result = [sstruct.pack(AVAR_HEADER_FORMAT, header)]
|
||||
if not hasattr(self, "table"):
|
||||
self.table = otTables.avar()
|
||||
if not hasattr(self.table, "Reserved"):
|
||||
self.table.Reserved = 0
|
||||
self.table.Version = (getattr(self, "majorVersion", 1) << 16) | getattr(
|
||||
self, "minorVersion", 0
|
||||
)
|
||||
self.table.AxisCount = len(axisTags)
|
||||
self.table.AxisSegmentMap = []
|
||||
for axis in axisTags:
|
||||
mappings = sorted(self.segments[axis].items())
|
||||
result.append(struct.pack(">H", len(mappings)))
|
||||
for key, value in mappings:
|
||||
fixedKey = fl2fi(key, 14)
|
||||
fixedValue = fl2fi(value, 14)
|
||||
result.append(struct.pack(">hh", fixedKey, fixedValue))
|
||||
return bytesjoin(result)
|
||||
mappings = self.segments[axis]
|
||||
segmentMap = otTables.AxisSegmentMap()
|
||||
segmentMap.PositionMapCount = len(mappings)
|
||||
segmentMap.AxisValueMap = []
|
||||
for key, value in sorted(mappings.items()):
|
||||
valueMap = otTables.AxisValueMap()
|
||||
valueMap.FromCoordinate = key
|
||||
valueMap.ToCoordinate = value
|
||||
segmentMap.AxisValueMap.append(valueMap)
|
||||
self.table.AxisSegmentMap.append(segmentMap)
|
||||
return super().compile(ttFont)
|
||||
|
||||
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
|
||||
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
||||
header = {}
|
||||
headerSize = sstruct.calcsize(AVAR_HEADER_FORMAT)
|
||||
header = sstruct.unpack(AVAR_HEADER_FORMAT, data[0:headerSize])
|
||||
majorVersion = header["majorVersion"]
|
||||
if majorVersion != 1:
|
||||
raise TTLibError("unsupported 'avar' version %d" % majorVersion)
|
||||
pos = headerSize
|
||||
for axis in axisTags:
|
||||
self.segments[axis] = {}
|
||||
for axis, segmentMap in zip(axisTags, self.table.AxisSegmentMap):
|
||||
segments = self.segments[axis] = {}
|
||||
numPairs = struct.unpack(">H", data[pos : pos + 2])[0]
|
||||
pos = pos + 2
|
||||
for _ in range(numPairs):
|
||||
fromValue, toValue = struct.unpack(">hh", data[pos : pos + 4])
|
||||
segments[fi2fl(fromValue, 14)] = fi2fl(toValue, 14)
|
||||
pos = pos + 4
|
||||
for segment in segmentMap.AxisValueMap:
|
||||
segments[segment.FromCoordinate] = segment.ToCoordinate
|
||||
|
||||
def toXML(self, writer, ttFont):
|
||||
writer.simpletag(
|
||||
"version",
|
||||
major=getattr(self, "majorVersion", 1),
|
||||
minor=getattr(self, "minorVersion", 0),
|
||||
)
|
||||
writer.newline()
|
||||
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
||||
for axis in axisTags:
|
||||
writer.begintag("segment", axis=axis)
|
||||
@ -105,9 +103,24 @@ class table__a_v_a_r(DefaultTable.DefaultTable):
|
||||
writer.newline()
|
||||
writer.endtag("segment")
|
||||
writer.newline()
|
||||
if getattr(self, "majorVersion", 1) >= 2:
|
||||
if self.table.VarIdxMap:
|
||||
self.table.VarIdxMap.toXML(writer, ttFont, name="VarIdxMap")
|
||||
if self.table.VarStore:
|
||||
self.table.VarStore.toXML(writer, ttFont)
|
||||
|
||||
def fromXML(self, name, attrs, content, ttFont):
|
||||
if name == "segment":
|
||||
if not hasattr(self, "table"):
|
||||
self.table = otTables.avar()
|
||||
if not hasattr(self.table, "Reserved"):
|
||||
self.table.Reserved = 0
|
||||
if name == "version":
|
||||
self.majorVersion = safeEval(attrs["major"])
|
||||
self.minorVersion = safeEval(attrs["minor"])
|
||||
self.table.Version = (getattr(self, "majorVersion", 1) << 16) | getattr(
|
||||
self, "minorVersion", 0
|
||||
)
|
||||
elif name == "segment":
|
||||
axis = attrs["axis"]
|
||||
segment = self.segments[axis] = {}
|
||||
for element in content:
|
||||
@ -121,3 +134,5 @@ class table__a_v_a_r(DefaultTable.DefaultTable):
|
||||
"duplicate entry for %s in axis '%s'", fromValue, axis
|
||||
)
|
||||
segment[fromValue] = toValue
|
||||
else:
|
||||
super().fromXML(name, attrs, content, ttFont)
|
||||
|
@ -571,12 +571,10 @@ class Version(SimpleValue):
|
||||
|
||||
def read(self, reader, font, tableDict):
|
||||
value = reader.readLong()
|
||||
assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
|
||||
return value
|
||||
|
||||
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
||||
value = fi2ve(value)
|
||||
assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
|
||||
writer.writeLong(value)
|
||||
|
||||
@staticmethod
|
||||
|
76
Lib/fontTools/ttLib/tables/otData.py
Executable file → Normal file
76
Lib/fontTools/ttLib/tables/otData.py
Executable file → Normal file
@ -6157,4 +6157,80 @@ otData = [
|
||||
),
|
||||
],
|
||||
),
|
||||
#
|
||||
# avar
|
||||
#
|
||||
(
|
||||
"AxisValueMap",
|
||||
[
|
||||
(
|
||||
"F2Dot14",
|
||||
"FromCoordinate",
|
||||
None,
|
||||
None,
|
||||
"A normalized coordinate value obtained using default normalization",
|
||||
),
|
||||
(
|
||||
"F2Dot14",
|
||||
"ToCoordinate",
|
||||
None,
|
||||
None,
|
||||
"The modified, normalized coordinate value",
|
||||
),
|
||||
],
|
||||
),
|
||||
(
|
||||
"AxisSegmentMap",
|
||||
[
|
||||
(
|
||||
"uint16",
|
||||
"PositionMapCount",
|
||||
None,
|
||||
None,
|
||||
"The number of correspondence pairs for this axis",
|
||||
),
|
||||
(
|
||||
"AxisValueMap",
|
||||
"AxisValueMap",
|
||||
"PositionMapCount",
|
||||
0,
|
||||
"The array of axis value map records for this axis",
|
||||
),
|
||||
],
|
||||
),
|
||||
(
|
||||
"avar",
|
||||
[
|
||||
(
|
||||
"Version",
|
||||
"Version",
|
||||
None,
|
||||
None,
|
||||
"Version of the avar table- 0x00010000 or 0x00020000",
|
||||
),
|
||||
("uint16", "Reserved", None, None, "Permanently reserved; set to zero"),
|
||||
(
|
||||
"uint16",
|
||||
"AxisCount",
|
||||
None,
|
||||
None,
|
||||
'The number of variation axes for this font. This must be the same number as axisCount in the "fvar" table',
|
||||
),
|
||||
(
|
||||
"AxisSegmentMap",
|
||||
"AxisSegmentMap",
|
||||
"AxisCount",
|
||||
0,
|
||||
'The segment maps array — one segment map for each axis, in the order of axes specified in the "fvar" table',
|
||||
),
|
||||
(
|
||||
"LOffsetTo(DeltaSetIndexMap)",
|
||||
"VarIdxMap",
|
||||
None,
|
||||
"Version >= 0x00020000",
|
||||
"",
|
||||
),
|
||||
("LOffset", "VarStore", None, "Version >= 0x00020000", ""),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
@ -637,6 +637,7 @@
|
||||
</STAT>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -1809,6 +1809,7 @@
|
||||
</VORG>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -378,6 +378,7 @@
|
||||
</GSUB>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -406,6 +406,7 @@
|
||||
</VVAR>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -129,6 +129,7 @@
|
||||
</VVAR>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -140,6 +140,7 @@
|
||||
</VVAR>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -9,6 +9,7 @@
|
||||
</GlyphOrder>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -8,6 +8,7 @@
|
||||
</GlyphOrder>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -2668,6 +2668,7 @@
|
||||
</STAT>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.36">
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.5" to="-0.75"/>
|
||||
|
@ -417,6 +417,7 @@
|
||||
</post>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="0000">
|
||||
</segment>
|
||||
<segment axis="0001">
|
||||
|
@ -1,9 +1,13 @@
|
||||
from fontTools.misc.testTools import parseXML
|
||||
from fontTools.misc.textTools import deHexStr
|
||||
from fontTools.misc.xmlWriter import XMLWriter
|
||||
from fontTools.ttLib import TTLibError
|
||||
from fontTools.misc.fixedTools import floatToFixed as fl2fi
|
||||
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
|
||||
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 unittest
|
||||
|
||||
@ -44,13 +48,6 @@ class AxisVariationTableTest(unittest.TestCase):
|
||||
avar.segments,
|
||||
)
|
||||
|
||||
def test_decompile_unsupportedVersion(self):
|
||||
avar = table__a_v_a_r()
|
||||
font = self.makeFont(["wdth", "wght"])
|
||||
self.assertRaises(
|
||||
TTLibError, avar.decompile, deHexStr("02 01 03 06 00 00 00 00"), font
|
||||
)
|
||||
|
||||
def test_toXML(self):
|
||||
avar = table__a_v_a_r()
|
||||
avar.segments["opsz"] = {-1.0: -1.0, 0.0: 0.0, 0.2999878: 0.7999878, 1.0: 1.0}
|
||||
@ -58,6 +55,7 @@ class AxisVariationTableTest(unittest.TestCase):
|
||||
avar.toXML(writer, self.makeFont(["opsz"]))
|
||||
self.assertEqual(
|
||||
[
|
||||
'<version major="1" minor="0"/>',
|
||||
'<segment axis="opsz">',
|
||||
'<mapping from="-1.0" to="-1.0"/>',
|
||||
'<mapping from="0.0" to="0.0"/>',
|
||||
@ -91,7 +89,9 @@ class AxisVariationTableTest(unittest.TestCase):
|
||||
axis = Axis()
|
||||
axis.axisTag = tag
|
||||
fvar.axes.append(axis)
|
||||
return {"fvar": fvar}
|
||||
font = TTFont()
|
||||
font["fvar"] = fvar
|
||||
return font
|
||||
|
||||
@staticmethod
|
||||
def xml_lines(writer):
|
||||
@ -99,6 +99,82 @@ class AxisVariationTableTest(unittest.TestCase):
|
||||
return [line.strip() for line in content.splitlines()][1:]
|
||||
|
||||
|
||||
class Avar2Test(unittest.TestCase):
|
||||
def test(self):
|
||||
|
||||
axisTags = ["wght", "wdth"]
|
||||
fvar = table__f_v_a_r()
|
||||
for tag in axisTags:
|
||||
axis = Axis()
|
||||
axis.axisTag = tag
|
||||
fvar.axes.append(axis)
|
||||
|
||||
master_locations_normalized = [
|
||||
{},
|
||||
{"wght": 1, "wdth": -1},
|
||||
]
|
||||
data = [
|
||||
{},
|
||||
{"wdth": -0.8},
|
||||
]
|
||||
|
||||
model = models.VariationModel(master_locations_normalized, axisTags)
|
||||
store_builder = varStore.OnlineVarStoreBuilder(axisTags)
|
||||
store_builder.setModel(model)
|
||||
varIdxes = {}
|
||||
for axis in axisTags:
|
||||
masters = [fl2fi(m.get(axis, 0), 14) for m in data]
|
||||
varIdxes[axis] = store_builder.storeMasters(masters)[1]
|
||||
store = store_builder.finish()
|
||||
mapping = store.optimize()
|
||||
varIdxes = {axis: mapping[value] for axis, value in varIdxes.items()}
|
||||
del model, store_builder, mapping
|
||||
|
||||
varIdxMap = otTables.DeltaSetIndexMap()
|
||||
varIdxMap.Format = 1
|
||||
varIdxMap.mapping = []
|
||||
for tag in axisTags:
|
||||
varIdxMap.mapping.append(varIdxes[tag])
|
||||
|
||||
avar = table__a_v_a_r()
|
||||
avar.segments["wght"] = {}
|
||||
avar.segments["wdth"] = {-1.0: -1.0, 0.0: 0.0, 0.4: 0.5, 1.0: 1.0}
|
||||
|
||||
avar.majorVersion = 2
|
||||
avar.table = otTables.avar()
|
||||
avar.table.VarIdxMap = varIdxMap
|
||||
avar.table.VarStore = store
|
||||
|
||||
font = TTFont()
|
||||
font["fvar"] = fvar
|
||||
font["avar"] = avar
|
||||
|
||||
b = BytesIO()
|
||||
font.save(b)
|
||||
b.seek(0)
|
||||
font2 = TTFont(b)
|
||||
|
||||
assert font2["avar"].table.VarStore.VarRegionList.RegionAxisCount == 2
|
||||
assert font2["avar"].table.VarStore.VarRegionList.RegionCount == 1
|
||||
|
||||
xml1 = BytesIO()
|
||||
writer = XMLWriter(xml1)
|
||||
font["avar"].toXML(writer, font)
|
||||
|
||||
xml2 = BytesIO()
|
||||
writer = XMLWriter(xml2)
|
||||
font2["avar"].toXML(writer, font2)
|
||||
|
||||
assert xml1.getvalue() == xml2.getvalue(), (xml1.getvalue(), xml2.getvalue())
|
||||
|
||||
avar = table__a_v_a_r()
|
||||
xml = b"".join(xml2.getvalue().splitlines()[1:])
|
||||
for name, attrs, content in parseXML(xml):
|
||||
avar.fromXML(name, attrs, content, ttFont=TTFont())
|
||||
assert avar.table.VarStore.VarRegionList.RegionAxisCount == 2
|
||||
assert avar.table.VarStore.VarRegionList.RegionCount == 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
|
@ -761,6 +761,7 @@
|
||||
</STAT>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.5" to="-0.7283"/>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="0.0" to="0.0"/>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.6667" to="-0.7969"/>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.6667" to="-0.7969"/>
|
||||
|
@ -787,6 +787,7 @@
|
||||
</STAT>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.6667" to="-0.7969"/>
|
||||
|
@ -1139,6 +1139,7 @@
|
||||
</STAT>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.6667" to="-0.7969"/>
|
||||
|
@ -1336,6 +1336,7 @@
|
||||
</STAT>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="wght">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.6667" to="-0.74194"/>
|
||||
|
@ -213,6 +213,7 @@
|
||||
</GPOS>
|
||||
|
||||
<avar>
|
||||
<version major="1" minor="0"/>
|
||||
<segment axis="opsz">
|
||||
<mapping from="-1.0" to="-1.0"/>
|
||||
<mapping from="-0.01" to="-0.9"/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user