blob: be0fe9aa4b3d6b91e628231b2b2fc97029a7cbe7 [file] [log] [blame]
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 itertools
import pytest
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
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>",
]
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>",
]
KERN_VER_0_FMT_0_OVERFLOWING_DATA = deHexStr(
"0000 " # 0: version=0
"0001 " # 2: nTables=1
"0000 " # 4: version=0 (bogus field, unused)
"0274 " # 6: length=628 (bogus value for 66164 % 0x10000)
"00 " # 8: format=0
"01 " # 9: coverage=1
"2B11 " # 10: nPairs=11025
"C000 " # 12: searchRange=49152
"000D " # 14: entrySelector=13
"4266 " # 16: rangeShift=16998
) + deHexStr(
" ".join(
"%04X %04X %04X" % (a, b, 0)
for (a, b) in itertools.product(range(105), repeat=2)
)
)
@pytest.fixture
def font():
return FakeFont(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"))
@pytest.fixture
def overflowing_font():
return FakeFont(["glyph%i" % i for i in range(105)])
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_compileOverflowingSubtable(self, overflowing_font):
font = overflowing_font
kern = newTable("kern")
kern.version = 0
st = KernTable_format_0(0)
kern.kernTables = [st]
st.coverage = 1
st.tupleIndex = None
st.kernTable = {
(a, b): 0 for (a, b) in itertools.product(font.getGlyphOrder(), repeat=2)
}
assert len(st.kernTable) == 11025
data = kern.compile(font)
assert data == KERN_VER_0_FMT_0_OVERFLOWING_DATA
def test_decompileOverflowingSubtable(self, overflowing_font):
font = overflowing_font
data = KERN_VER_0_FMT_0_OVERFLOWING_DATA
kern = newTable("kern")
kern.decompile(data, font)
st = kern.kernTables[0]
assert st.kernTable == {
(a, b): 0 for (a, b) in itertools.product(font.getGlyphOrder(), repeat=2)
}
if __name__ == "__main__":
import sys
sys.exit(pytest.main(sys.argv))