diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index e8ee6fc38..d149c3c64 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -2368,8 +2368,10 @@ def prune_pre_subset(self, font, options):
nameIDs = set(options.name_IDs)
fvar = font.get('fvar')
if fvar:
- nameIDs.update([inst.nameID for inst in fvar.instances])
- nameIDs.update([axis.nameID for axis in fvar.axes])
+ nameIDs.update([axis.axisNameID for axis in fvar.axes])
+ nameIDs.update([inst.subfamilyNameID for inst in fvar.instances])
+ nameIDs.update([inst.postscriptNameID for inst in fvar.instances
+ if inst.postscriptNameID != 0xFFFF])
if '*' not in options.name_IDs:
self.names = [n for n in self.names if n.nameID in nameIDs]
if not options.name_legacy:
diff --git a/Lib/fontTools/subset/testdata/TestGVAR.ttx b/Lib/fontTools/subset/testdata/TestGVAR.ttx
index d4d58f42b..36c29d33d 100644
--- a/Lib/fontTools/subset/testdata/TestGVAR.ttx
+++ b/Lib/fontTools/subset/testdata/TestGVAR.ttx
@@ -394,31 +394,31 @@
100.0
400.0
900.0
- 257
+ 257
-
+
-
+
-
+
-
+
-
+
diff --git a/Lib/fontTools/subset/testdata/expect_keep_gvar.ttx b/Lib/fontTools/subset/testdata/expect_keep_gvar.ttx
index 5088bdf48..2acce68c4 100644
--- a/Lib/fontTools/subset/testdata/expect_keep_gvar.ttx
+++ b/Lib/fontTools/subset/testdata/expect_keep_gvar.ttx
@@ -25,31 +25,31 @@
100.0
400.0
900.0
- 257
+ 257
-
+
-
+
-
+
-
+
-
+
diff --git a/Lib/fontTools/subset/testdata/expect_keep_gvar_notdef_outline.ttx b/Lib/fontTools/subset/testdata/expect_keep_gvar_notdef_outline.ttx
index b453a636d..a9f2a1490 100644
--- a/Lib/fontTools/subset/testdata/expect_keep_gvar_notdef_outline.ttx
+++ b/Lib/fontTools/subset/testdata/expect_keep_gvar_notdef_outline.ttx
@@ -24,31 +24,31 @@
100.0
400.0
900.0
- 257
+ 257
-
+
-
+
-
+
-
+
-
+
diff --git a/Lib/fontTools/ttLib/tables/_f_v_a_r.py b/Lib/fontTools/ttLib/tables/_f_v_a_r.py
index 3d64c4589..ec64a1c55 100644
--- a/Lib/fontTools/ttLib/tables/_f_v_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_f_v_a_r.py
@@ -29,12 +29,12 @@ FVAR_AXIS_FORMAT = """
defaultValue: 16.16F
maxValue: 16.16F
flags: H
- nameID: H
+ axisNameID: H
"""
FVAR_INSTANCE_FORMAT = """
> # big endian
- nameID: H
+ subfamilyNameID: H
flags: H
"""
@@ -47,6 +47,9 @@ class table__f_v_a_r(DefaultTable.DefaultTable):
self.instances = []
def compile(self, ttFont):
+ instanceSize = sstruct.calcsize(FVAR_INSTANCE_FORMAT) + (len(self.axes) * 4)
+ if any(instance.postscriptNameID != 0xFFFF for instance in self.instances):
+ instanceSize += 2
header = {
"version": 0x00010000,
"offsetToData": sstruct.calcsize(FVAR_HEADER_FORMAT),
@@ -54,12 +57,12 @@ class table__f_v_a_r(DefaultTable.DefaultTable):
"axisCount": len(self.axes),
"axisSize": sstruct.calcsize(FVAR_AXIS_FORMAT),
"instanceCount": len(self.instances),
- "instanceSize": sstruct.calcsize(FVAR_INSTANCE_FORMAT) + len(self.axes) * 4
+ "instanceSize": instanceSize,
}
result = [sstruct.pack(FVAR_HEADER_FORMAT, header)]
result.extend([axis.compile() for axis in self.axes])
axisTags = [axis.axisTag for axis in self.axes]
- result.extend([instance.compile(axisTags) for instance in self.instances])
+ result.extend([instance.compile(axisTags)[:instanceSize] for instance in self.instances])
return bytesjoin(result)
def decompile(self, data, ttFont):
@@ -102,7 +105,7 @@ class table__f_v_a_r(DefaultTable.DefaultTable):
class Axis(object):
def __init__(self):
self.axisTag = None
- self.nameID = 0
+ self.axisNameID = 0
self.flags = 0 # not exposed in XML because spec defines no values
self.minValue = -1.0
self.defaultValue = 0.0
@@ -115,7 +118,7 @@ class Axis(object):
sstruct.unpack2(FVAR_AXIS_FORMAT, data, self)
def toXML(self, writer, ttFont):
- name = ttFont["name"].getDebugName(self.nameID)
+ name = ttFont["name"].getDebugName(self.axisNameID)
if name is not None:
writer.newline()
writer.comment(name)
@@ -126,7 +129,7 @@ class Axis(object):
("MinValue", str(self.minValue)),
("DefaultValue", str(self.defaultValue)),
("MaxValue", str(self.maxValue)),
- ("NameID", str(self.nameID))]:
+ ("AxisNameID", str(self.axisNameID))]:
writer.begintag(tag)
writer.write(value)
writer.endtag(tag)
@@ -140,12 +143,13 @@ class Axis(object):
value = ''.join(value)
if tag == "AxisTag":
self.axisTag = Tag(value)
- elif tag in ["MinValue", "DefaultValue", "MaxValue", "NameID"]:
+ elif tag in ["MinValue", "DefaultValue", "MaxValue", "AxisNameID"]:
setattr(self, tag[0].lower() + tag[1:], safeEval(value))
class NamedInstance(object):
def __init__(self):
- self.nameID = 0
+ self.subfamilyNameID = 0
+ self.postscriptNameID = 0xFFFF
self.flags = 0 # not exposed in XML because spec defines no values
self.coordinates = {}
@@ -154,6 +158,7 @@ class NamedInstance(object):
for axis in axisTags:
fixedCoord = floatToFixed(self.coordinates[axis], 16)
result.append(struct.pack(">l", fixedCoord))
+ result.append(struct.pack(">H", self.postscriptNameID))
return bytesjoin(result)
def decompile(self, data, axisTags):
@@ -163,14 +168,22 @@ class NamedInstance(object):
value = struct.unpack(">l", data[pos : pos + 4])[0]
self.coordinates[axis] = fixedToFloat(value, 16)
pos += 4
+ if pos + 2 <= len(data):
+ self.postscriptNameID = struct.unpack(">H", data[pos : pos + 2])[0]
+ else:
+ self.postscriptNameID = 0xFFFF
def toXML(self, writer, ttFont):
- name = ttFont["name"].getDebugName(self.nameID)
+ name = ttFont["name"].getDebugName(self.subfamilyNameID)
if name is not None:
writer.newline()
writer.comment(name)
writer.newline()
- writer.begintag("NamedInstance", nameID=self.nameID)
+ if self.postscriptNameID == 0xFFFF:
+ writer.begintag("NamedInstance", subfamilyNameID=self.subfamilyNameID)
+ else:
+ writer.begintag("NamedInstance", subfamilyNameID=self.subfamilyNameID,
+ postscriptNameID=self.postscriptNameID, )
writer.newline()
for axis in ttFont["fvar"].axes:
writer.simpletag("coord", axis=axis.axisTag,
@@ -181,7 +194,12 @@ class NamedInstance(object):
def fromXML(self, name, attrs, content, ttFont):
assert(name == "NamedInstance")
- self.nameID = safeEval(attrs["nameID"])
+ self.subfamilyNameID = safeEval(attrs["subfamilyNameID"])
+ if "postscriptNameID" in attrs:
+ self.postscriptNameID = safeEval(attrs["postscriptNameID"])
+ else:
+ self.postscriptNameID = 0xFFFF
+
for tag, elementAttrs, _ in filter(lambda t: type(t) is tuple, content):
if tag == "coord":
self.coordinates[elementAttrs["axis"]] = safeEval(elementAttrs["value"])
diff --git a/Lib/fontTools/ttLib/tables/_f_v_a_r_test.py b/Lib/fontTools/ttLib/tables/_f_v_a_r_test.py
index dd9894aff..290cc5298 100644
--- a/Lib/fontTools/ttLib/tables/_f_v_a_r_test.py
+++ b/Lib/fontTools/ttLib/tables/_f_v_a_r_test.py
@@ -20,7 +20,7 @@ FVAR_DATA = deHexStr(
FVAR_AXIS_DATA = deHexStr(
"6F 70 73 7a ff ff 80 00 00 01 4c cd 00 01 80 00 00 00 01 59")
-FVAR_INSTANCE_DATA = deHexStr("01 59 00 00 00 00 b3 33 00 00 80 00")
+FVAR_INSTANCE_DATA = deHexStr("01 59 00 00 00 00 b3 33 00 00 80 00 ff ff")
def xml_lines(writer):
@@ -51,11 +51,11 @@ def MakeFont():
axis.axisTag = tag
axis.defaultValue = defaultValue
axis.minValue, axis.maxValue = minValue, maxValue
- axis.nameID = AddName(font, name).nameID
+ axis.axisNameID = AddName(font, name).nameID
fvarTable.axes.append(axis)
for name, weight, width in instances:
inst = NamedInstance()
- inst.nameID = AddName(font, name).nameID
+ inst.subfamilyNameID = AddName(font, name).nameID
inst.coordinates = {"wght": weight, "wdth": width}
fvarTable.instances.append(inst)
return font
@@ -71,7 +71,7 @@ class FontVariationTableTest(unittest.TestCase):
fvar = table__f_v_a_r()
fvar.decompile(FVAR_DATA, ttFont={"fvar": fvar})
self.assertEqual(["wght", "wdth"], [a.axisTag for a in fvar.axes])
- self.assertEqual([259, 260], [i.nameID for i in fvar.instances])
+ self.assertEqual([259, 260], [i.subfamilyNameID for i in fvar.instances])
def test_toXML(self):
font = MakeFont()
@@ -94,17 +94,17 @@ class FontVariationTableTest(unittest.TestCase):
''
' slnt'
''
- ''
- ''):
+ ''
+ ''):
fvar.fromXML(name, attrs, content, ttFont=None)
self.assertEqual(["opsz", "slnt"], [a.axisTag for a in fvar.axes])
- self.assertEqual([765, 234], [i.nameID for i in fvar.instances])
+ self.assertEqual([765, 234], [i.subfamilyNameID for i in fvar.instances])
class AxisTest(unittest.TestCase):
def test_compile(self):
axis = Axis()
- axis.axisTag, axis.nameID = ('opsz', 345)
+ axis.axisTag, axis.axisNameID = ('opsz', 345)
axis.minValue, axis.defaultValue, axis.maxValue = (-0.5, 1.3, 1.5)
self.assertEqual(FVAR_AXIS_DATA, axis.compile())
@@ -112,7 +112,7 @@ class AxisTest(unittest.TestCase):
axis = Axis()
axis.decompile(FVAR_AXIS_DATA)
self.assertEqual("opsz", axis.axisTag)
- self.assertEqual(345, axis.nameID)
+ self.assertEqual(345, axis.axisNameID)
self.assertEqual(-0.5, axis.minValue)
self.assertEqual(1.3, axis.defaultValue)
self.assertEqual(1.5, axis.maxValue)
@@ -122,7 +122,7 @@ class AxisTest(unittest.TestCase):
axis = Axis()
axis.decompile(FVAR_AXIS_DATA)
AddName(font, "Optical Size").nameID = 256
- axis.nameID = 256
+ axis.axisNameID = 256
writer = XMLWriter(BytesIO())
axis.toXML(writer, font)
self.assertEqual([
@@ -133,7 +133,7 @@ class AxisTest(unittest.TestCase):
'-0.5',
'1.3',
'1.5',
- '256',
+ '256',
''
], xml_lines(writer))
@@ -145,40 +145,40 @@ class AxisTest(unittest.TestCase):
' 100'
' 400'
' 900'
- ' 256'
+ ' 256'
''):
axis.fromXML(name, attrs, content, ttFont=None)
self.assertEqual("wght", axis.axisTag)
self.assertEqual(100, axis.minValue)
self.assertEqual(400, axis.defaultValue)
self.assertEqual(900, axis.maxValue)
- self.assertEqual(256, axis.nameID)
+ self.assertEqual(256, axis.axisNameID)
class NamedInstanceTest(unittest.TestCase):
def test_compile(self):
inst = NamedInstance()
- inst.nameID = 345
+ inst.subfamilyNameID = 345
inst.coordinates = {"wght": 0.7, "wdth": 0.5}
self.assertEqual(FVAR_INSTANCE_DATA, inst.compile(["wght", "wdth"]))
def test_decompile(self):
inst = NamedInstance()
inst.decompile(FVAR_INSTANCE_DATA, ["wght", "wdth"])
- self.assertEqual(345, inst.nameID)
+ self.assertEqual(345, inst.subfamilyNameID)
self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
def test_toXML(self):
font = MakeFont()
inst = NamedInstance()
- inst.nameID = AddName(font, "Light Condensed").nameID
+ inst.subfamilyNameID = AddName(font, "Light Condensed").nameID
inst.coordinates = {"wght": 0.7, "wdth": 0.5}
writer = XMLWriter(BytesIO())
inst.toXML(writer, font)
self.assertEqual([
'',
'',
- '' % inst.nameID,
+ '' % inst.subfamilyNameID,
'',
'',
''
@@ -187,12 +187,12 @@ class NamedInstanceTest(unittest.TestCase):
def test_fromXML(self):
inst = NamedInstance()
for name, attrs, content in parseXML(
- ''
+ ''
' '
' '
''):
inst.fromXML(name, attrs, content, ttFont=MakeFont())
- self.assertEqual(345, inst.nameID)
+ self.assertEqual(345, inst.subfamilyNameID)
self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 5b047302d..f8ce7739d 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -68,15 +68,18 @@ def _add_fvar(font, axes, instances, axis_map):
axis = Axis()
axis.axisTag = Tag(axis_map[iden][0])
axis.minValue, axis.defaultValue, axis.maxValue = axes[iden]
- axis.nameID = _AddName(font, axis_map[iden][1]).nameID
+ axis.axisNameID = _AddName(font, axis_map[iden][1]).nameID
fvar.axes.append(axis)
for instance in instances:
coordinates = instance['location']
name = instance['stylename']
+ psname = instance.get('postscriptfontname')
inst = NamedInstance()
- inst.nameID = _AddName(font, name).nameID
+ inst.subfamilyNameID = _AddName(font, name).nameID
+ if psname:
+ inst.postscriptNamedID = _AddName(font, psname).nameID
inst.coordinates = {axis_map[k][0]:v for k,v in coordinates.items()}
fvar.instances.append(inst)