[AAT] Add Char64 converter for 64-byte ASCII character strings

Used by Apple Advanced Typography tables, for example `gcid`.
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html

https://github.com/fonttools/fonttools/issues/178
This commit is contained in:
Sascha Brawer 2017-03-10 12:17:14 +01:00
parent d7a298f36d
commit ea1adf42a7
2 changed files with 86 additions and 1 deletions

View File

@ -306,6 +306,37 @@ class Version(BaseConverter):
return fl2fi(v, 16)
class Char64(SimpleValue):
"""An ASCII string with up to 64 characters.
Unused character positions are filled with 0x00 bytes.
Used in Apple AAT fonts in the `gcid` table.
"""
staticSize = 64
def read(self, reader, font, tableDict):
data = reader.readData(self.staticSize)
zeroPos = data.find(b"\0")
if zeroPos >= 0:
data = data[:zeroPos]
s = tounicode(data, encoding="ascii", errors="replace")
if s != tounicode(data, encoding="ascii", errors="ignore"):
log.warning('replaced non-ASCII characters in "%s"' %
s)
return s
def write(self, writer, font, tableDict, value, repeatIndex=None):
data = tobytes(value, encoding="ascii", errors="replace")
if data != tobytes(value, encoding="ascii", errors="ignore"):
log.warning('replacing non-ASCII characters in "%s"' %
value)
if len(data) > self.staticSize:
log.warning('truncating overlong "%s" to %d bytes' %
(value, self.staticSize))
data = (data + b"\0" * self.staticSize)[:self.staticSize]
writer.writeData(data)
class Struct(BaseConverter):
def getRecordSize(self, reader):
@ -628,6 +659,7 @@ converterMapping = {
"uint16": UShort,
"uint24": UInt24,
"uint32": ULong,
"char64": Char64,
"Version": Version,
"Tag": Tag,
"GlyphID": GlyphID,

View File

@ -1,4 +1,6 @@
from __future__ import print_function, division, absolute_import
# coding: utf-8
from __future__ import print_function, division, absolute_import, \
unicode_literals
from fontTools.misc.py23 import *
from fontTools.misc.loggingTools import CapturingLogHandler
from fontTools.misc.testTools import FakeFont, makeXMLWriter
@ -9,6 +11,57 @@ from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
import unittest
class Char64Test(unittest.TestCase):
font = FakeFont([])
converter = otConverters.Char64("char64", 0, None, None)
def test_read(self):
reader = OTTableReader(b"Hello\0junk after zero byte" + 100 * b"\0")
self.assertEqual(self.converter.read(reader, self.font, {}), "Hello")
self.assertEqual(reader.pos, 64)
def test_read_replace_not_ascii(self):
reader = OTTableReader(b"Hello \xE4 world" + 100 * b"\0")
with CapturingLogHandler(otConverters.log, "WARNING") as captor:
data = self.converter.read(reader, self.font, {})
self.assertEqual(data, "Hello <20> world")
self.assertEqual(reader.pos, 64)
self.assertIn('replaced non-ASCII characters in "Hello <20> world"',
[r.msg for r in captor.records])
def test_write(self):
writer = OTTableWriter()
self.converter.write(writer, self.font, {}, "Hello world")
self.assertEqual(writer.getData(), b"Hello world" + 53 * b"\0")
def test_write_replace_not_ascii(self):
writer = OTTableWriter()
with CapturingLogHandler(otConverters.log, "WARNING") as captor:
self.converter.write(writer, self.font, {}, "Hello 🌍")
self.assertEqual(writer.getData(), b"Hello ?" + 57 * b"\0")
self.assertIn('replacing non-ASCII characters in "Hello 🌍"',
[r.msg for r in captor.records])
def test_write_truncated(self):
writer = OTTableWriter()
with CapturingLogHandler(otConverters.log, "WARNING") as captor:
self.converter.write(writer, self.font, {}, "A" * 80)
self.assertEqual(writer.getData(), b"A" * 64)
self.assertIn('truncating overlong "' + "A" * 80 + '" to 64 bytes',
[r.msg for r in captor.records])
def test_xmlRead(self):
value = self.converter.xmlRead({"value": "Foo"}, [], self.font)
self.assertEqual(value, "Foo")
def test_xmlWrite(self):
writer = makeXMLWriter()
self.converter.xmlWrite(writer, self.font, "Hello world", "Element",
[("attr", "v")])
xml = writer.file.getvalue().decode("utf-8").rstrip()
self.assertEqual(xml, '<Element attr="v" value="Hello world"/>')
class GlyphIDTest(unittest.TestCase):
font = FakeFont(".notdef A B C".split())
converter = otConverters.GlyphID('GlyphID', 0, None, None)