[AAT] Support gcid table with glyph-to-CID mapping

This commit is contained in:
Sascha Brawer 2017-09-06 00:46:55 +02:00
parent ee1662e57e
commit 0f05f824d2
6 changed files with 156 additions and 4 deletions

View File

@ -60,6 +60,7 @@ def _moduleFinderHint():
from . import _f_p_g_m
from . import _f_v_a_r
from . import _g_a_s_p
from . import _g_c_i_d
from . import _g_l_y_f
from . import _g_v_a_r
from . import _h_d_m_x

View File

@ -0,0 +1,8 @@
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from .otBase import BaseTTXConverter
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
class table__g_c_i_d(BaseTTXConverter):
pass

View File

@ -5,7 +5,8 @@ from fontTools.misc.fixedTools import (
versionToFixed as ve2fi)
from fontTools.misc.textTools import pad, safeEval
from fontTools.ttLib import getSearchRange
from .otBase import ValueRecordFactory, CountReference, OTTableWriter
from .otBase import (CountReference, FormatSwitchingBaseTable,
OTTableWriter, ValueRecordFactory)
from .otTables import (AATStateTable, AATState, AATAction,
ContextualMorphAction)
from functools import partial
@ -42,6 +43,8 @@ def buildConverters(tableSpec, tableNamespace):
converterClass = SubStruct
elif name == "FeatureParams":
converterClass = FeatureParams
elif name == "GlyphCIDMapping":
converterClass = StructWithLength
else:
if not tp in converterMapping and '(' not in tp:
tableName = tp
@ -461,6 +464,8 @@ class StructWithLength(Struct):
if conv.name == "StructLength":
break
lengthIndex = len(writer.items) + convIndex
if isinstance(value, FormatSwitchingBaseTable):
lengthIndex += 1 # implicit Format field
deadbeef = {1:0xDE, 2:0xDEAD, 4:0xDEADBEEF}[conv.staticSize]
before = writer.getDataLength()
@ -1077,6 +1082,52 @@ class STXHeader(BaseConverter):
return state
class GlyphCIDMap(BaseConverter):
def read(self, reader, font, tableDict):
glyphOrder = font.getGlyphOrder()
count = reader.readUShort()
cids = reader.readUShortArray(count)
if count > len(glyphOrder):
log.warning("GlyphCIDMap has %d elements, "
"but the font has only %d glyphs; "
"ignoring the rest" %
(count, len(glyphOrder)))
result = {}
for glyphID in range(min(len(cids), len(glyphOrder))):
cid = cids[glyphID]
if cid != 0xFFFF:
result[glyphOrder[glyphID]] = cids[glyphID]
return result
def write(self, writer, font, tableDict, value, repeatIndex=None):
items = {font.getGlyphID(g): cid
for g, cid in value.items()
if cid is not None and cid != 0xFFFF}
count = max(items) + 1 if items else 0
writer.writeUShort(count)
for glyphID in range(count):
writer.writeUShort(items.get(glyphID, 0xFFFF))
def xmlRead(self, attrs, content, font):
result = {}
for eName, eAttrs, _eContent in filter(istuple, content):
if eName == "CID":
result[eAttrs["glyph"]] = \
safeEval(eAttrs["value"])
return result
def xmlWrite(self, xmlWriter, font, value, name, attrs):
xmlWriter.begintag(name, attrs)
xmlWriter.newline()
for glyph, cid in sorted(value.items()):
if cid is not None and cid != 0xFFFF:
xmlWriter.simpletag(
"CID", glyph=glyph, value=cid)
xmlWriter.newline()
xmlWriter.endtag(name)
xmlWriter.newline()
class DeltaValue(BaseConverter):
def read(self, reader, font, tableDict):
@ -1243,6 +1294,7 @@ converterMapping = {
"VarDataValue": VarDataValue,
# AAT
"GlyphCIDMap": GlyphCIDMap,
"MortChain": StructWithLength,
"MortSubtable": StructWithLength,
"MorxChain": StructWithLength,

View File

@ -1283,6 +1283,27 @@ otData = [
]),
#
# gcid
#
('gcid', [
('struct', 'GlyphCIDMapping', None, None, 'Glyph to CID mapping table.'),
]),
('GlyphCIDMappingFormat0', [
('uint16', 'Format', None, None, 'Format of the glyph-to-CID mapping table, = 0.'),
('uint16', 'DataFormat', None, None, 'Currenty unused, set to zero.'),
('uint32', 'StructLength', None, None, 'Size of the table in bytes.'),
('uint16', 'Registry', None, None, 'The registry ID.'),
('char64', 'RegistryName', None, None, 'The registry name in ASCII; unused bytes should be set to 0.'),
('uint16', 'Order', None, None, 'The order ID.'),
('char64', 'OrderName', None, None, 'The order name in ASCII; unused bytes should be set to 0.'),
('uint16', 'SupplementVersion', None, None, 'The supplement version.'),
('GlyphCIDMap', 'CIDs', None, None, 'The CIDs for the glyphs in the font, starting with glyph 0. If a glyph does not correspond to a CID in the identified collection, 0xFFFF is used'),
]),
#
# lcar
#

