[varLib] Rename fields in fvar, to accommodate for postscriptNameID

This commit is contained in:
Behdad Esfahbod 2016-09-02 18:12:14 -07:00
parent 8e675db59b
commit ae93928275
7 changed files with 76 additions and 53 deletions

View File

@ -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:

View File

@ -394,31 +394,31 @@
<MinValue>100.0</MinValue>
<DefaultValue>400.0</DefaultValue>
<MaxValue>900.0</MaxValue>
<NameID>257</NameID>
<AxisNameID>257</AxisNameID>
</Axis>
<!-- Thin -->
<NamedInstance nameID="258">
<NamedInstance subfamilyNameID="258">
<coord axis="wght" value="100.0"/>
</NamedInstance>
<!-- Light -->
<NamedInstance nameID="259">
<NamedInstance subfamilyNameID="259">
<coord axis="wght" value="300.0"/>
</NamedInstance>
<!-- Regular -->
<NamedInstance nameID="260">
<NamedInstance subfamilyNameID="260">
<coord axis="wght" value="400.0"/>
</NamedInstance>
<!-- Bold -->
<NamedInstance nameID="261">
<NamedInstance subfamilyNameID="261">
<coord axis="wght" value="700.0"/>
</NamedInstance>
<!-- Black -->
<NamedInstance nameID="262">
<NamedInstance subfamilyNameID="262">
<coord axis="wght" value="900.0"/>
</NamedInstance>
</fvar>

View File

@ -25,31 +25,31 @@
<MinValue>100.0</MinValue>
<DefaultValue>400.0</DefaultValue>
<MaxValue>900.0</MaxValue>
<NameID>257</NameID>
<AxisNameID>257</AxisNameID>
</Axis>
<!-- Thin -->
<NamedInstance nameID="258">
<NamedInstance subfamilyNameID="258">
<coord axis="wght" value="100.0"/>
</NamedInstance>
<!-- Light -->
<NamedInstance nameID="259">
<NamedInstance subfamilyNameID="259">
<coord axis="wght" value="300.0"/>
</NamedInstance>
<!-- Regular -->
<NamedInstance nameID="260">
<NamedInstance subfamilyNameID="260">
<coord axis="wght" value="400.0"/>
</NamedInstance>
<!-- Bold -->
<NamedInstance nameID="261">
<NamedInstance subfamilyNameID="261">
<coord axis="wght" value="700.0"/>
</NamedInstance>
<!-- Black -->
<NamedInstance nameID="262">
<NamedInstance subfamilyNameID="262">
<coord axis="wght" value="900.0"/>
</NamedInstance>
</fvar>

View File

@ -24,31 +24,31 @@
<MinValue>100.0</MinValue>
<DefaultValue>400.0</DefaultValue>
<MaxValue>900.0</MaxValue>
<NameID>257</NameID>
<AxisNameID>257</AxisNameID>
</Axis>
<!-- Thin -->
<NamedInstance nameID="258">
<NamedInstance subfamilyNameID="258">
<coord axis="wght" value="100.0"/>
</NamedInstance>
<!-- Light -->
<NamedInstance nameID="259">
<NamedInstance subfamilyNameID="259">
<coord axis="wght" value="300.0"/>
</NamedInstance>
<!-- Regular -->
<NamedInstance nameID="260">
<NamedInstance subfamilyNameID="260">
<coord axis="wght" value="400.0"/>
</NamedInstance>
<!-- Bold -->
<NamedInstance nameID="261">
<NamedInstance subfamilyNameID="261">
<coord axis="wght" value="700.0"/>
</NamedInstance>
<!-- Black -->
<NamedInstance nameID="262">
<NamedInstance subfamilyNameID="262">
<coord axis="wght" value="900.0"/>
</NamedInstance>
</fvar>

View File

@ -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"])

View File

@ -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):
'<Axis>'
' <AxisTag>slnt</AxisTag>'
'</Axis>'
'<NamedInstance nameID="765"/>'
'<NamedInstance nameID="234"/>'):
'<NamedInstance subfamilyNameID="765"/>'
'<NamedInstance subfamilyNameID="234"/>'):
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):
'<MinValue>-0.5</MinValue>',
'<DefaultValue>1.3</DefaultValue>',
'<MaxValue>1.5</MaxValue>',
'<NameID>256</NameID>',
'<AxisNameID>256</AxisNameID>',
'</Axis>'
], xml_lines(writer))
@ -145,40 +145,40 @@ class AxisTest(unittest.TestCase):
' <MinValue>100</MinValue>'
' <DefaultValue>400</DefaultValue>'
' <MaxValue>900</MaxValue>'
' <NameID>256</NameID>'
' <AxisNameID>256</AxisNameID>'
'</Axis>'):
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([
'',
'<!-- Light Condensed -->',
'<NamedInstance nameID="%s">' % inst.nameID,
'<NamedInstance subfamilyNameID="%s">' % inst.subfamilyNameID,
'<coord axis="wght" value="0.7"/>',
'<coord axis="wdth" value="0.5"/>',
'</NamedInstance>'
@ -187,12 +187,12 @@ class NamedInstanceTest(unittest.TestCase):
def test_fromXML(self):
inst = NamedInstance()
for name, attrs, content in parseXML(
'<NamedInstance nameID="345">'
'<NamedInstance subfamilyNameID="345">'
' <coord axis="wght" value="0.7"/>'
' <coord axis="wdth" value="0.5"/>'
'</NamedInstance>'):
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)

View File

@ -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)