[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:
parent
d7a298f36d
commit
ea1adf42a7
@ -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,
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user