[AAT] Support gcid
table with glyph-to-CID mapping
This commit is contained in:
parent
ee1662e57e
commit
0f05f824d2
@ -60,6 +60,7 @@ def _moduleFinderHint():
|
|||||||
from . import _f_p_g_m
|
from . import _f_p_g_m
|
||||||
from . import _f_v_a_r
|
from . import _f_v_a_r
|
||||||
from . import _g_a_s_p
|
from . import _g_a_s_p
|
||||||
|
from . import _g_c_i_d
|
||||||
from . import _g_l_y_f
|
from . import _g_l_y_f
|
||||||
from . import _g_v_a_r
|
from . import _g_v_a_r
|
||||||
from . import _h_d_m_x
|
from . import _h_d_m_x
|
||||||
|
8
Lib/fontTools/ttLib/tables/_g_c_i_d.py
Normal file
8
Lib/fontTools/ttLib/tables/_g_c_i_d.py
Normal 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
|
@ -5,7 +5,8 @@ from fontTools.misc.fixedTools import (
|
|||||||
versionToFixed as ve2fi)
|
versionToFixed as ve2fi)
|
||||||
from fontTools.misc.textTools import pad, safeEval
|
from fontTools.misc.textTools import pad, safeEval
|
||||||
from fontTools.ttLib import getSearchRange
|
from fontTools.ttLib import getSearchRange
|
||||||
from .otBase import ValueRecordFactory, CountReference, OTTableWriter
|
from .otBase import (CountReference, FormatSwitchingBaseTable,
|
||||||
|
OTTableWriter, ValueRecordFactory)
|
||||||
from .otTables import (AATStateTable, AATState, AATAction,
|
from .otTables import (AATStateTable, AATState, AATAction,
|
||||||
ContextualMorphAction)
|
ContextualMorphAction)
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@ -42,6 +43,8 @@ def buildConverters(tableSpec, tableNamespace):
|
|||||||
converterClass = SubStruct
|
converterClass = SubStruct
|
||||||
elif name == "FeatureParams":
|
elif name == "FeatureParams":
|
||||||
converterClass = FeatureParams
|
converterClass = FeatureParams
|
||||||
|
elif name == "GlyphCIDMapping":
|
||||||
|
converterClass = StructWithLength
|
||||||
else:
|
else:
|
||||||
if not tp in converterMapping and '(' not in tp:
|
if not tp in converterMapping and '(' not in tp:
|
||||||
tableName = tp
|
tableName = tp
|
||||||
@ -461,6 +464,8 @@ class StructWithLength(Struct):
|
|||||||
if conv.name == "StructLength":
|
if conv.name == "StructLength":
|
||||||
break
|
break
|
||||||
lengthIndex = len(writer.items) + convIndex
|
lengthIndex = len(writer.items) + convIndex
|
||||||
|
if isinstance(value, FormatSwitchingBaseTable):
|
||||||
|
lengthIndex += 1 # implicit Format field
|
||||||
deadbeef = {1:0xDE, 2:0xDEAD, 4:0xDEADBEEF}[conv.staticSize]
|
deadbeef = {1:0xDE, 2:0xDEAD, 4:0xDEADBEEF}[conv.staticSize]
|
||||||
|
|
||||||
before = writer.getDataLength()
|
before = writer.getDataLength()
|
||||||
@ -1077,6 +1082,52 @@ class STXHeader(BaseConverter):
|
|||||||
return state
|
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):
|
class DeltaValue(BaseConverter):
|
||||||
|
|
||||||
def read(self, reader, font, tableDict):
|
def read(self, reader, font, tableDict):
|
||||||
@ -1243,6 +1294,7 @@ converterMapping = {
|
|||||||
"VarDataValue": VarDataValue,
|
"VarDataValue": VarDataValue,
|
||||||
|
|
||||||
# AAT
|
# AAT
|
||||||
|
"GlyphCIDMap": GlyphCIDMap,
|
||||||
"MortChain": StructWithLength,
|
"MortChain": StructWithLength,
|
||||||
"MortSubtable": StructWithLength,
|
"MortSubtable": StructWithLength,
|
||||||
"MorxChain": StructWithLength,
|
"MorxChain": StructWithLength,
|
||||||
|
@ -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
|
# lcar
|
||||||
#
|
#
|
||||||
|
@ -101,9 +101,9 @@ The following tables are currently supported:
|
|||||||
GDEF, GMAP, GPKG, GPOS, GSUB, HVAR, JSTF, LTSH, MATH, META, MVAR,
|
GDEF, GMAP, GPKG, GPOS, GSUB, HVAR, JSTF, LTSH, MATH, META, MVAR,
|
||||||
OS/2, SING, STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID,
|
OS/2, SING, STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID,
|
||||||
TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX, VORG, VVAR, ankr, avar, bsln,
|
TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX, VORG, VVAR, ankr, avar, bsln,
|
||||||
cmap, cvar, cvt, feat, fpgm, fvar, gasp, glyf, gvar, hdmx, head,
|
cmap, cvar, cvt, feat, fpgm, fvar, gasp, gcid, glyf, gvar, hdmx,
|
||||||
hhea, hmtx, kern, lcar, loca, ltag, maxp, meta, mort, morx, name,
|
head, hhea, hmtx, kern, lcar, loca, ltag, maxp, meta, mort, morx,
|
||||||
opbd, post, prep, prop, sbix, trak, vhea and vmtx
|
name, opbd, post, prep, prop, sbix, trak, vhea and vmtx
|
||||||
.. end table list
|
.. end table list
|
||||||
|
|
||||||
Other tables are dumped as hexadecimal data.
|
Other tables are dumped as hexadecimal data.
|
||||||
|
70
Tests/ttLib/tables/_g_c_i_d_test.py
Normal file
70
Tests/ttLib/tables/_g_c_i_d_test.py
Normal 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())
|
Loading…
x
Reference in New Issue
Block a user