Merge pull request #758 from fonttools/STAT

Implement STAT table
This commit is contained in:
Behdad Esfahbod 2016-12-02 21:30:19 -06:00 committed by GitHub
commit 1bd7183d97
7 changed files with 248 additions and 10 deletions

View File

@ -21,6 +21,7 @@ def parseXML(xmlSnippet):
class FakeFont:
def __init__(self, glyphs):
self.glyphOrder_ = glyphs
self.lazy = False
def getGlyphID(self, name):
return self.glyphOrder_.index(name)

View File

@ -0,0 +1,7 @@
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from .otBase import BaseTTXConverter
class table_S_T_A_T_(BaseTTXConverter):
pass

View File

@ -0,0 +1,180 @@
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc.testTools import FakeFont, getXML, parseXML
from fontTools.misc.textTools import deHexStr
from fontTools.ttLib import getTableModule, newTable
import unittest
STAT_DATA = deHexStr(
'0001 0000 ' # 0: Version=1.0
'0008 0002 ' # 4: DesignAxisSize=8, DesignAxisCount=2
'0000 0012 ' # 8: OffsetToDesignAxes=18
'0003 0000 0022 ' # 12: AxisValueCount=3, OffsetToAxisValueOffsets=34
'7767 6874 ' # 18: DesignAxis[0].AxisTag='wght'
'012D 0002 ' # 22: DesignAxis[0].NameID=301, .AxisOrdering=2
'5445 5354 ' # 26: DesignAxis[1].AxisTag='TEST'
'012E 0001 ' # 30: DesignAxis[1].NameID=302, .AxisOrdering=1
'0006 0012 0026 ' # 34: AxisValueOffsets = [6, 18, 38] (+34)
'0001 0000 0000 ' # 40: AxisValue[0].Format=1, .AxisIndex=0, .Flags=0
'0191 0190 0000 ' # 46: AxisValue[0].ValueNameID=401, .Value=400.0
'0002 0001 0000 ' # 52: AxisValue[1].Format=2, .AxisIndex=1, .Flags=0
'0192 ' # 58: AxisValue[1].ValueNameID=402
'0002 0000 ' # 60: AxisValue[1].NominalValue=2.0
'0001 0000 ' # 64: AxisValue[1].RangeMinValue=1.0
'0003 0000 ' # 68: AxisValue[1].RangeMaxValue=3.0
'0003 0000 0000 ' # 72: AxisValue[2].Format=3, .AxisIndex=0, .Flags=0
'0002 ' # 78: AxisValue[2].ValueNameID=2 'Regular'
'0190 0000 02BC 0000 ' # 80: AxisValue[2].Value=400.0, .LinkedValue=700.0
) # 88: <end>
assert(len(STAT_DATA) == 88)
STAT_XML = (
'<Version value="0x00010000"/>'
'<DesignAxisRecordSize value="8"/>'
'<!-- DesignAxisCount=2 -->'
'<DesignAxisRecord>'
' <Axis index="0">'
' <AxisTag value="wght"/>'
' <AxisNameID value="301"/>'
' <AxisOrdering value="2"/>'
' </Axis>'
' <Axis index="1">'
' <AxisTag value="TEST"/>'
' <AxisNameID value="302"/>'
' <AxisOrdering value="1"/>'
' </Axis>'
'</DesignAxisRecord>'
'<!-- AxisValueCount=3 -->'
'<AxisValueArray>'
' <AxisValue index="0" Format="1">'
' <AxisIndex value="0"/>'
' <Flags value="0"/>'
' <ValueNameID value="401"/>'
' <Value value="400.0"/>'
' </AxisValue>'
' <AxisValue index="1" Format="2">'
' <AxisIndex value="1"/>'
' <Flags value="0"/>'
' <ValueNameID value="402"/>'
' <NominalValue value="2.0"/>'
' <RangeMinValue value="1.0"/>'
' <RangeMaxValue value="3.0"/>'
' </AxisValue>'
' <AxisValue index="2" Format="3">'
' <AxisIndex value="0"/>'
' <Flags value="0"/>'
' <ValueNameID value="2"/>'
' <Value value="400.0"/>'
' <LinkedValue value="700.0"/>'
' </AxisValue>'
'</AxisValueArray>'
)
# Contains junk data for making sure we get our offset decoding right.
STAT_DATA_WITH_AXIS_JUNK = deHexStr(
'0001 0000 ' # 0: Version=1.0
'000A 0002 ' # 4: DesignAxisSize=10, DesignAxisCount=2
'0000 0012 ' # 8: OffsetToDesignAxes=18
'0000 0000 0000 ' # 12: AxisValueCount=3, OffsetToAxisValueOffsets=34
'7767 6874 ' # 18: DesignAxis[0].AxisTag='wght'
'012D 0002 ' # 22: DesignAxis[0].NameID=301, .AxisOrdering=2
'DEAD ' # 26: <junk>
'5445 5354 ' # 28: DesignAxis[1].AxisTag='TEST'
'012E 0001 ' # 32: DesignAxis[1].NameID=302, .AxisOrdering=1
'BEEF ' # 36: <junk>
) # 38: <end>
assert(len(STAT_DATA_WITH_AXIS_JUNK) == 38)
STAT_XML_WITH_AXIS_JUNK = (
'<Version value="0x00010000"/>'
'<DesignAxisRecordSize value="10"/>'
'<!-- DesignAxisCount=2 -->'
'<DesignAxisRecord>'
' <Axis index="0">'
' <AxisTag value="wght"/>'
' <AxisNameID value="301"/>'
' <AxisOrdering value="2"/>'
' <MoreBytes index="0" value="222"/>' # 0xDE
' <MoreBytes index="1" value="173"/>' # 0xAD
' </Axis>'
' <Axis index="1">'
' <AxisTag value="TEST"/>'
' <AxisNameID value="302"/>'
' <AxisOrdering value="1"/>'
' <MoreBytes index="0" value="190"/>' # 0xBE
' <MoreBytes index="1" value="239"/>' # 0xEF
' </Axis>'
'</DesignAxisRecord>'
'<!-- AxisValueCount=0 -->'
)
STAT_DATA_AXIS_VALUE_FORMAT3 = deHexStr(
'0001 0000 ' # 0: Version=1.0
'0008 0001 ' # 4: DesignAxisSize=8, DesignAxisCount=1
'0000 0012 ' # 8: OffsetToDesignAxes=18
'0001 ' # 12: AxisValueCount=1
'0000 001A ' # 14: OffsetToAxisValueOffsets=26
'7767 6874 ' # 18: DesignAxis[0].AxisTag='wght'
'0102 ' # 22: DesignAxis[0].AxisNameID=258 'Weight'
'0000 ' # 24: DesignAxis[0].AxisOrdering=0
'0002 ' # 26: AxisValueOffsets=[2] (+26)
'0003 ' # 28: AxisValue[0].Format=3
'0000 0002 ' # 30: AxisValue[0].AxisIndex=0, .Flags=0x2
'0002 ' # 34: AxisValue[0].ValueNameID=2 'Regular'
'0190 0000 ' # 36: AxisValue[0].Value=400.0
'02BC 0000 ' # 40: AxisValue[0].LinkedValue=700.0
) # 44: <end>
assert(len(STAT_DATA_AXIS_VALUE_FORMAT3) == 44)
STAT_XML_AXIS_VALUE_FORMAT3 = (
'<Version value="0x00010000"/>'
'<DesignAxisRecordSize value="8"/>'
'<!-- DesignAxisCount=1 -->'
'<DesignAxisRecord>'
' <Axis index="0">'
' <AxisTag value="wght"/>'
' <AxisNameID value="258"/>'
' <AxisOrdering value="0"/>'
' </Axis>'
'</DesignAxisRecord>'
'<!-- AxisValueCount=1 -->'
'<AxisValueArray>'
' <AxisValue index="0" Format="3">'
' <AxisIndex value="0"/>'
' <Flags value="2"/>'
' <ValueNameID value="2"/>'
' <Value value="400.0"/>'
' <LinkedValue value="700.0"/>'
' </AxisValue>'
'</AxisValueArray>'
)
class STATTest(unittest.TestCase):
def test_decompile_toXML(self):
table = newTable('STAT')
table.decompile(STAT_DATA, font=FakeFont(['.notdef']))
self.maxDiff = None
self.assertEqual(getXML(table.toXML), STAT_XML)
def test_decompile_toXML_withAxisJunk(self):
table = newTable('STAT')
table.decompile(STAT_DATA_WITH_AXIS_JUNK, font=FakeFont(['.notdef']))
self.assertEqual(getXML(table.toXML), STAT_XML_WITH_AXIS_JUNK)
def test_decompile_toXML_format3(self):
table = newTable('STAT')
table.decompile(STAT_DATA_AXIS_VALUE_FORMAT3,
font=FakeFont(['.notdef']))
self.assertEqual(getXML(table.toXML), STAT_XML_AXIS_VALUE_FORMAT3)
if __name__ == '__main__':
unittest.main()

