Merge pull request #1094 from anthrotype/apple-kern
fix compilation of AAT kern tables
This commit is contained in:
commit
34f06f41a9
@ -2,7 +2,9 @@ from __future__ import print_function, division, absolute_import
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.ttLib import getSearchRange
|
||||
from fontTools.misc.textTools import safeEval, readHex
|
||||
from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
|
||||
from fontTools.misc.fixedTools import (
|
||||
fixedToFloat as fi2fl,
|
||||
floatToFixed as fl2fi)
|
||||
from . import DefaultTable
|
||||
import struct
|
||||
import sys
|
||||
@ -17,7 +19,7 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
|
||||
|
||||
def getkern(self, format):
|
||||
for subtable in self.kernTables:
|
||||
if subtable.version == format:
|
||||
if subtable.format == format:
|
||||
return subtable
|
||||
return None # not found
|
||||
|
||||
@ -33,21 +35,23 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
|
||||
else:
|
||||
self.version = version
|
||||
data = data[4:]
|
||||
tablesIndex = []
|
||||
self.kernTables = []
|
||||
for i in range(nTables):
|
||||
if self.version == 1.0:
|
||||
# Apple
|
||||
length, coverage, tupleIndex = struct.unpack(">lHH", data[:8])
|
||||
version = coverage & 0xff
|
||||
length, coverage, subtableFormat = struct.unpack(
|
||||
">LBB", data[:6])
|
||||
else:
|
||||
version, length = struct.unpack(">HH", data[:4])
|
||||
length = int(length)
|
||||
if version not in kern_classes:
|
||||
subtable = KernTable_format_unkown(version)
|
||||
# in OpenType spec the "version" field refers to the common
|
||||
# subtable header; the actual subtable format is stored in
|
||||
# the 8-15 mask bits of "coverage" field.
|
||||
# This "version" is always 0 so we ignore it here
|
||||
_, length, subtableFormat, coverage = struct.unpack(
|
||||
">HHBB", data[:6])
|
||||
if subtableFormat not in kern_classes:
|
||||
subtable = KernTable_format_unkown(subtableFormat)
|
||||
else:
|
||||
subtable = kern_classes[version]()
|
||||
subtable.apple = apple
|
||||
subtable = kern_classes[subtableFormat](apple)
|
||||
subtable.decompile(data[:length], ttFont)
|
||||
self.kernTables.append(subtable)
|
||||
data = data[length:]
|
||||
@ -59,7 +63,7 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
|
||||
nTables = 0
|
||||
if self.version == 1.0:
|
||||
# AAT Apple's "new" format.
|
||||
data = struct.pack(">ll", fl2fi(self.version, 16), nTables)
|
||||
data = struct.pack(">LL", fl2fi(self.version, 16), nTables)
|
||||
else:
|
||||
data = struct.pack(">HH", self.version, nTables)
|
||||
if hasattr(self, "kernTables"):
|
||||
@ -85,80 +89,142 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
|
||||
if format not in kern_classes:
|
||||
subtable = KernTable_format_unkown(format)
|
||||
else:
|
||||
subtable = kern_classes[format]()
|
||||
apple = self.version == 1.0
|
||||
subtable = kern_classes[format](apple)
|
||||
self.kernTables.append(subtable)
|
||||
subtable.fromXML(name, attrs, content, ttFont)
|
||||
|
||||
|
||||
class KernTable_format_0(object):
|
||||
|
||||
# 'version' is kept for backward compatibility
|
||||
version = format = 0
|
||||
|
||||
def __init__(self, apple=False):
|
||||
self.apple = apple
|
||||
|
||||
def decompile(self, data, ttFont):
|
||||
version, length, coverage = (0,0,0)
|
||||
if not self.apple:
|
||||
version, length, coverage = struct.unpack(">HHH", data[:6])
|
||||
version, length, subtableFormat, coverage = struct.unpack(
|
||||
">HHBB", data[:6])
|
||||
if version != 0:
|
||||
from fontTools.ttLib import TTLibError
|
||||
raise TTLibError(
|
||||
"unsupported kern subtable version: %d" % version)
|
||||
tupleIndex = None
|
||||
# Should we also assert length == len(data)?
|
||||
data = data[6:]
|
||||
else:
|
||||
version, length, coverage = struct.unpack(">LHH", data[:8])
|
||||
length, coverage, subtableFormat, tupleIndex = struct.unpack(
|
||||
">LBBH", data[:8])
|
||||
data = data[8:]
|
||||
self.version, self.coverage = int(version), int(coverage)
|
||||
assert self.format == subtableFormat, "unsupported format"
|
||||
self.coverage = coverage
|
||||
self.tupleIndex = tupleIndex
|
||||
|
||||
self.kernTable = kernTable = {}
|
||||
|
||||
nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8])
|
||||
nPairs, searchRange, entrySelector, rangeShift = struct.unpack(
|
||||
">HHHH", data[:8])
|
||||
data = data[8:]
|
||||
|
||||
nPairs = min(nPairs, len(data) // 6)
|
||||
datas = array.array("H", data[:6 * nPairs])
|
||||
if sys.byteorder != "big":
|
||||
if sys.byteorder != "big": # pragma: no cover
|
||||
datas.byteswap()
|
||||
it = iter(datas)
|
||||
glyphOrder = ttFont.getGlyphOrder()
|
||||
for k in range(nPairs):
|
||||
left, right, value = next(it), next(it), next(it)
|
||||
if value >= 32768: value -= 65536
|
||||
if value >= 32768:
|
||||
value -= 65536
|
||||
try:
|
||||
kernTable[(glyphOrder[left], glyphOrder[right])] = value
|
||||
except IndexError:
|
||||
# Slower, but will not throw an IndexError on an invalid glyph id.
|
||||
kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value
|
||||
if len(data) > 6 * nPairs + 4: # Ignore up to 4 bytes excess
|
||||
log.warning("excess data in 'kern' subtable: %d bytes", len(data) - 6 * nPairs)
|
||||
# Slower, but will not throw an IndexError on an invalid
|
||||
# glyph id.
|
||||
kernTable[(
|
||||
ttFont.getGlyphName(left),
|
||||
ttFont.getGlyphName(right))] = value
|
||||
if len(data) > 6 * nPairs + 4: # Ignore up to 4 bytes excess
|
||||
log.warning(
|
||||
"excess data in 'kern' subtable: %d bytes",
|
||||
len(data) - 6 * nPairs)
|
||||
|
||||
def compile(self, ttFont):
|
||||
nPairs = len(self.kernTable)
|
||||
searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
|
||||
data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
|
||||
data = struct.pack(
|
||||
">HHHH", nPairs, searchRange, entrySelector, rangeShift)
|
||||
|
||||
# yeehee! (I mean, turn names into indices)
|
||||
try:
|
||||
reverseOrder = ttFont.getReverseGlyphMap()
|
||||
kernTable = sorted((reverseOrder[left], reverseOrder[right], value) for ((left,right),value) in self.kernTable.items())
|
||||
kernTable = sorted(
|
||||
(reverseOrder[left], reverseOrder[right], value)
|
||||
for ((left, right), value) in self.kernTable.items())
|
||||
except KeyError:
|
||||
# Slower, but will not throw KeyError on invalid glyph id.
|
||||
getGlyphID = ttFont.getGlyphID
|
||||
kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
|
||||
kernTable = sorted(
|
||||
(getGlyphID(left), getGlyphID(right), value)
|
||||
for ((left, right), value) in self.kernTable.items())
|
||||
|
||||
for left, right, value in kernTable:
|
||||
data = data + struct.pack(">HHh", left, right, value)
|
||||
return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
|
||||
|
||||
if not self.apple:
|
||||
version = 0
|
||||
length = len(data) + 6
|
||||
header = struct.pack(
|
||||
">HHBB", version, length, self.format, self.coverage)
|
||||
else:
|
||||
if self.tupleIndex is None:
|
||||
# sensible default when compiling a TTX from an old fonttools
|
||||
# or when inserting a Windows-style format 0 subtable into an
|
||||
# Apple version=1.0 kern table
|
||||
log.warning("'tupleIndex' is None; default to 0")
|
||||
self.tupleIndex = 0
|
||||
length = len(data) + 8
|
||||
header = struct.pack(
|
||||
">LBBH", length, self.coverage, self.format, self.tupleIndex)
|
||||
return header + data
|
||||
|
||||
def toXML(self, writer, ttFont):
|
||||
writer.begintag("kernsubtable", coverage=self.coverage, format=0)
|
||||
attrs = dict(coverage=self.coverage, format=self.format)
|
||||
if self.apple:
|
||||
if self.tupleIndex is None:
|
||||
log.warning("'tupleIndex' is None; default to 0")
|
||||
attrs["tupleIndex"] = 0
|
||||
else:
|
||||
attrs["tupleIndex"] = self.tupleIndex
|
||||
writer.begintag("kernsubtable", **attrs)
|
||||
writer.newline()
|
||||
items = sorted(self.kernTable.items())
|
||||
for (left, right), value in items:
|
||||
writer.simpletag("pair", [
|
||||
("l", left),
|
||||
("r", right),
|
||||
("v", value)
|
||||
])
|
||||
("l", left),
|
||||
("r", right),
|
||||
("v", value)
|
||||
])
|
||||
writer.newline()
|
||||
writer.endtag("kernsubtable")
|
||||
writer.newline()
|
||||
|
||||
def fromXML(self, name, attrs, content, ttFont):
|
||||
self.coverage = safeEval(attrs["coverage"])
|
||||
self.version = safeEval(attrs["format"])
|
||||
subtableFormat = safeEval(attrs["format"])
|
||||
if self.apple:
|
||||
if "tupleIndex" in attrs:
|
||||
self.tupleIndex = safeEval(attrs["tupleIndex"])
|
||||
else:
|
||||
# previous fontTools versions didn't export tupleIndex
|
||||
log.warning(
|
||||
"Apple kern subtable is missing 'tupleIndex' attribute")
|
||||
self.tupleIndex = None
|
||||
else:
|
||||
self.tupleIndex = None
|
||||
assert subtableFormat == self.format, "unsupported format"
|
||||
if not hasattr(self, "kernTable"):
|
||||
self.kernTable = {}
|
||||
for element in content:
|
||||
|
@ -1,30 +1,370 @@
|
||||
from __future__ import print_function, absolute_import
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools import ttLib
|
||||
import unittest
|
||||
from fontTools.ttLib.tables._k_e_r_n import KernTable_format_0
|
||||
from fontTools.ttLib import newTable
|
||||
from fontTools.ttLib.tables._k_e_r_n import (
|
||||
KernTable_format_0, KernTable_format_unkown)
|
||||
from fontTools.misc.textTools import deHexStr
|
||||
from fontTools.misc.testTools import FakeFont, getXML, parseXML
|
||||
import pytest
|
||||
|
||||
class MockFont(object):
|
||||
|
||||
def getGlyphOrder(self):
|
||||
return ["glyph00000", "glyph00001", "glyph00002", "glyph00003"]
|
||||
KERN_VER_0_FMT_0_DATA = deHexStr(
|
||||
'0000 ' # 0: version=0
|
||||
'0001 ' # 2: nTables=1
|
||||
'0000 ' # 4: version=0 (bogus field, unused)
|
||||
'0020 ' # 6: length=32
|
||||
'00 ' # 8: format=0
|
||||
'01 ' # 9: coverage=1
|
||||
'0003 ' # 10: nPairs=3
|
||||
'000C ' # 12: searchRange=12
|
||||
'0001 ' # 14: entrySelector=1
|
||||
'0006 ' # 16: rangeShift=6
|
||||
'0004 000C FFD8 ' # 18: l=4, r=12, v=-40
|
||||
'0004 001C 0028 ' # 24: l=4, r=28, v=40
|
||||
'0005 0028 FFCE ' # 30: l=5, r=40, v=-50
|
||||
)
|
||||
assert len(KERN_VER_0_FMT_0_DATA) == 36
|
||||
|
||||
def getGlyphName(self, glyphID):
|
||||
return "glyph%.5d" % glyphID
|
||||
KERN_VER_0_FMT_0_XML = [
|
||||
'<version value="0"/>',
|
||||
'<kernsubtable coverage="1" format="0">',
|
||||
' <pair l="E" r="M" v="-40"/>',
|
||||
' <pair l="E" r="c" v="40"/>',
|
||||
' <pair l="F" r="o" v="-50"/>',
|
||||
'</kernsubtable>',
|
||||
]
|
||||
|
||||
class KernTable_format_0_Test(unittest.TestCase):
|
||||
KERN_VER_1_FMT_0_DATA = deHexStr(
|
||||
'0001 0000 ' # 0: version=1
|
||||
'0000 0001 ' # 4: nTables=1
|
||||
'0000 0022 ' # 8: length=34
|
||||
'00 ' # 12: coverage=0
|
||||
'00 ' # 13: format=0
|
||||
'0000 ' # 14: tupleIndex=0
|
||||
'0003 ' # 16: nPairs=3
|
||||
'000C ' # 18: searchRange=12
|
||||
'0001 ' # 20: entrySelector=1
|
||||
'0006 ' # 22: rangeShift=6
|
||||
'0004 000C FFD8 ' # 24: l=4, r=12, v=-40
|
||||
'0004 001C 0028 ' # 30: l=4, r=28, v=40
|
||||
'0005 0028 FFCE ' # 36: l=5, r=40, v=-50
|
||||
)
|
||||
assert len(KERN_VER_1_FMT_0_DATA) == 42
|
||||
|
||||
KERN_VER_1_FMT_0_XML = [
|
||||
'<version value="1.0"/>',
|
||||
'<kernsubtable coverage="0" format="0" tupleIndex="0">',
|
||||
' <pair l="E" r="M" v="-40"/>',
|
||||
' <pair l="E" r="c" v="40"/>',
|
||||
' <pair l="F" r="o" v="-50"/>',
|
||||
'</kernsubtable>',
|
||||
]
|
||||
|
||||
KERN_VER_0_FMT_UNKNOWN_DATA = deHexStr(
|
||||
'0000 ' # 0: version=0
|
||||
'0002 ' # 2: nTables=2
|
||||
'0000 ' # 4: version=0
|
||||
'000A ' # 6: length=10
|
||||
'04 ' # 8: format=4 (format 4 doesn't exist)
|
||||
'01 ' # 9: coverage=1
|
||||
'1234 5678 ' # 10: garbage...
|
||||
'0000 ' # 14: version=0
|
||||
'000A ' # 16: length=10
|
||||
'05 ' # 18: format=5 (format 5 doesn't exist)
|
||||
'01 ' # 19: coverage=1
|
||||
'9ABC DEF0 ' # 20: garbage...
|
||||
)
|
||||
assert len(KERN_VER_0_FMT_UNKNOWN_DATA) == 24
|
||||
|
||||
KERN_VER_0_FMT_UNKNOWN_XML = [
|
||||
'<version value="0"/>',
|
||||
'<kernsubtable format="4">',
|
||||
" <!-- unknown 'kern' subtable format -->",
|
||||
' 0000000A 04011234',
|
||||
' 5678 ',
|
||||
'</kernsubtable>',
|
||||
'<kernsubtable format="5">',
|
||||
"<!-- unknown 'kern' subtable format -->",
|
||||
' 0000000A 05019ABC',
|
||||
' DEF0 ',
|
||||
'</kernsubtable>',
|
||||
]
|
||||
|
||||
KERN_VER_1_FMT_UNKNOWN_DATA = deHexStr(
|
||||
'0001 0000 ' # 0: version=1
|
||||
'0000 0002 ' # 4: nTables=2
|
||||
'0000 000C ' # 8: length=12
|
||||
'00 ' # 12: coverage=0
|
||||
'04 ' # 13: format=4 (format 4 doesn't exist)
|
||||
'0000 ' # 14: tupleIndex=0
|
||||
'1234 5678' # 16: garbage...
|
||||
'0000 000C ' # 20: length=12
|
||||
'00 ' # 24: coverage=0
|
||||
'05 ' # 25: format=5 (format 5 doesn't exist)
|
||||
'0000 ' # 26: tupleIndex=0
|
||||
'9ABC DEF0 ' # 28: garbage...
|
||||
)
|
||||
assert len(KERN_VER_1_FMT_UNKNOWN_DATA) == 32
|
||||
|
||||
KERN_VER_1_FMT_UNKNOWN_XML = [
|
||||
'<version value="1"/>',
|
||||
'<kernsubtable format="4">',
|
||||
" <!-- unknown 'kern' subtable format -->",
|
||||
' 0000000C 00040000',
|
||||
' 12345678 ',
|
||||
'</kernsubtable>',
|
||||
'<kernsubtable format="5">',
|
||||
" <!-- unknown 'kern' subtable format -->",
|
||||
' 0000000C 00050000',
|
||||
' 9ABCDEF0 ',
|
||||
'</kernsubtable>',
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def font():
|
||||
return FakeFont(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"))
|
||||
|
||||
|
||||
class KernTableTest(object):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data, version",
|
||||
[
|
||||
(KERN_VER_0_FMT_0_DATA, 0),
|
||||
(KERN_VER_1_FMT_0_DATA, 1.0),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_decompile_single_format_0(self, data, font, version):
|
||||
kern = newTable("kern")
|
||||
kern.decompile(data, font)
|
||||
|
||||
assert kern.version == version
|
||||
assert len(kern.kernTables) == 1
|
||||
|
||||
st = kern.kernTables[0]
|
||||
assert st.apple is (version == 1.0)
|
||||
assert st.format == 0
|
||||
# horizontal kerning in OT kern is coverage 0x01, while in
|
||||
# AAT kern it's the default (0)
|
||||
assert st.coverage == (0 if st.apple else 1)
|
||||
assert st.tupleIndex == (0 if st.apple else None)
|
||||
assert len(st.kernTable) == 3
|
||||
assert st.kernTable == {
|
||||
('E', 'M'): -40,
|
||||
('E', 'c'): 40,
|
||||
('F', 'o'): -50
|
||||
}
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"version, expected",
|
||||
[
|
||||
(0, KERN_VER_0_FMT_0_DATA),
|
||||
(1.0, KERN_VER_1_FMT_0_DATA),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_compile_single_format_0(self, font, version, expected):
|
||||
kern = newTable("kern")
|
||||
kern.version = version
|
||||
apple = version == 1.0
|
||||
st = KernTable_format_0(apple)
|
||||
kern.kernTables = [st]
|
||||
st.coverage = (0 if apple else 1)
|
||||
st.tupleIndex = 0 if apple else None
|
||||
st.kernTable = {
|
||||
('E', 'M'): -40,
|
||||
('E', 'c'): 40,
|
||||
('F', 'o'): -50
|
||||
}
|
||||
data = kern.compile(font)
|
||||
assert data == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xml, version",
|
||||
[
|
||||
(KERN_VER_0_FMT_0_XML, 0),
|
||||
(KERN_VER_1_FMT_0_XML, 1.0),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_fromXML_single_format_0(self, xml, font, version):
|
||||
kern = newTable("kern")
|
||||
for name, attrs, content in parseXML(xml):
|
||||
kern.fromXML(name, attrs, content, ttFont=font)
|
||||
|
||||
assert kern.version == version
|
||||
assert len(kern.kernTables) == 1
|
||||
|
||||
st = kern.kernTables[0]
|
||||
assert st.apple is (version == 1.0)
|
||||
assert st.format == 0
|
||||
assert st.coverage == (0 if st.apple else 1)
|
||||
assert st.tupleIndex == (0 if st.apple else None)
|
||||
assert len(st.kernTable) == 3
|
||||
assert st.kernTable == {
|
||||
('E', 'M'): -40,
|
||||
('E', 'c'): 40,
|
||||
('F', 'o'): -50
|
||||
}
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"version, expected",
|
||||
[
|
||||
(0, KERN_VER_0_FMT_0_XML),
|
||||
(1.0, KERN_VER_1_FMT_0_XML),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_toXML_single_format_0(self, font, version, expected):
|
||||
kern = newTable("kern")
|
||||
kern.version = version
|
||||
apple = version == 1.0
|
||||
st = KernTable_format_0(apple)
|
||||
kern.kernTables = [st]
|
||||
st.coverage = 0 if apple else 1
|
||||
st.tupleIndex = 0 if apple else None
|
||||
st.kernTable = {
|
||||
('E', 'M'): -40,
|
||||
('E', 'c'): 40,
|
||||
('F', 'o'): -50
|
||||
}
|
||||
xml = getXML(kern.toXML, font)
|
||||
assert xml == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data, version, header_length, st_length",
|
||||
[
|
||||
(KERN_VER_0_FMT_UNKNOWN_DATA, 0, 4, 10),
|
||||
(KERN_VER_1_FMT_UNKNOWN_DATA, 1.0, 8, 12),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_decompile_format_unknown(
|
||||
self, data, font, version, header_length, st_length):
|
||||
kern = newTable("kern")
|
||||
kern.decompile(data, font)
|
||||
|
||||
assert kern.version == version
|
||||
assert len(kern.kernTables) == 2
|
||||
|
||||
st_data = data[header_length:]
|
||||
st0 = kern.kernTables[0]
|
||||
assert st0.format == 4
|
||||
assert st0.data == st_data[:st_length]
|
||||
st_data = st_data[st_length:]
|
||||
|
||||
st1 = kern.kernTables[1]
|
||||
assert st1.format == 5
|
||||
assert st1.data == st_data[:st_length]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"version, st_length, expected",
|
||||
[
|
||||
(0, 10, KERN_VER_0_FMT_UNKNOWN_DATA),
|
||||
(1.0, 12, KERN_VER_1_FMT_UNKNOWN_DATA),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_compile_format_unknown(self, version, st_length, expected):
|
||||
kern = newTable("kern")
|
||||
kern.version = version
|
||||
kern.kernTables = []
|
||||
|
||||
for unknown_fmt, kern_data in zip((4, 5), ("1234 5678", "9ABC DEF0")):
|
||||
if version > 0:
|
||||
coverage = 0
|
||||
header_fmt = deHexStr(
|
||||
"%08X %02X %02X %04X" % (
|
||||
st_length, coverage, unknown_fmt, 0))
|
||||
else:
|
||||
coverage = 1
|
||||
header_fmt = deHexStr(
|
||||
"%04X %04X %02X %02X" % (
|
||||
0, st_length, unknown_fmt, coverage))
|
||||
st = KernTable_format_unkown(unknown_fmt)
|
||||
st.data = header_fmt + deHexStr(kern_data)
|
||||
kern.kernTables.append(st)
|
||||
|
||||
data = kern.compile(font)
|
||||
assert data == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xml, version, st_length",
|
||||
[
|
||||
(KERN_VER_0_FMT_UNKNOWN_XML, 0, 10),
|
||||
(KERN_VER_1_FMT_UNKNOWN_XML, 1.0, 12),
|
||||
],
|
||||
ids=["version_0", "version_1"]
|
||||
)
|
||||
def test_fromXML_format_unknown(self, xml, font, version, st_length):
|
||||
kern = newTable("kern")
|
||||
for name, attrs, content in parseXML(xml):
|
||||
kern.fromXML(name, attrs, content, ttFont=font)
|
||||
|
||||
assert kern.version == version
|
||||
assert len(kern.kernTables) == 2
|
||||
|
||||
st0 = kern.kernTables[0]
|
||||
assert st0.format == 4
|
||||
assert len(st0.data) == st_length
|
||||
|
||||
st1 = kern.kernTables[1]
|
||||
assert st1.format == 5
|
||||
assert len(st1.data) == st_length
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"version", [0, 1.0], ids=["version_0", "version_1"])
|
||||
def test_toXML_format_unknown(self, font, version):
|
||||
kern = newTable("kern")
|
||||
kern.version = version
|
||||
st = KernTable_format_unkown(4)
|
||||
st.data = b"ABCD"
|
||||
kern.kernTables = [st]
|
||||
|
||||
xml = getXML(kern.toXML, font)
|
||||
|
||||
assert xml == [
|
||||
'<version value="%s"/>' % version,
|
||||
'<kernsubtable format="4">',
|
||||
' <!-- unknown \'kern\' subtable format -->',
|
||||
' 41424344 ',
|
||||
'</kernsubtable>',
|
||||
]
|
||||
|
||||
def test_getkern(self):
|
||||
table = newTable("kern")
|
||||
table.version = 0
|
||||
table.kernTables = []
|
||||
|
||||
assert table.getkern(0) is None
|
||||
|
||||
st0 = KernTable_format_0()
|
||||
table.kernTables.append(st0)
|
||||
|
||||
assert table.getkern(0) is st0
|
||||
assert table.getkern(4) is None
|
||||
|
||||
st1 = KernTable_format_unkown(4)
|
||||
table.kernTables.append(st1)
|
||||
|
||||
|
||||
class KernTable_format_0_Test(object):
|
||||
|
||||
def test_decompileBadGlyphId(self, font):
|
||||
subtable = KernTable_format_0()
|
||||
subtable.decompile(
|
||||
b'\x00' + b'\x00' + b'\x00' + b'\x1a' + b'\x00' + b'\x00' +
|
||||
b'\x00' + b'\x02' + b'\x00' * 6 +
|
||||
b'\x00' + b'\x01' + b'\x00' + b'\x03' + b'\x00' + b'\x01' +
|
||||
b'\x00' + b'\x01' + b'\xFF' + b'\xFF' + b'\x00' + b'\x02',
|
||||
font)
|
||||
assert subtable[("B", "D")] == 1
|
||||
assert subtable[("B", "glyph65535")] == 2
|
||||
|
||||
def test_decompileBadGlyphId(self):
|
||||
subtable = KernTable_format_0()
|
||||
subtable.apple = False
|
||||
subtable.decompile( b'\x00' * 6
|
||||
+ b'\x00' + b'\x02' + b'\x00' * 6
|
||||
+ b'\x00' + b'\x01' + b'\x00' + b'\x03' + b'\x00' + b'\x01'
|
||||
+ b'\x00' + b'\x01' + b'\xFF' + b'\xFF' + b'\x00' + b'\x02',
|
||||
MockFont())
|
||||
self.assertEqual(subtable[("glyph00001", "glyph00003")], 1)
|
||||
self.assertEqual(subtable[("glyph00001", "glyph65535")], 2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
sys.exit(unittest.main())
|
||||
import sys
|
||||
sys.exit(pytest.main(sys.argv))
|
||||
|
Loading…
x
Reference in New Issue
Block a user