[morx] Parametrize AATLookup value type in otData
This commit is contained in:
parent
e8f21cb571
commit
020f7391ef
@ -551,35 +551,34 @@ class ValueRecord(ValueFormat):
|
|||||||
class AATLookup(BaseConverter):
|
class AATLookup(BaseConverter):
|
||||||
BIN_SEARCH_HEADER_SIZE = 10
|
BIN_SEARCH_HEADER_SIZE = 10
|
||||||
|
|
||||||
valueConverter = GlyphID(name='Glyph', repeat=None, aux=None)
|
|
||||||
|
|
||||||
# TODO: Use self.valueConverter for reading values.
|
|
||||||
def read(self, reader, font, tableDict):
|
def read(self, reader, font, tableDict):
|
||||||
format = reader.readUShort()
|
format = reader.readUShort()
|
||||||
|
conv = self.tableClass(name='Value', repeat=None, aux=None)
|
||||||
# TODO: Do not prune the results.
|
# TODO: Do not prune the results.
|
||||||
prune = lambda x: {k:v for k,v in x.items() if k != v}
|
prune = lambda x: {k:v for k,v in x.items() if k != v}
|
||||||
if format == 0:
|
if format == 0:
|
||||||
return prune(self.readFormat0(reader, font))
|
return prune(self.readFormat0(reader, font, conv))
|
||||||
elif format == 2:
|
elif format == 2:
|
||||||
return prune(self.readFormat2(reader, font))
|
return prune(self.readFormat2(reader, font, conv))
|
||||||
elif format == 4:
|
elif format == 4:
|
||||||
return prune(self.readFormat4(reader, font))
|
return prune(self.readFormat4(reader, font, conv))
|
||||||
elif format == 6:
|
elif format == 6:
|
||||||
return prune(self.readFormat6(reader, font))
|
return prune(self.readFormat6(reader, font, conv))
|
||||||
elif format == 8:
|
elif format == 8:
|
||||||
return prune(self.readFormat8(reader, font))
|
return prune(self.readFormat8(reader, font, conv))
|
||||||
else:
|
else:
|
||||||
assert False, "unsupported lookup format: %d" % format
|
assert False, "unsupported lookup format: %d" % format
|
||||||
|
|
||||||
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
||||||
values = list(sorted([(font.getGlyphID(glyph), val)
|
values = list(sorted([(font.getGlyphID(glyph), val)
|
||||||
for glyph, val in value.items()]))
|
for glyph, val in value.items()]))
|
||||||
|
conv = self.tableClass(name='Value', repeat=None, aux=None)
|
||||||
# TODO: Also implement format 4.
|
# TODO: Also implement format 4.
|
||||||
formats = list(sorted(filter(None, [
|
formats = list(sorted(filter(None, [
|
||||||
self.buildFormat0(writer, font, values),
|
self.buildFormat0(writer, font, conv, values),
|
||||||
self.buildFormat2(writer, font, values),
|
self.buildFormat2(writer, font, conv, values),
|
||||||
self.buildFormat6(writer, font, values),
|
self.buildFormat6(writer, font, conv, values),
|
||||||
self.buildFormat8(writer, font, values),
|
self.buildFormat8(writer, font, conv, values),
|
||||||
])))
|
])))
|
||||||
# We use the format ID as secondary sort key to make the output
|
# We use the format ID as secondary sort key to make the output
|
||||||
# deterministic when multiple formats have same encoded size.
|
# deterministic when multiple formats have same encoded size.
|
||||||
@ -601,22 +600,23 @@ class AATLookup(BaseConverter):
|
|||||||
writer.writeUShort(entrySelector)
|
writer.writeUShort(entrySelector)
|
||||||
writer.writeUShort(rangeShift)
|
writer.writeUShort(rangeShift)
|
||||||
|
|
||||||
def buildFormat0(self, writer, font, values):
|
def buildFormat0(self, writer, font, valueConverter, values):
|
||||||
numGlyphs = len(font.getGlyphOrder())
|
numGlyphs = len(font.getGlyphOrder())
|
||||||
if len(values) != numGlyphs:
|
if len(values) != numGlyphs:
|
||||||
return None
|
return None
|
||||||
valueSize = self.valueConverter.staticSize
|
valueSize = valueConverter.staticSize
|
||||||
return (2 + numGlyphs * valueSize, 0,
|
return (2 + numGlyphs * valueSize, 0,
|
||||||
lambda: self.writeFormat0(writer, font, values))
|
lambda: self.writeFormat0(
|
||||||
|
writer, font, valueConverter, values))
|
||||||
|
|
||||||
def writeFormat0(self, writer, font, values):
|
def writeFormat0(self, writer, font, valueConverter, values):
|
||||||
writer.writeUShort(0)
|
writer.writeUShort(0)
|
||||||
for glyphID_, value in values:
|
for glyphID_, value in values:
|
||||||
self.valueConverter.write(
|
valueConverter.write(
|
||||||
writer, font, tableDict=None,
|
writer, font, tableDict=None,
|
||||||
value=value, repeatIndex=None)
|
value=value, repeatIndex=None)
|
||||||
|
|
||||||
def buildFormat2(self, writer, font, values):
|
def buildFormat2(self, writer, font, valueConverter, values):
|
||||||
segStart, segValue = values[0]
|
segStart, segValue = values[0]
|
||||||
segEnd = segStart
|
segEnd = segStart
|
||||||
segments = []
|
segments = []
|
||||||
@ -628,87 +628,89 @@ class AATLookup(BaseConverter):
|
|||||||
else:
|
else:
|
||||||
segEnd = glyphID
|
segEnd = glyphID
|
||||||
segments.append((segStart, segEnd, segValue))
|
segments.append((segStart, segEnd, segValue))
|
||||||
valueSize = self.valueConverter.staticSize
|
valueSize = valueConverter.staticSize
|
||||||
numUnits, unitSize = len(segments) + 1, valueSize + 4
|
numUnits, unitSize = len(segments) + 1, valueSize + 4
|
||||||
return (2 + self.BIN_SEARCH_HEADER_SIZE + numUnits * unitSize, 2,
|
return (2 + self.BIN_SEARCH_HEADER_SIZE + numUnits * unitSize, 2,
|
||||||
lambda: self.writeFormat2(writer, font, segments))
|
lambda: self.writeFormat2(
|
||||||
|
writer, font, valueConverter, segments))
|
||||||
|
|
||||||
def writeFormat2(self, writer, font, segments):
|
def writeFormat2(self, writer, font, valueConverter, segments):
|
||||||
writer.writeUShort(2)
|
writer.writeUShort(2)
|
||||||
valueSize = self.valueConverter.staticSize
|
valueSize = valueConverter.staticSize
|
||||||
numUnits, unitSize = len(segments) + 1, valueSize + 4
|
numUnits, unitSize = len(segments) + 1, valueSize + 4
|
||||||
self.writeBinSearchHeader(writer, numUnits, unitSize)
|
self.writeBinSearchHeader(writer, numUnits, unitSize)
|
||||||
for firstGlyph, lastGlyph, value in segments:
|
for firstGlyph, lastGlyph, value in segments:
|
||||||
writer.writeUShort(lastGlyph)
|
writer.writeUShort(lastGlyph)
|
||||||
writer.writeUShort(firstGlyph)
|
writer.writeUShort(firstGlyph)
|
||||||
self.valueConverter.write(
|
valueConverter.write(
|
||||||
writer, font, tableDict=None,
|
writer, font, tableDict=None,
|
||||||
value=value, repeatIndex=None)
|
value=value, repeatIndex=None)
|
||||||
writer.writeUShort(0xFFFF)
|
writer.writeUShort(0xFFFF)
|
||||||
writer.writeUShort(0xFFFF)
|
writer.writeUShort(0xFFFF)
|
||||||
writer.writeData(b'\xFF' * valueSize)
|
writer.writeData(b'\xFF' * valueSize)
|
||||||
|
|
||||||
def buildFormat6(self, writer, font, values):
|
def buildFormat6(self, writer, font, valueConverter, values):
|
||||||
valueSize = self.valueConverter.staticSize
|
valueSize = valueConverter.staticSize
|
||||||
numUnits, unitSize = len(values) + 1, valueSize + 2
|
numUnits, unitSize = len(values) + 1, valueSize + 2
|
||||||
return (2 + self.BIN_SEARCH_HEADER_SIZE + numUnits * unitSize, 6,
|
return (2 + self.BIN_SEARCH_HEADER_SIZE + numUnits * unitSize, 6,
|
||||||
lambda: self.writeFormat6(writer, font, values))
|
lambda: self.writeFormat6(
|
||||||
|
writer, font, valueConverter, values))
|
||||||
|
|
||||||
def writeFormat6(self, writer, font, values):
|
def writeFormat6(self, writer, font, valueConverter, values):
|
||||||
writer.writeUShort(6)
|
writer.writeUShort(6)
|
||||||
valueSize = self.valueConverter.staticSize
|
valueSize = valueConverter.staticSize
|
||||||
numUnits, unitSize = len(values) + 1, valueSize + 2
|
numUnits, unitSize = len(values) + 1, valueSize + 2
|
||||||
self.writeBinSearchHeader(writer, numUnits, unitSize)
|
self.writeBinSearchHeader(writer, numUnits, unitSize)
|
||||||
for glyphID, value in values:
|
for glyphID, value in values:
|
||||||
writer.writeUShort(glyphID)
|
writer.writeUShort(glyphID)
|
||||||
self.valueConverter.write(
|
valueConverter.write(
|
||||||
writer, font, tableDict=None,
|
writer, font, tableDict=None,
|
||||||
value=value, repeatIndex=None)
|
value=value, repeatIndex=None)
|
||||||
writer.writeUShort(0xFFFF)
|
writer.writeUShort(0xFFFF)
|
||||||
writer.writeData(b'\xFF' * valueSize)
|
writer.writeData(b'\xFF' * valueSize)
|
||||||
|
|
||||||
def buildFormat8(self, writer, font, values):
|
def buildFormat8(self, writer, font, valueConverter, values):
|
||||||
minGlyphID, maxGlyphID = values[0][0], values[-1][0]
|
minGlyphID, maxGlyphID = values[0][0], values[-1][0]
|
||||||
if len(values) != maxGlyphID - minGlyphID + 1:
|
if len(values) != maxGlyphID - minGlyphID + 1:
|
||||||
return None
|
return None
|
||||||
valueSize = self.valueConverter.staticSize
|
valueSize = valueConverter.staticSize
|
||||||
return (6 + len(values) * valueSize, 8,
|
return (6 + len(values) * valueSize, 8,
|
||||||
lambda: self.writeFormat8(writer, font, values))
|
lambda: self.writeFormat8(
|
||||||
|
writer, font, valueConverter, values))
|
||||||
|
|
||||||
def writeFormat8(self, writer, font, values):
|
def writeFormat8(self, writer, font, valueConverter, values):
|
||||||
firstGlyphID = values[0][0]
|
firstGlyphID = values[0][0]
|
||||||
writer.writeUShort(8)
|
writer.writeUShort(8)
|
||||||
writer.writeUShort(firstGlyphID)
|
writer.writeUShort(firstGlyphID)
|
||||||
writer.writeUShort(len(values))
|
writer.writeUShort(len(values))
|
||||||
for _, value in values:
|
for _, value in values:
|
||||||
self.valueConverter.write(
|
valueConverter.write(
|
||||||
writer, font, tableDict=None,
|
writer, font, tableDict=None,
|
||||||
value=value, repeatIndex=None)
|
value=value, repeatIndex=None)
|
||||||
|
|
||||||
def readFormat0(self, reader, font):
|
def readFormat0(self, reader, font, valueConverter):
|
||||||
numGlyphs = len(font.getGlyphOrder())
|
numGlyphs = len(font.getGlyphOrder())
|
||||||
data = self.valueConverter.readArray(
|
data = valueConverter.readArray(
|
||||||
reader, font, tableDict=None, count=numGlyphs)
|
reader, font, tableDict=None, count=numGlyphs)
|
||||||
return {font.getGlyphName(k): value
|
return {font.getGlyphName(k): value
|
||||||
for k, value in enumerate(data)}
|
for k, value in enumerate(data)}
|
||||||
|
|
||||||
def readFormat2(self, reader, font):
|
def readFormat2(self, reader, font, valueConverter):
|
||||||
mapping = {}
|
mapping = {}
|
||||||
pos = reader.pos - 2 # start of table is at UShort for format
|
pos = reader.pos - 2 # start of table is at UShort for format
|
||||||
unitSize, numUnits = reader.readUShort(), reader.readUShort()
|
unitSize, numUnits = reader.readUShort(), reader.readUShort()
|
||||||
assert unitSize >= 4 + self.valueConverter.staticSize, unitSize
|
assert unitSize >= 4 + valueConverter.staticSize, unitSize
|
||||||
for i in range(numUnits):
|
for i in range(numUnits):
|
||||||
reader.seek(pos + i * unitSize + 12)
|
reader.seek(pos + i * unitSize + 12)
|
||||||
last = reader.readUShort()
|
last = reader.readUShort()
|
||||||
first = reader.readUShort()
|
first = reader.readUShort()
|
||||||
value = self.valueConverter.read(
|
value = valueConverter.read(reader, font, tableDict=None)
|
||||||
reader, font, tableDict=None)
|
|
||||||
if last != 0xFFFF:
|
if last != 0xFFFF:
|
||||||
for k in range(first, last + 1):
|
for k in range(first, last + 1):
|
||||||
mapping[font.getGlyphName(k)] = value
|
mapping[font.getGlyphName(k)] = value
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
def readFormat4(self, reader, font):
|
def readFormat4(self, reader, font, valueConverter):
|
||||||
mapping = {}
|
mapping = {}
|
||||||
pos = reader.pos - 2 # start of table is at UShort for format
|
pos = reader.pos - 2 # start of table is at UShort for format
|
||||||
unitSize = reader.readUShort()
|
unitSize = reader.readUShort()
|
||||||
@ -720,31 +722,31 @@ class AATLookup(BaseConverter):
|
|||||||
offset = reader.readUShort()
|
offset = reader.readUShort()
|
||||||
if last != 0xFFFF:
|
if last != 0xFFFF:
|
||||||
dataReader = reader.getSubReader(pos + offset)
|
dataReader = reader.getSubReader(pos + offset)
|
||||||
data = self.valueConverter.readArray(
|
data = valueConverter.readArray(
|
||||||
dataReader, font, tableDict=None,
|
dataReader, font, tableDict=None,
|
||||||
count=last - first + 1)
|
count=last - first + 1)
|
||||||
for k, v in enumerate(data):
|
for k, v in enumerate(data):
|
||||||
mapping[font.getGlyphName(first + k)] = v
|
mapping[font.getGlyphName(first + k)] = v
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
def readFormat6(self, reader, font):
|
def readFormat6(self, reader, font, valueConverter):
|
||||||
mapping = {}
|
mapping = {}
|
||||||
pos = reader.pos - 2 # start of table is at UShort for format
|
pos = reader.pos - 2 # start of table is at UShort for format
|
||||||
unitSize = reader.readUShort()
|
unitSize = reader.readUShort()
|
||||||
assert unitSize >= 2 + self.valueConverter.staticSize, unitSize
|
assert unitSize >= 2 + valueConverter.staticSize, unitSize
|
||||||
for i in range(reader.readUShort()):
|
for i in range(reader.readUShort()):
|
||||||
reader.seek(pos + i * unitSize + 12)
|
reader.seek(pos + i * unitSize + 12)
|
||||||
glyphID = reader.readUShort()
|
glyphID = reader.readUShort()
|
||||||
value = self.valueConverter.read(
|
value = valueConverter.read(
|
||||||
reader, font, tableDict=None)
|
reader, font, tableDict=None)
|
||||||
if glyphID != 0xFFFF:
|
if glyphID != 0xFFFF:
|
||||||
mapping[font.getGlyphName(glyphID)] = value
|
mapping[font.getGlyphName(glyphID)] = value
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
def readFormat8(self, reader, font):
|
def readFormat8(self, reader, font, valueConverter):
|
||||||
first = reader.readUShort()
|
first = reader.readUShort()
|
||||||
count = reader.readUShort()
|
count = reader.readUShort()
|
||||||
data = self.valueConverter.readArray(
|
data = valueConverter.readArray(
|
||||||
reader, font, tableDict=None, count=count)
|
reader, font, tableDict=None, count=count)
|
||||||
return {font.getGlyphName(first + k): value
|
return {font.getGlyphName(first + k): value
|
||||||
for (k, value) in enumerate(data)}
|
for (k, value) in enumerate(data)}
|
||||||
@ -935,10 +937,10 @@ converterMapping = {
|
|||||||
"VarIdxMapValue": VarIdxMapValue,
|
"VarIdxMapValue": VarIdxMapValue,
|
||||||
"VarDataValue": VarDataValue,
|
"VarDataValue": VarDataValue,
|
||||||
# AAT
|
# AAT
|
||||||
"AATLookup": AATLookup,
|
|
||||||
"MorphChain": StructWithLength,
|
"MorphChain": StructWithLength,
|
||||||
"MorphSubtable":StructWithLength,
|
"MorphSubtable":StructWithLength,
|
||||||
# "Template" types
|
# "Template" types
|
||||||
|
"AATLookup": lambda C: partial(AATLookup, tableClass=C),
|
||||||
"OffsetTo": lambda C: partial(Table, tableClass=C),
|
"OffsetTo": lambda C: partial(Table, tableClass=C),
|
||||||
"LOffsetTo": lambda C: partial(LTable, tableClass=C),
|
"LOffsetTo": lambda C: partial(LTable, tableClass=C),
|
||||||
}
|
}
|
||||||
|
@ -1284,7 +1284,7 @@ otData = [
|
|||||||
]),
|
]),
|
||||||
|
|
||||||
('NoncontextualMorph', [
|
('NoncontextualMorph', [
|
||||||
('AATLookup', 'mapping', None, None, 'The noncontextual glyph substitution table.'),
|
('AATLookup(GlyphID)', 'mapping', None, None, 'The noncontextual glyph substitution table.'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
('InsertionMorph', [
|
('InsertionMorph', [
|
||||||
|
@ -176,7 +176,8 @@ class UInt8Test(unittest.TestCase):
|
|||||||
|
|
||||||
class AATLookupTest(unittest.TestCase):
|
class AATLookupTest(unittest.TestCase):
|
||||||
font = FakeFont(".notdef A B C D E F G H A.alt B.alt".split())
|
font = FakeFont(".notdef A B C D E F G H A.alt B.alt".split())
|
||||||
converter = otConverters.AATLookup("AATLookup", 0, None, None)
|
converter = otConverters.AATLookup("AATLookup", 0, None,
|
||||||
|
tableClass=otConverters.GlyphID)
|
||||||
|
|
||||||
def __init__(self, methodName):
|
def __init__(self, methodName):
|
||||||
unittest.TestCase.__init__(self, methodName)
|
unittest.TestCase.__init__(self, methodName)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user