View File

@ -31,6 +31,7 @@ def _moduleFinderHint():
from . import M_E_T_A_
from . import O_S_2f_2
from . import S_I_N_G_
from . import S_T_A_T_
from . import S_V_G_
from . import T_S_I_B_
from . import T_S_I_D_

View File

@ -95,7 +95,7 @@ class BaseConverter(object):
self.tableClass = tableClass
self.isCount = name.endswith("Count")
self.isLookupType = name.endswith("LookupType")
self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "VarRegionCount", "MappingCount", "RegionAxisCount"]
self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "VarRegionCount", "MappingCount", "RegionAxisCount", 'DesignAxisCount', 'DesignAxisRecordSize', 'AxisValueCount']
def readArray(self, reader, font, tableDict, count):
"""Read an array of values from the reader."""
@ -369,11 +369,6 @@ class Table(Struct):
offset = self.readOffset(reader)
if offset == 0:
return None
if offset <= 3:
# XXX hack to work around buggy pala.ttf
log.warning("offset is not 0, yet suspiciously low (%d). table: %s",
offset, self.tableClass.__name__)
return None
table = self.tableClass()
reader = reader.getSubReader(offset)
if font.lazy:

View File

@ -840,6 +840,60 @@ otData = [
]),
#
# STAT
#
('STAT', [
('Version', 'Version', None, None, 'Version of the table-initially set to 0x00010000'),
('uint16', 'DesignAxisRecordSize', None, None, 'Size in bytes of each design axis record'),
('uint16', 'DesignAxisCount', None, None, 'Number of design axis records'),
('LOffsetTo(AxisRecordArray)', 'DesignAxisRecord', None, None, 'Offset in bytes from the beginning of the STAT table to the start of the design axes array'),
('uint16', 'AxisValueCount', None, None, 'Number of axis value tables'),
('LOffsetTo(AxisValueArray)', 'AxisValueArray', None, None, 'Offset in bytes from the beginning of the STAT table to the start of the axes value offset array'),
]),
('AxisRecordArray', [
('AxisRecord', 'Axis', 'DesignAxisCount', 0, 'Axis records'),
]),
('AxisRecord', [
('Tag', 'AxisTag', None, None, 'A tag identifying the axis of design variation'),
('uint16', 'AxisNameID', None, None, 'The name ID for entries in the "name" table that provide a display string for this axis'),
('uint16', 'AxisOrdering', None, None, 'A value that applications can use to determine primary sorting of face names, or for ordering of descriptors when composing family or face names'),
('uint8', 'MoreBytes', 'DesignAxisRecordSize', -8, 'Extra bytes. Set to empty array.'),
]),
('AxisValueArray', [
('Offset', 'AxisValue', 'AxisValueCount', 0, 'Axis values'),
]),
('AxisValueFormat1', [
('uint16', 'Format', None, None, 'Format, = 1'),
('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
('uint16', 'Flags', None, None, 'Flags.'),
('NameID', 'ValueNameID', None, None, ''),
('Fixed', 'Value', None, None, ''),
]),
('AxisValueFormat2', [
('uint16', 'Format', None, None, 'Format, = 2'),
('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
('uint16', 'Flags', None, None, 'Flags.'),
('NameID', 'ValueNameID', None, None, ''),
('Fixed', 'NominalValue', None, None, ''),
('Fixed', 'RangeMinValue', None, None, ''),
('Fixed', 'RangeMaxValue', None, None, ''),
]),
('AxisValueFormat3', [
('uint16', 'Format', None, None, 'Format, = 3'),
('uint16', 'AxisIndex', None, None, 'Index into the axis record array identifying the axis of design variation to which the axis value record applies.'),
('uint16', 'Flags', None, None, 'Flags.'),
('NameID', 'ValueNameID', None, None, ''),
('Fixed', 'Value', None, None, ''),
('Fixed', 'LinkedValue', None, None, ''),
]),
#
# Variation fonts
#

View File

@ -77,10 +77,10 @@ The following tables are currently supported:
<!-- begin table list -->
BASE, CBDT, CBLC, CFF, COLR, CPAL, DSIG, EBDT, EBLC, FFTM, GDEF,
GMAP, GPKG, GPOS, GSUB, HVAR, JSTF, LTSH, MATH, META, OS/2, SING,
SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS,
TSIV, VDMX, VORG, VVAR, avar, cmap, cvt, feat, fpgm, fvar, gasp,
glyf, gvar, hdmx, head, hhea, hmtx, kern, loca, ltag, maxp, meta,
name, post, prep, sbix, trak, vhea and vmtx
STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP,
TSIS, TSIV, VDMX, VORG, VVAR, avar, cmap, cvt, feat, fpgm, fvar,
gasp, glyf, gvar, hdmx, head, hhea, hmtx, kern, loca, ltag, maxp,
meta, name, post, prep, sbix, trak, vhea and vmtx
<!-- end table list -->
Other tables are dumped as hexadecimal data.