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

This commit is contained in:
Sascha Brawer 2017-09-06 10:31:35 +02:00
parent 0f05f824d2
commit db0d193db6
5 changed files with 154 additions and 5 deletions

View File

@ -0,0 +1,20 @@
# coding: utf-8
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from .otBase import BaseTTXConverter
# The AAT cidg table has almost the same structure as gidc,
# just mapping CIDs to GlyphIDs instead of the reverse direction.
#
# It is useful for fonts that may be used by a PDF renderer in lieu of
# a font reference with a known glyph collection but no subsetted
# glyphs. For instance, a PDF can say “please use a font conforming
# to Adobe-Japan-1”; the cidg mapping is necessary if the font is,
# say, a TrueType font. gidc is lossy for this purpose and is
# obsoleted by cidg.
#
# For example, the first font in /System/Library/Fonts/PingFang.ttc
# (which Apple ships pre-installed on MacOS 10.12.6) has a cidg table.
class table__c_i_d_g(BaseTTXConverter):
pass

View File

@ -43,7 +43,7 @@ def buildConverters(tableSpec, tableNamespace):
converterClass = SubStruct
elif name == "FeatureParams":
converterClass = FeatureParams
elif name == "GlyphCIDMapping":
elif name in ("CIDGlyphMapping", "GlyphCIDMapping"):
converterClass = StructWithLength
else:
if not tp in converterMapping and '(' not in tp:
@ -1082,6 +1082,43 @@ class STXHeader(BaseConverter):
return state
class CIDGlyphMap(BaseConverter):
def read(self, reader, font, tableDict):
numCIDs = reader.readUShort()
result = {}
for cid, glyphID in enumerate(reader.readUShortArray(numCIDs)):
if glyphID != 0xFFFF:
result[cid] = font.getGlyphName(glyphID)
return result
def write(self, writer, font, tableDict, value, repeatIndex=None):
items = {cid: font.getGlyphID(glyph)
for cid, glyph in value.items()}
count = max(items) + 1 if items else 0
writer.writeUShort(count)
for cid in range(count):
writer.writeUShort(items.get(cid, 0xFFFF))
def xmlRead(self, attrs, content, font):
result = {}
for eName, eAttrs, _eContent in filter(istuple, content):
if eName == "CID":
result[safeEval(eAttrs["cid"])] = \
eAttrs["glyph"].strip()
return result
def xmlWrite(self, xmlWriter, font, value, name, attrs):
xmlWriter.begintag(name, attrs)
xmlWriter.newline()
for cid, glyph in sorted(value.items()):
if glyph is not None and glyph != 0xFFFF:
xmlWriter.simpletag(
"CID", cid=cid, glyph=glyph)
xmlWriter.newline()
xmlWriter.endtag(name)
xmlWriter.newline()
class GlyphCIDMap(BaseConverter):
def read(self, reader, font, tableDict):
glyphOrder = font.getGlyphOrder()
@ -1096,7 +1133,7 @@ class GlyphCIDMap(BaseConverter):
for glyphID in range(min(len(cids), len(glyphOrder))):
cid = cids[glyphID]
if cid != 0xFFFF:
result[glyphOrder[glyphID]] = cids[glyphID]
result[glyphOrder[glyphID]] = cid
return result
def write(self, writer, font, tableDict, value, repeatIndex=None):
@ -1294,6 +1331,7 @@ converterMapping = {
"VarDataValue": VarDataValue,
# AAT
"CIDGlyphMap": CIDGlyphMap,
"GlyphCIDMap": GlyphCIDMap,
"MortChain": StructWithLength,
"MortSubtable": StructWithLength,

View File

@ -1249,6 +1249,27 @@ otData = [
]),
#
# cidg
#
('cidg', [
('struct', 'CIDGlyphMapping', None, None, 'CID-to-glyph mapping table.'),
]),
('CIDGlyphMappingFormat0', [
('uint16', 'Format', None, None, 'Format of the CID-to-glyph 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.'),
('CIDGlyphMap', 'Mapping', None, None, 'A mapping from CIDs to the glyphs in the font, starting with CID 0. If a CID from the identified collection has no glyph in the font, 0xFFFF is used'),
]),
#
# feat
#
@ -1300,7 +1321,7 @@ otData = [
('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'),
('GlyphCIDMap', 'Mapping', 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'),
]),

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.6, the first font in /System/Library/Fonts/PingFang.ttc
# has a cidg table with a similar structure as this test data, just larger.
CIDG_DATA = deHexStr(
"0000 0000 " # 0: Format=0, Flags=0
"0000 0098 " # 4: StructLength=152
"0000 " # 8: Registry=0
"41 64 6F 62 65 " # 10: RegistryName="Adobe"
+ ("00" * 59) + # 15: <padding>
"0002 " # 74: Order=2
"43 4E 53 31 " # 76: Order="CNS1"
+ ("00" * 60) + # 80: <padding>
"0000 " # 140: SupplementVersion=0
"0004 " # 142: Count
"0000 " # 144: GlyphID[0]=.notdef
"FFFF " # 146: CIDs[1]=<None>
"0003 " # 148: CIDs[2]=C
"0001 " # 150: CIDs[3]=A
) # 152: <end>
assert len(CIDG_DATA) == 152, len(CIDG_DATA)
CIDG_XML = [
'<CIDGlyphMapping Format="0">',
' <DataFormat value="0"/>',
' <!-- StructLength=152 -->',
' <Registry value="0"/>',
' <RegistryName value="Adobe"/>',
' <Order value="2"/>',
' <OrderName value="CNS1"/>',
' <SupplementVersion value="0"/>',
' <Mapping>',
' <CID cid="0" glyph=".notdef"/>',
' <CID cid="2" glyph="C"/>',
' <CID cid="3" glyph="A"/>',
' </Mapping>',
'</CIDGlyphMapping>',
]
class GCIDTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.maxDiff = None
cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D'])
def testDecompileToXML(self):
table = newTable('cidg')
table.decompile(CIDG_DATA, self.font)
self.assertEqual(getXML(table.toXML, self.font), CIDG_XML)
def testCompileFromXML(self):
table = newTable('cidg')
for name, attrs, content in parseXML(CIDG_XML):
table.fromXML(name, attrs, content, font=self.font)
self.assertEqual(hexStr(table.compile(self.font)),
hexStr(CIDG_DATA))
if __name__ == '__main__':
import sys
sys.exit(unittest.main())

View File

@ -37,11 +37,11 @@ GCID_XML = [
' <Order value="3"/>',
' <OrderName value="Korea1"/>',
' <SupplementVersion value="1"/>',
' <CIDs>',
' <Mapping>',
' <CID glyph=".notdef" value="4660"/>',
' <CID glyph="B" value="7"/>',
' <CID glyph="C" value="57072"/>',
' </CIDs>',
' </Mapping>',
'</GlyphCIDMapping>',
]