Reusing otlLib buildStatTable() in feaLib
This commit is contained in:
parent
0434b1a917
commit
29ff42d15f
@ -29,7 +29,7 @@ __all__ = [
|
||||
"Anchor",
|
||||
"AnchorDefinition",
|
||||
"AttachStatement",
|
||||
"AxisValueLocation",
|
||||
"AxisValueLocationStatement",
|
||||
"BaseAxis",
|
||||
"CVParametersNameStatement",
|
||||
"ChainContextPosStatement",
|
||||
@ -66,8 +66,8 @@ __all__ = [
|
||||
"SingleSubstStatement",
|
||||
"SizeParameters",
|
||||
"Statement",
|
||||
"STATAxisValueRecord",
|
||||
"STATDesignAxis",
|
||||
"STATAxisValueStatement",
|
||||
"STATDesignAxisStatement",
|
||||
"STATNameStatement",
|
||||
"SubtableStatement",
|
||||
"TableBlock",
|
||||
@ -1902,7 +1902,7 @@ class VheaField(Statement):
|
||||
return "{} {};".format(keywords[self.key], self.value)
|
||||
|
||||
|
||||
class STATDesignAxis(Statement):
|
||||
class STATDesignAxisStatement(Statement):
|
||||
"""A STAT table Design Axis
|
||||
|
||||
Args:
|
||||
@ -1970,12 +1970,12 @@ class ElidedFallbackNameID(Statement):
|
||||
return f"ElidedFallbackNameID {self.value};"
|
||||
|
||||
|
||||
class STATAxisValueRecord(Statement):
|
||||
class STATAxisValueStatement(Statement):
|
||||
"""A STAT table Axis Value Record
|
||||
|
||||
Args:
|
||||
names (list): a list of :class:`STATNameStatement` objects
|
||||
locations (list): a list of :class:`AxisValueLocation` objects
|
||||
locations (list): a list of :class:`AxisValueLocationStatement` objects
|
||||
flags (int): an int
|
||||
"""
|
||||
def __init__(self, names, locations, flags, location=None):
|
||||
@ -1990,8 +1990,7 @@ class STATAxisValueRecord(Statement):
|
||||
def asFea(self, indent=""):
|
||||
res = "AxisValue {\n"
|
||||
for location in self.locations:
|
||||
res += f"location {location.tag} "
|
||||
res += f"{' '.join(str(i) for i in location.values)};\n"
|
||||
res += location.asFea()
|
||||
|
||||
for nameRecord in self.names:
|
||||
res += nameRecord.asFea()
|
||||
@ -2010,7 +2009,7 @@ class STATAxisValueRecord(Statement):
|
||||
return res
|
||||
|
||||
|
||||
class AxisValueLocation(NamedTuple):
|
||||
class AxisValueLocationStatement(Statement):
|
||||
"""
|
||||
A STAT table Axis Value Location
|
||||
|
||||
@ -2018,5 +2017,12 @@ class AxisValueLocation(NamedTuple):
|
||||
tag (str): a 4 letter axis tag
|
||||
values (list): a list of ints and/or floats
|
||||
"""
|
||||
tag: str
|
||||
values: list
|
||||
def __init__(self, tag, values, location=None):
|
||||
Statement.__init__(self, location)
|
||||
self.tag = tag
|
||||
self.values = values
|
||||
|
||||
def asFea(self, res=""):
|
||||
res += f"location {self.tag} "
|
||||
res += f"{' '.join(str(i) for i in self.values)};\n"
|
||||
return res
|
||||
|
@ -536,7 +536,7 @@ class Builder(object):
|
||||
self.stat_["DesignAxes"] = []
|
||||
if designAxis.tag in (r.tag for r in self.stat_["DesignAxes"]):
|
||||
raise FeatureLibError(
|
||||
'DesignAxis already defined for tag "%s".' % designAxis.tag,
|
||||
f'DesignAxis already defined for tag "{designAxis.tag}".',
|
||||
location,
|
||||
)
|
||||
if designAxis.axisOrder in (r.axisOrder for r in self.stat_["DesignAxes"]):
|
||||
@ -551,10 +551,11 @@ class Builder(object):
|
||||
self.stat_["AxisValueRecords"] = []
|
||||
# Check for duplicate AxisValueRecords
|
||||
for record_ in self.stat_["AxisValueRecords"]:
|
||||
if (sorted([n.asFea() for n in record_.names]) ==
|
||||
sorted([n.asFea() for n in axisValueRecord.names]) and
|
||||
sorted(record_.locations) == sorted(axisValueRecord.locations)
|
||||
and record_.flags == axisValueRecord.flags):
|
||||
if ({n.asFea() for n in record_.names} ==
|
||||
{n.asFea() for n in axisValueRecord.names} and
|
||||
{n.asFea() for n in record_.locations} ==
|
||||
{n.asFea() for n in axisValueRecord.locations}
|
||||
and record_.flags == axisValueRecord.flags):
|
||||
raise FeatureLibError(
|
||||
"An AxisValueRecord with these values is already defined.",
|
||||
location,
|
||||
@ -564,126 +565,65 @@ class Builder(object):
|
||||
def build_STAT(self):
|
||||
if not self.stat_:
|
||||
return
|
||||
self.font["STAT"] = newTable("STAT")
|
||||
table = self.font["STAT"].table = otTables.STAT()
|
||||
table.Version = 0x00010001
|
||||
|
||||
axes = self.stat_.get("DesignAxes")
|
||||
if not axes:
|
||||
raise FeatureLibError('DesignAxes not defined', None)
|
||||
axisValueRecords = self.stat_.get("AxisValueRecords")
|
||||
axisValues = {}
|
||||
format4_locations = []
|
||||
for tag in axes:
|
||||
axisValues[tag.tag] = []
|
||||
if axisValueRecords is not None:
|
||||
for avr in axisValueRecords:
|
||||
valuesDict = {}
|
||||
if avr.flags > 0:
|
||||
valuesDict['flags'] = avr.flags
|
||||
if len(avr.locations) == 1:
|
||||
location = avr.locations[0]
|
||||
values = location.values
|
||||
if len(values) == 1: #format1
|
||||
valuesDict.update({'value': values[0],'name': avr.names})
|
||||
if len(values) == 2: #format3
|
||||
valuesDict.update({ 'value': values[0],
|
||||
'linkedValue': values[1],
|
||||
'name': avr.names})
|
||||
if len(values) == 3: #format2
|
||||
nominal, minVal, maxVal = values
|
||||
valuesDict.update({ 'nominalValue': nominal,
|
||||
'rangeMinValue': minVal,
|
||||
'rangeMaxValue': maxVal,
|
||||
'name': avr.names})
|
||||
axisValues[location.tag].append(valuesDict)
|
||||
else:
|
||||
valuesDict.update({"location": {i.tag: i.values[0]
|
||||
for i in avr.locations},
|
||||
"name": avr.names})
|
||||
format4_locations.append(valuesDict)
|
||||
|
||||
designAxes = [{"ordering": a.axisOrder,
|
||||
"tag": a.tag,
|
||||
"name": a.names,
|
||||
'values': axisValues[a.tag]} for a in axes]
|
||||
|
||||
nameTable = self.font.get("name")
|
||||
if not nameTable: # this only happens for unit tests
|
||||
nameTable = self.font["name"] = newTable("name")
|
||||
nameTable.names = []
|
||||
|
||||
if "ElidedFallbackNameID" in self.stat_:
|
||||
nameID = self.stat_["ElidedFallbackNameID"]
|
||||
name = nameTable.getDebugName(nameID)
|
||||
nameID = self.stat_["ElidedFallbackNameID"]
|
||||
name = nameTable.getDebugName(nameID)
|
||||
if not name:
|
||||
raise FeatureLibError('ElidedFallbackNameID %d points '
|
||||
raise FeatureLibError(f'ElidedFallbackNameID {nameID} points '
|
||||
'to a nameID that does not exist in the '
|
||||
'"name" table' % nameID, None)
|
||||
table.ElidedFallbackNameID = nameID
|
||||
if "ElidedFallbackName" in self.stat_:
|
||||
nameRecords = self.stat_["ElidedFallbackName"]
|
||||
nameID = self.get_user_name_id(nameTable)
|
||||
for nameRecord in nameRecords:
|
||||
nameTable.setName(nameRecord.string, nameID,
|
||||
nameRecord.platformID, nameRecord.platEncID,
|
||||
nameRecord.langID)
|
||||
table.ElidedFallbackNameID = nameID
|
||||
'"name" table', None)
|
||||
elif "ElidedFallbackName" in self.stat_:
|
||||
nameID = self.stat_["ElidedFallbackName"]
|
||||
|
||||
otl.buildStatTable(self.font, designAxes, locations=format4_locations,
|
||||
elidedFallbackName=nameID)
|
||||
|
||||
axisRecords = []
|
||||
axisValueRecords = []
|
||||
designAxisOrder = {}
|
||||
for record in self.stat_["DesignAxes"]:
|
||||
axis = otTables.AxisRecord()
|
||||
axis.AxisTag = record.tag
|
||||
nameID = self.get_user_name_id(nameTable)
|
||||
for nameRecord in record.names:
|
||||
nameTable.setName(nameRecord.string, nameID,
|
||||
nameRecord.platformID, nameRecord.platEncID,
|
||||
nameRecord.langID)
|
||||
|
||||
axis.AxisNameID = nameID
|
||||
axis.AxisOrdering = record.axisOrder
|
||||
axisRecords.append(axis)
|
||||
designAxisOrder[record.tag] = record.axisOrder
|
||||
|
||||
if "AxisValueRecords" in self.stat_:
|
||||
for record in self.stat_["AxisValueRecords"]:
|
||||
if len(record.locations) == 1:
|
||||
location = record.locations[0]
|
||||
tag = location.tag
|
||||
values = location.values
|
||||
axisOrder = designAxisOrder[tag]
|
||||
axisValueRecord = otTables.AxisValue()
|
||||
axisValueRecord.AxisIndex = axisOrder
|
||||
axisValueRecord.Flags = record.flags
|
||||
|
||||
nameID = self.get_user_name_id(nameTable)
|
||||
for nameRecord in record.names:
|
||||
nameTable.setName(nameRecord.string, nameID,
|
||||
nameRecord.platformID,
|
||||
nameRecord.platEncID,
|
||||
nameRecord.langID)
|
||||
|
||||
axisValueRecord.ValueNameID = nameID
|
||||
|
||||
if len(values) == 1:
|
||||
axisValueRecord.Format = 1
|
||||
axisValueRecord.Value = values[0]
|
||||
if len(values) == 2:
|
||||
axisValueRecord.Format = 3
|
||||
axisValueRecord.Value = values[0]
|
||||
axisValueRecord.LinkedValue = values[1]
|
||||
if len(values) == 3:
|
||||
axisValueRecord.Format = 2
|
||||
nominal, minVal, maxVal = values
|
||||
axisValueRecord.NominalValue = nominal
|
||||
axisValueRecord.RangeMinValue = minVal
|
||||
axisValueRecord.RangeMaxValue = maxVal
|
||||
axisValueRecords.append(axisValueRecord)
|
||||
|
||||
if len(record.locations) > 1:
|
||||
# Multiple locations = Format 4
|
||||
table.Version = 0x00010002
|
||||
axisValue = otTables.AxisValue()
|
||||
axisValue.Format = 4
|
||||
|
||||
nameID = self.get_user_name_id(nameTable)
|
||||
for nameRecord in record.names:
|
||||
nameTable.setName(nameRecord.string, nameID,
|
||||
nameRecord.platformID,
|
||||
nameRecord.platEncID,
|
||||
nameRecord.langID)
|
||||
|
||||
axisValue.ValueNameID = nameID
|
||||
axisValue.Flags = record.flags
|
||||
|
||||
axisValueRecords_fmt4 = []
|
||||
for location in record.locations:
|
||||
tag = location.tag
|
||||
values = location.values
|
||||
axisOrder = designAxisOrder[tag]
|
||||
axisValueRecord = otTables.AxisValueRecord()
|
||||
axisValueRecord.AxisIndex = axisOrder
|
||||
axisValueRecord.Value = values[0]
|
||||
axisValueRecords_fmt4.append(axisValueRecord)
|
||||
axisValue.AxisCount = len(axisValueRecords_fmt4)
|
||||
axisValue.AxisValueRecord = axisValueRecords_fmt4
|
||||
axisValueRecords.append(axisValue)
|
||||
|
||||
if axisRecords:
|
||||
# Store AxisRecords
|
||||
axisRecordArray = otTables.AxisRecordArray()
|
||||
axisRecordArray.Axis = axisRecords
|
||||
# XXX these should not be hard-coded but computed automatically
|
||||
table.DesignAxisRecordSize = 8
|
||||
table.DesignAxisRecord = axisRecordArray
|
||||
table.DesignAxisCount = len(axisRecords)
|
||||
|
||||
if axisValueRecords:
|
||||
# Store AxisValueRecords
|
||||
axisValueArray = otTables.AxisValueArray()
|
||||
axisValueArray.AxisValue = axisValueRecords
|
||||
table.AxisValueArray = axisValueArray
|
||||
table.AxisValueCount = len(axisValueRecords)
|
||||
|
||||
def build_codepages_(self, pages):
|
||||
pages2bits = {
|
||||
|
@ -1186,8 +1186,6 @@ class Parser(object):
|
||||
langID = langID or 0x0409 # English
|
||||
|
||||
string = self.expect_string_()
|
||||
# self.expect_symbol_(";")
|
||||
|
||||
encoding = getEncoding(platformID, platEncID, langID)
|
||||
if encoding is None:
|
||||
raise FeatureLibError("Unsupported encoding", location)
|
||||
@ -1368,7 +1366,7 @@ class Parser(object):
|
||||
self.cur_token_location_)
|
||||
|
||||
self.expect_symbol_("}")
|
||||
return self.ast.STATDesignAxis(axisTag, axisOrder, names, self.cur_token_location_)
|
||||
return self.ast.STATDesignAxisStatement(axisTag, axisOrder, names, self.cur_token_location_)
|
||||
|
||||
def parse_STAT_axis_value_(self):
|
||||
assert self.is_cur_keyword_("AxisValue")
|
||||
@ -1420,7 +1418,7 @@ class Parser(object):
|
||||
self.cur_token_location_)
|
||||
format4_tags.append(tag)
|
||||
|
||||
return self.ast.STATAxisValueRecord(names, locations, flags, self.cur_token_location_)
|
||||
return self.ast.STATAxisValueStatement(names, locations, flags, self.cur_token_location_)
|
||||
|
||||
def parse_STAT_location(self):
|
||||
values = []
|
||||
@ -1447,7 +1445,7 @@ class Parser(object):
|
||||
f'of specified range '
|
||||
f'{min_val}-{max_val}.',
|
||||
self.next_token_location_)
|
||||
return self.ast.AxisValueLocation(tag, values)
|
||||
return self.ast.AxisValueLocationStatement(tag, values)
|
||||
|
||||
def parse_table_STAT_(self, table):
|
||||
statements = table.statements
|
||||
@ -1989,7 +1987,7 @@ class Parser(object):
|
||||
raise FeatureLibError("Expected a tag", self.cur_token_location_)
|
||||
if len(self.cur_token_) > 4:
|
||||
raise FeatureLibError(
|
||||
"Tags can not be longer than 4 characters", self.cur_token_location_
|
||||
"Tags cannot be longer than 4 characters", self.cur_token_location_
|
||||
)
|
||||
return (self.cur_token_ + " ")[:4]
|
||||
|
||||
|
@ -9,6 +9,7 @@ from fontTools.ttLib.tables.otBase import (
|
||||
CountReference,
|
||||
)
|
||||
from fontTools.ttLib.tables import otBase
|
||||
from fontTools.feaLib.ast import STATNameStatement
|
||||
from fontTools.otlLib.error import OpenTypeLibError
|
||||
import logging
|
||||
import copy
|
||||
@ -2687,8 +2688,8 @@ def buildStatTable(ttFont, axes, locations=None, elidedFallbackName=2):
|
||||
]
|
||||
|
||||
The optional 'elidedFallbackName' argument can be a name ID (int),
|
||||
a string, or a dictionary containing multilingual names. It
|
||||
translates to the ElidedFallbackNameID field.
|
||||
a string, a dictionary containing multilingual names, or a list of
|
||||
STATNameStatements. It translates to the ElidedFallbackNameID field.
|
||||
|
||||
The 'ttFont' argument must be a TTFont instance that already has a
|
||||
'name' table. If a 'STAT' table already exists, it will be
|
||||
@ -2797,6 +2798,16 @@ def _addName(nameTable, value, minNameID=0):
|
||||
names = dict(en=value)
|
||||
elif isinstance(value, dict):
|
||||
names = value
|
||||
elif isinstance(value, list):
|
||||
nameID = nameTable._findUnusedNameID()
|
||||
for nameRecord in value:
|
||||
if isinstance(nameRecord, STATNameStatement):
|
||||
nameTable.setName(nameRecord.string,
|
||||
nameID,nameRecord.platformID,
|
||||
nameRecord.platEncID,nameRecord.langID)
|
||||
else:
|
||||
raise TypeError("value must be a list of STATNameStatements")
|
||||
return nameID
|
||||
else:
|
||||
raise TypeError("value must be int, str or dict")
|
||||
raise TypeError("value must be int, str, dict or list")
|
||||
return nameTable.addMultilingualName(names, minNameID=minNameID)
|
||||
|
@ -74,7 +74,7 @@ class BuilderTest(unittest.TestCase):
|
||||
LigatureSubtable AlternateSubtable MultipleSubstSubtable
|
||||
SingleSubstSubtable aalt_chain_contextual_subst AlternateChained
|
||||
MultipleLookupsPerGlyph MultipleLookupsPerGlyph2 GSUB_6_formats
|
||||
GSUB_5_formats delete_glyph STAT_test
|
||||
GSUB_5_formats delete_glyph STAT_test STAT_test_elidedFallbackNameID
|
||||
""".split()
|
||||
|
||||
def __init__(self, methodName):
|
||||
@ -514,6 +514,7 @@ class BuilderTest(unittest.TestCase):
|
||||
'} name;'
|
||||
'table STAT {'
|
||||
' ElidedFallbackNameID 256;'
|
||||
' DesignAxis opsz 1 { name "Optical Size"; };'
|
||||
'} STAT;')
|
||||
|
||||
def test_STAT_design_axis_name(self):
|
||||
@ -647,7 +648,7 @@ class BuilderTest(unittest.TestCase):
|
||||
def test_STAT_invalid_location_tag(self):
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError,
|
||||
'Tags can not be longer than 4 characters',
|
||||
'Tags cannot be longer than 4 characters',
|
||||
self.build,
|
||||
'table name {'
|
||||
' nameid 256 "Roman"; '
|
||||
|
@ -1,3 +1,4 @@
|
||||
# bad fea file: Testing DesignAxis tag with incorrect label
|
||||
table name {
|
||||
nameid 25 "TestFont";
|
||||
} name;
|
||||
@ -7,7 +8,7 @@ table STAT {
|
||||
|
||||
ElidedFallbackName { name "Roman"; };
|
||||
|
||||
DesignAxis opsz 0 { badtag "Optical Size"; };
|
||||
DesignAxis opsz 0 { badtag "Optical Size"; }; #'badtag' instead of 'name' is incorrect
|
||||
DesignAxis wdth 1 { name "Width"; };
|
||||
DesignAxis wght 2 { name "Weight"; };
|
||||
DesignAxis ital 3 { name "Italic"; };
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.13">
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
|
||||
|
||||
<name>
|
||||
<namerecord nameID="25" platformID="3" platEncID="1" langID="0x409">
|
||||
@ -15,59 +15,59 @@
|
||||
Optical Size
|
||||
</namerecord>
|
||||
<namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
|
||||
Width
|
||||
</namerecord>
|
||||
<namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
|
||||
Weight
|
||||
</namerecord>
|
||||
<namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
|
||||
Italic
|
||||
</namerecord>
|
||||
<namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
|
||||
Caption
|
||||
</namerecord>
|
||||
<namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
|
||||
Text
|
||||
</namerecord>
|
||||
<namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
|
||||
Subhead
|
||||
</namerecord>
|
||||
<namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
|
||||
Display
|
||||
</namerecord>
|
||||
<namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
|
||||
Width
|
||||
</namerecord>
|
||||
<namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
|
||||
Condensed
|
||||
</namerecord>
|
||||
<namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
|
||||
Semicondensed
|
||||
</namerecord>
|
||||
<namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
|
||||
Normal
|
||||
</namerecord>
|
||||
<namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
|
||||
Extended
|
||||
</namerecord>
|
||||
<namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
|
||||
Weight
|
||||
</namerecord>
|
||||
<namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
|
||||
Light
|
||||
</namerecord>
|
||||
<namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
|
||||
Medium
|
||||
</namerecord>
|
||||
<namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
|
||||
Semibold
|
||||
</namerecord>
|
||||
<namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
|
||||
Bold
|
||||
</namerecord>
|
||||
<namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
|
||||
Black
|
||||
</namerecord>
|
||||
<namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
|
||||
<namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
|
||||
Italic
|
||||
</namerecord>
|
||||
<namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
|
||||
Roman
|
||||
</namerecord>
|
||||
<namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
|
||||
Caption
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<STAT>
|
||||
@ -82,17 +82,17 @@
|
||||
</Axis>
|
||||
<Axis index="1">
|
||||
<AxisTag value="wdth"/>
|
||||
<AxisNameID value="258"/> <!-- Width -->
|
||||
<AxisNameID value="261"/> <!-- Width -->
|
||||
<AxisOrdering value="1"/>
|
||||
</Axis>
|
||||
<Axis index="2">
|
||||
<AxisTag value="wght"/>
|
||||
<AxisNameID value="259"/> <!-- Weight -->
|
||||
<AxisNameID value="266"/> <!-- Weight -->
|
||||
<AxisOrdering value="2"/>
|
||||
</Axis>
|
||||
<Axis index="3">
|
||||
<AxisTag value="ital"/>
|
||||
<AxisNameID value="260"/> <!-- Italic -->
|
||||
<AxisNameID value="273"/> <!-- Italic -->
|
||||
<AxisOrdering value="3"/>
|
||||
</Axis>
|
||||
</DesignAxisRecord>
|
||||
@ -101,7 +101,7 @@
|
||||
<AxisValue index="0" Format="4">
|
||||
<!-- AxisCount=2 -->
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="261"/> <!-- Caption -->
|
||||
<ValueNameID value="275"/> <!-- Caption -->
|
||||
<AxisValueRecord index="0">
|
||||
<AxisIndex value="0"/>
|
||||
<Value value="8.0"/>
|
||||
@ -114,7 +114,7 @@
|
||||
<AxisValue index="1" Format="2">
|
||||
<AxisIndex value="0"/>
|
||||
<Flags value="3"/> <!-- OlderSiblingFontAttribute ElidableAxisValueName -->
|
||||
<ValueNameID value="262"/> <!-- Text -->
|
||||
<ValueNameID value="258"/> <!-- Text -->
|
||||
<NominalValue value="11.0"/>
|
||||
<RangeMinValue value="9.0"/>
|
||||
<RangeMaxValue value="12.0"/>
|
||||
@ -122,7 +122,7 @@
|
||||
<AxisValue index="2" Format="2">
|
||||
<AxisIndex value="0"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="263"/> <!-- Subhead -->
|
||||
<ValueNameID value="259"/> <!-- Subhead -->
|
||||
<NominalValue value="16.7"/>
|
||||
<RangeMinValue value="12.0"/>
|
||||
<RangeMaxValue value="24.0"/>
|
||||
@ -130,7 +130,7 @@
|
||||
<AxisValue index="3" Format="2">
|
||||
<AxisIndex value="0"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="264"/> <!-- Display -->
|
||||
<ValueNameID value="260"/> <!-- Display -->
|
||||
<NominalValue value="72.0"/>
|
||||
<RangeMinValue value="24.0"/>
|
||||
<RangeMaxValue value="72.0"/>
|
||||
@ -138,7 +138,7 @@
|
||||
<AxisValue index="4" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="265"/> <!-- Condensed -->
|
||||
<ValueNameID value="262"/> <!-- Condensed -->
|
||||
<NominalValue value="80.0"/>
|
||||
<RangeMinValue value="80.0"/>
|
||||
<RangeMaxValue value="89.0"/>
|
||||
@ -146,7 +146,7 @@
|
||||
<AxisValue index="5" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="266"/> <!-- Semicondensed -->
|
||||
<ValueNameID value="263"/> <!-- Semicondensed -->
|
||||
<NominalValue value="90.0"/>
|
||||
<RangeMinValue value="90.0"/>
|
||||
<RangeMaxValue value="96.0"/>
|
||||
@ -154,7 +154,7 @@
|
||||
<AxisValue index="6" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="2"/> <!-- ElidableAxisValueName -->
|
||||
<ValueNameID value="267"/> <!-- Normal -->
|
||||
<ValueNameID value="264"/> <!-- Normal -->
|
||||
<NominalValue value="100.0"/>
|
||||
<RangeMinValue value="97.0"/>
|
||||
<RangeMaxValue value="101.0"/>
|
||||
@ -162,7 +162,7 @@
|
||||
<AxisValue index="7" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="268"/> <!-- Extended -->
|
||||
<ValueNameID value="265"/> <!-- Extended -->
|
||||
<NominalValue value="125.0"/>
|
||||
<RangeMinValue value="102.0"/>
|
||||
<RangeMaxValue value="125.0"/>
|
||||
@ -170,7 +170,7 @@
|
||||
<AxisValue index="8" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="269"/> <!-- Light -->
|
||||
<ValueNameID value="267"/> <!-- Light -->
|
||||
<NominalValue value="300.0"/>
|
||||
<RangeMinValue value="300.0"/>
|
||||
<RangeMaxValue value="349.0"/>
|
||||
@ -178,7 +178,7 @@
|
||||
<AxisValue index="9" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="2"/> <!-- ElidableAxisValueName -->
|
||||
<ValueNameID value="270"/> <!-- Regular -->
|
||||
<ValueNameID value="268"/> <!-- Regular -->
|
||||
<NominalValue value="400.0"/>
|
||||
<RangeMinValue value="350.0"/>
|
||||
<RangeMaxValue value="449.0"/>
|
||||
@ -186,7 +186,7 @@
|
||||
<AxisValue index="10" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="271"/> <!-- Medium -->
|
||||
<ValueNameID value="269"/> <!-- Medium -->
|
||||
<NominalValue value="500.0"/>
|
||||
<RangeMinValue value="450.0"/>
|
||||
<RangeMaxValue value="549.0"/>
|
||||
@ -194,7 +194,7 @@
|
||||
<AxisValue index="11" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="272"/> <!-- Semibold -->
|
||||
<ValueNameID value="270"/> <!-- Semibold -->
|
||||
<NominalValue value="600.0"/>
|
||||
<RangeMinValue value="550.0"/>
|
||||
<RangeMaxValue value="649.0"/>
|
||||
@ -202,7 +202,7 @@
|
||||
<AxisValue index="12" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="273"/> <!-- Bold -->
|
||||
<ValueNameID value="271"/> <!-- Bold -->
|
||||
<NominalValue value="700.0"/>
|
||||
<RangeMinValue value="650.0"/>
|
||||
<RangeMaxValue value="749.0"/>
|
||||
@ -210,7 +210,7 @@
|
||||
<AxisValue index="13" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="274"/> <!-- Black -->
|
||||
<ValueNameID value="272"/> <!-- Black -->
|
||||
<NominalValue value="900.0"/>
|
||||
<RangeMinValue value="750.0"/>
|
||||
<RangeMaxValue value="900.0"/>
|
||||
@ -218,7 +218,7 @@
|
||||
<AxisValue index="14" Format="1">
|
||||
<AxisIndex value="3"/>
|
||||
<Flags value="2"/> <!-- ElidableAxisValueName -->
|
||||
<ValueNameID value="275"/> <!-- Roman -->
|
||||
<ValueNameID value="274"/> <!-- Roman -->
|
||||
<Value value="0.0"/>
|
||||
</AxisValue>
|
||||
</AxisValueArray>
|
||||
|
84
Tests/feaLib/data/STAT_test_elidedFallbackNameID.fea
Normal file
84
Tests/feaLib/data/STAT_test_elidedFallbackNameID.fea
Normal file
@ -0,0 +1,84 @@
|
||||
table name {
|
||||
nameid 25 "TestFont";
|
||||
nameid 256 "Roman";
|
||||
} name;
|
||||
table STAT {
|
||||
ElidedFallbackNameID 256;
|
||||
DesignAxis opsz 0 {
|
||||
name "Optical Size";
|
||||
};
|
||||
DesignAxis wdth 1 {
|
||||
name "Width";
|
||||
};
|
||||
DesignAxis wght 2 {
|
||||
name "Weight";
|
||||
};
|
||||
DesignAxis ital 3 {
|
||||
name "Italic";
|
||||
}; # here comment
|
||||
AxisValue {
|
||||
location opsz 8; # comment here
|
||||
location wdth 400; # another comment
|
||||
name "Caption"; # more comments
|
||||
};
|
||||
AxisValue {
|
||||
location opsz 11 9 12;
|
||||
name "Text";
|
||||
flag OlderSiblingFontAttribute ElidableAxisValueName;
|
||||
};
|
||||
AxisValue {
|
||||
location opsz 16.7 12 24;
|
||||
name "Subhead";
|
||||
};
|
||||
AxisValue {
|
||||
location opsz 72 24 72;
|
||||
name "Display";
|
||||
};
|
||||
AxisValue {
|
||||
location wdth 80 80 89;
|
||||
name "Condensed";
|
||||
};
|
||||
AxisValue {
|
||||
location wdth 90 90 96;
|
||||
name "Semicondensed";
|
||||
};
|
||||
AxisValue {
|
||||
location wdth 100 97 101;
|
||||
name "Normal";
|
||||
flag ElidableAxisValueName;
|
||||
};
|
||||
AxisValue {
|
||||
location wdth 125 102 125;
|
||||
name "Extended";
|
||||
};
|
||||
AxisValue {
|
||||
location wght 300 300 349;
|
||||
name "Light";
|
||||
};
|
||||
AxisValue {
|
||||
location wght 400 350 449;
|
||||
name "Regular";
|
||||
flag ElidableAxisValueName;
|
||||
};
|
||||
AxisValue {
|
||||
location wght 500 450 549;
|
||||
name "Medium";
|
||||
};
|
||||
AxisValue {
|
||||
location wght 600 550 649;
|
||||
name "Semibold";
|
||||
};
|
||||
AxisValue {
|
||||
location wght 700 650 749;
|
||||
name "Bold";
|
||||
};
|
||||
AxisValue {
|
||||
location wght 900 750 900;
|
||||
name "Black";
|
||||
};
|
||||
AxisValue {
|
||||
location ital 0;
|
||||
name "Roman";
|
||||
flag ElidableAxisValueName; # flag comment
|
||||
};
|
||||
} STAT;
|
225
Tests/feaLib/data/STAT_test_elidedFallbackNameID.ttx
Normal file
225
Tests/feaLib/data/STAT_test_elidedFallbackNameID.ttx
Normal file
@ -0,0 +1,225 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
|
||||
|
||||
<name>
|
||||
<namerecord nameID="25" platformID="3" platEncID="1" langID="0x409">
|
||||
TestFont
|
||||
</namerecord>
|
||||
<namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
|
||||
Roman
|
||||
</namerecord>
|
||||
<namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
|
||||
Optical Size
|
||||
</namerecord>
|
||||
<namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
|
||||
Text
|
||||
</namerecord>
|
||||
<namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
|
||||
Subhead
|
||||
</namerecord>
|
||||
<namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
|
||||
Display
|
||||
</namerecord>
|
||||
<namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
|
||||
Width
|
||||
</namerecord>
|
||||
<namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
|
||||
Condensed
|
||||
</namerecord>
|
||||
<namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
|
||||
Semicondensed
|
||||
</namerecord>
|
||||
<namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
|
||||
Normal
|
||||
</namerecord>
|
||||
<namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
|
||||
Extended
|
||||
</namerecord>
|
||||
<namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
|
||||
Weight
|
||||
</namerecord>
|
||||
<namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
|
||||
Light
|
||||
</namerecord>
|
||||
<namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
|
||||
Medium
|
||||
</namerecord>
|
||||
<namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
|
||||
Semibold
|
||||
</namerecord>
|
||||
<namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
|
||||
Bold
|
||||
</namerecord>
|
||||
<namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
|
||||
Black
|
||||
</namerecord>
|
||||
<namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
|
||||
Italic
|
||||
</namerecord>
|
||||
<namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
|
||||
Roman
|
||||
</namerecord>
|
||||
<namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
|
||||
Caption
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<STAT>
|
||||
<Version value="0x00010002"/>
|
||||
<DesignAxisRecordSize value="8"/>
|
||||
<!-- DesignAxisCount=4 -->
|
||||
<DesignAxisRecord>
|
||||
<Axis index="0">
|
||||
<AxisTag value="opsz"/>
|
||||
<AxisNameID value="257"/> <!-- Optical Size -->
|
||||
<AxisOrdering value="0"/>
|
||||
</Axis>
|
||||
<Axis index="1">
|
||||
<AxisTag value="wdth"/>
|
||||
<AxisNameID value="261"/> <!-- Width -->
|
||||
<AxisOrdering value="1"/>
|
||||
</Axis>
|
||||
<Axis index="2">
|
||||
<AxisTag value="wght"/>
|
||||
<AxisNameID value="266"/> <!-- Weight -->
|
||||
<AxisOrdering value="2"/>
|
||||
</Axis>
|
||||
<Axis index="3">
|
||||
<AxisTag value="ital"/>
|
||||
<AxisNameID value="273"/> <!-- Italic -->
|
||||
<AxisOrdering value="3"/>
|
||||
</Axis>
|
||||
</DesignAxisRecord>
|
||||
<!-- AxisValueCount=15 -->
|
||||
<AxisValueArray>
|
||||
<AxisValue index="0" Format="4">
|
||||
<!-- AxisCount=2 -->
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="275"/> <!-- Caption -->
|
||||
<AxisValueRecord index="0">
|
||||
<AxisIndex value="0"/>
|
||||
<Value value="8.0"/>
|
||||
</AxisValueRecord>
|
||||
<AxisValueRecord index="1">
|
||||
<AxisIndex value="1"/>
|
||||
<Value value="400.0"/>
|
||||
</AxisValueRecord>
|
||||
</AxisValue>
|
||||
<AxisValue index="1" Format="2">
|
||||
<AxisIndex value="0"/>
|
||||
<Flags value="3"/> <!-- OlderSiblingFontAttribute ElidableAxisValueName -->
|
||||
<ValueNameID value="258"/> <!-- Text -->
|
||||
<NominalValue value="11.0"/>
|
||||
<RangeMinValue value="9.0"/>
|
||||
<RangeMaxValue value="12.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="2" Format="2">
|
||||
<AxisIndex value="0"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="259"/> <!-- Subhead -->
|
||||
<NominalValue value="16.7"/>
|
||||
<RangeMinValue value="12.0"/>
|
||||
<RangeMaxValue value="24.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="3" Format="2">
|
||||
<AxisIndex value="0"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="260"/> <!-- Display -->
|
||||
<NominalValue value="72.0"/>
|
||||
<RangeMinValue value="24.0"/>
|
||||
<RangeMaxValue value="72.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="4" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="262"/> <!-- Condensed -->
|
||||
<NominalValue value="80.0"/>
|
||||
<RangeMinValue value="80.0"/>
|
||||
<RangeMaxValue value="89.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="5" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="263"/> <!-- Semicondensed -->
|
||||
<NominalValue value="90.0"/>
|
||||
<RangeMinValue value="90.0"/>
|
||||
<RangeMaxValue value="96.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="6" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="2"/> <!-- ElidableAxisValueName -->
|
||||
<ValueNameID value="264"/> <!-- Normal -->
|
||||
<NominalValue value="100.0"/>
|
||||
<RangeMinValue value="97.0"/>
|
||||
<RangeMaxValue value="101.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="7" Format="2">
|
||||
<AxisIndex value="1"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="265"/> <!-- Extended -->
|
||||
<NominalValue value="125.0"/>
|
||||
<RangeMinValue value="102.0"/>
|
||||
<RangeMaxValue value="125.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="8" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="267"/> <!-- Light -->
|
||||
<NominalValue value="300.0"/>
|
||||
<RangeMinValue value="300.0"/>
|
||||
<RangeMaxValue value="349.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="9" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="2"/> <!-- ElidableAxisValueName -->
|
||||
<ValueNameID value="268"/> <!-- Regular -->
|
||||
<NominalValue value="400.0"/>
|
||||
<RangeMinValue value="350.0"/>
|
||||
<RangeMaxValue value="449.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="10" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="269"/> <!-- Medium -->
|
||||
<NominalValue value="500.0"/>
|
||||
<RangeMinValue value="450.0"/>
|
||||
<RangeMaxValue value="549.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="11" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="270"/> <!-- Semibold -->
|
||||
<NominalValue value="600.0"/>
|
||||
<RangeMinValue value="550.0"/>
|
||||
<RangeMaxValue value="649.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="12" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="271"/> <!-- Bold -->
|
||||
<NominalValue value="700.0"/>
|
||||
<RangeMinValue value="650.0"/>
|
||||
<RangeMaxValue value="749.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="13" Format="2">
|
||||
<AxisIndex value="2"/>
|
||||
<Flags value="0"/>
|
||||
<ValueNameID value="272"/> <!-- Black -->
|
||||
<NominalValue value="900.0"/>
|
||||
<RangeMinValue value="750.0"/>
|
||||
<RangeMaxValue value="900.0"/>
|
||||
</AxisValue>
|
||||
<AxisValue index="14" Format="1">
|
||||
<AxisIndex value="3"/>
|
||||
<Flags value="2"/> <!-- ElidableAxisValueName -->
|
||||
<ValueNameID value="274"/> <!-- Roman -->
|
||||
<Value value="0.0"/>
|
||||
</AxisValue>
|
||||
</AxisValueArray>
|
||||
<ElidedFallbackNameID value="256"/> <!-- Roman -->
|
||||
</STAT>
|
||||
|
||||
</ttFont>
|
@ -1284,7 +1284,7 @@ class ParserTest(unittest.TestCase):
|
||||
doc = self.parse('table STAT { DesignAxis opsz 0 '
|
||||
'{name "Optical Size";}; } STAT;')
|
||||
da = doc.statements[0].statements[0]
|
||||
self.assertIsInstance(da, ast.STATDesignAxis)
|
||||
self.assertIsInstance(da, ast.STATDesignAxisStatement)
|
||||
self.assertEqual(da.tag, 'opsz')
|
||||
self.assertEqual(da.axisOrder, 0)
|
||||
self.assertEqual(da.names[0].string, 'Optical Size')
|
||||
@ -1295,7 +1295,7 @@ class ParserTest(unittest.TestCase):
|
||||
'AxisValue {location opsz 8; name "Caption";}; } '
|
||||
'STAT;')
|
||||
avr = doc.statements[0].statements[1]
|
||||
self.assertIsInstance(avr, ast.STATAxisValueRecord)
|
||||
self.assertIsInstance(avr, ast.STATAxisValueStatement)
|
||||
self.assertEqual(avr.locations[0].tag, 'opsz')
|
||||
self.assertEqual(avr.locations[0].values[0], 8)
|
||||
self.assertEqual(avr.names[0].string, 'Caption')
|
||||
@ -1306,7 +1306,7 @@ class ParserTest(unittest.TestCase):
|
||||
'AxisValue {location opsz 8 6 10; name "Caption";}; } '
|
||||
'STAT;')
|
||||
avr = doc.statements[0].statements[1]
|
||||
self.assertIsInstance(avr, ast.STATAxisValueRecord)
|
||||
self.assertIsInstance(avr, ast.STATAxisValueStatement)
|
||||
self.assertEqual(avr.locations[0].tag, 'opsz')
|
||||
self.assertEqual(avr.locations[0].values, [8, 6, 10])
|
||||
self.assertEqual(avr.names[0].string, 'Caption')
|
||||
|
Loading…
x
Reference in New Issue
Block a user