View File

@ -101,9 +101,9 @@ The following tables are currently supported:
GDEF, GMAP, GPKG, GPOS, GSUB, HVAR, JSTF, LTSH, MATH, META, MVAR,
OS/2, SING, STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID,
TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX, VORG, VVAR, ankr, avar, bsln,
cmap, cvar, cvt, feat, fpgm, fvar, gasp, glyf, gvar, hdmx, head,
hhea, hmtx, kern, lcar, loca, ltag, maxp, meta, mort, morx, name,
opbd, post, prep, prop, sbix, trak, vhea and vmtx
cmap, cvar, cvt, feat, fpgm, fvar, gasp, gcid, glyf, gvar, hdmx,
head, hhea, hmtx, kern, lcar, loca, ltag, maxp, meta, mort, morx,
name, opbd, post, prep, prop, sbix, trak, vhea and vmtx
.. end table list
Other tables are dumped as hexadecimal data.

View File

@ -0,0 +1,70 @@
# coding: utf-8
from __future__ import print_function, division, absolute_import, unicode_literals
from fontTools.misc.py23 import *
from fontTools.misc.testTools import FakeFont, getXML, parseXML
from fontTools.misc.textTools import deHexStr, hexStr
from fontTools.ttLib import newTable
import unittest
# On macOS X 10.12.3, the font /Library/Fonts/AppleGothic.ttf has a gcid
# table with a similar structure as this test data, just more CIDs.
GCID_DATA = deHexStr(
"0000 0000 " # 0: Format=0, Flags=0
"0000 0098 " # 4: Size=152
"0000 " # 8: Registry=0
"41 64 6F 62 65 " # 10: RegistryName="Adobe"
+ ("00" * 59) + # 15: <padding>
"0003 " # 74: Order=3
"4B 6F 72 65 61 31 " # 76: Order="Korea1"
+ ("00" * 58) + # 82: <padding>
"0001 " # 140: SupplementVersion
"0004 " # 142: Count
"1234 " # 144: CIDs[0/.notdef]=4660
"FFFF " # 146: CIDs[1/A]=None
"0007 " # 148: CIDs[2/B]=7
"DEF0 " # 150: CIDs[3/C]=57072
) # 152: <end>
assert len(GCID_DATA) == 152, len(GCID_DATA)
GCID_XML = [
'<GlyphCIDMapping Format="0">',
' <DataFormat value="0"/>',
' <!-- StructLength=152 -->',
' <Registry value="0"/>',
' <RegistryName value="Adobe"/>',
' <Order value="3"/>',
' <OrderName value="Korea1"/>',
' <SupplementVersion value="1"/>',
' <CIDs>',
' <CID glyph=".notdef" value="4660"/>',
' <CID glyph="B" value="7"/>',
' <CID glyph="C" value="57072"/>',
' </CIDs>',
'</GlyphCIDMapping>',
]
class GCIDTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.maxDiff = None
cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D'])
def testDecompileToXML(self):
table = newTable('gcid')
table.decompile(GCID_DATA, self.font)
self.assertEqual(getXML(table.toXML, self.font), GCID_XML)
def testCompileFromXML(self):
table = newTable('gcid')
for name, attrs, content in parseXML(GCID_XML):
table.fromXML(name, attrs, content, font=self.font)
self.assertEqual(hexStr(table.compile(self.font)),
hexStr(GCID_DATA))
if __name__ == '__main__':
import sys
sys.exit(unittest.main())