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)