2016-01-13 17:52:30 +00:00
|
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
from fontTools import ttLib
|
|
|
|
from fontTools.ttLib.tables import otTables as ot
|
|
|
|
from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict
|
|
|
|
|
2016-01-14 17:10:45 +01:00
|
|
|
|
|
|
|
def buildCoverage(glyphs, glyphMap):
|
|
|
|
self = ot.Coverage()
|
|
|
|
self.glyphs = sorted(glyphs, key=glyphMap.__getitem__)
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
2016-01-13 17:52:30 +00:00
|
|
|
# GSUB
|
|
|
|
|
2016-01-14 11:46:25 +01:00
|
|
|
|
2016-01-13 17:52:30 +00:00
|
|
|
def buildSingleSubst(mapping):
|
2016-01-14 11:46:25 +01:00
|
|
|
self = ot.SingleSubst()
|
|
|
|
self.mapping = dict(mapping)
|
|
|
|
return self
|
|
|
|
|
2016-01-13 17:52:30 +00:00
|
|
|
|
|
|
|
def buildMultipleSubst(mapping):
|
2016-01-14 11:46:25 +01:00
|
|
|
self = ot.MultipleSubst()
|
|
|
|
self.mapping = dict(mapping)
|
|
|
|
return self
|
|
|
|
|
2016-01-13 17:52:30 +00:00
|
|
|
|
|
|
|
def buildAlternateSubst(mapping):
|
2016-01-14 11:46:25 +01:00
|
|
|
self = ot.AlternateSubst()
|
|
|
|
self.alternates = dict(mapping)
|
|
|
|
return self
|
|
|
|
|
2016-01-13 17:52:30 +00:00
|
|
|
|
2016-01-14 12:27:39 +00:00
|
|
|
def _getLigatureKey(components):
|
2016-01-14 11:46:25 +01:00
|
|
|
"""Computes a key for ordering ligatures in a GSUB Type-4 lookup.
|
2016-01-14 10:27:54 +01:00
|
|
|
|
2016-01-14 11:46:25 +01:00
|
|
|
When building the OpenType lookup, we need to make sure that
|
|
|
|
the longest sequence of components is listed first, so we
|
|
|
|
use the negative length as the primary key for sorting.
|
|
|
|
To make buildLigatureSubst() deterministic, we use the
|
|
|
|
component sequence as the secondary key.
|
|
|
|
|
|
|
|
For example, this will sort (f,f,f) < (f,f,i) < (f,f) < (f,i) < (f,l).
|
|
|
|
"""
|
|
|
|
return (-len(components), components)
|
2016-01-14 10:27:54 +01:00
|
|
|
|
2016-01-14 16:25:28 +01:00
|
|
|
|
2016-01-13 17:52:30 +00:00
|
|
|
def buildLigatureSubst(mapping):
|
2016-01-14 11:46:25 +01:00
|
|
|
self = ot.LigatureSubst()
|
|
|
|
# The following single line can replace the rest of this function
|
|
|
|
# with fontTools >= 3.1:
|
|
|
|
# self.ligatures = dict(mapping)
|
|
|
|
self.ligatures = {}
|
2016-01-14 12:27:39 +00:00
|
|
|
for components in sorted(mapping.keys(), key=_getLigatureKey):
|
2016-01-14 11:46:25 +01:00
|
|
|
ligature = ot.Ligature()
|
|
|
|
ligature.Component = components[1:]
|
|
|
|
ligature.CompCount = len(components)
|
|
|
|
ligature.LigGlyph = mapping[components]
|
|
|
|
firstGlyph = components[0]
|
|
|
|
self.ligatures.setdefault(firstGlyph, []).append(ligature)
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
2016-01-14 12:28:32 +00:00
|
|
|
# GPOS
|
|
|
|
|
|
|
|
|
2016-01-14 13:08:26 +01:00
|
|
|
def buildAnchor(x, y, point=None, deviceX=None, deviceY=None):
|
|
|
|
self = ot.Anchor()
|
|
|
|
self.XCoordinate, self.YCoordinate = x, y
|
|
|
|
self.Format = 1
|
|
|
|
if point is not None:
|
|
|
|
self.AnchorPoint = point
|
|
|
|
self.Format = 2
|
2016-01-14 14:59:10 +00:00
|
|
|
if deviceX is not None or deviceY is not None:
|
2016-01-14 15:06:59 +00:00
|
|
|
assert self.Format == 1, "Either point, or both of deviceX/deviceY, must be None."
|
2016-01-14 13:08:26 +01:00
|
|
|
self.XDeviceTable = deviceX
|
|
|
|
self.YDeviceTable = deviceY
|
|
|
|
self.Format = 3
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
2016-01-14 17:54:47 +01:00
|
|
|
def buildCursivePos(attach, glyphMap):
|
|
|
|
"""{"alef": (entry, exit)} --> otTables.CursivePos"""
|
|
|
|
self = ot.CursivePos()
|
|
|
|
self.Format = 1
|
|
|
|
self.Coverage = buildCoverage(attach.keys(), glyphMap)
|
|
|
|
self.EntryExitRecord = []
|
|
|
|
for glyph in self.Coverage.glyphs:
|
|
|
|
entryAnchor, exitAnchor = attach[glyph]
|
|
|
|
rec = ot.EntryExitRecord()
|
|
|
|
rec.EntryAnchor = entryAnchor
|
|
|
|
rec.ExitAnchor = exitAnchor
|
|
|
|
self.EntryExitRecord.append(rec)
|
2016-01-18 12:32:01 +01:00
|
|
|
self.EntryExitCount = len(self.EntryExitRecord)
|
2016-01-14 17:54:47 +01:00
|
|
|
return self
|
|
|
|
|
|
|
|
|
2016-01-14 11:46:25 +01:00
|
|
|
def buildDevice(device):
|
|
|
|
"""[(11, 22), (7, -7), ...] --> otTables.Device"""
|
|
|
|
self = ot.Device()
|
|
|
|
device = tuple(sorted(device))
|
|
|
|
self.StartSize = startSize = device[0][0]
|
|
|
|
self.EndSize = endSize = device[-1][0]
|
|
|
|
deviceDict = dict(device)
|
|
|
|
self.DeltaValue = deltaValues = [
|
|
|
|
deviceDict.get(size, 0)
|
|
|
|
for size in range(startSize, endSize + 1)]
|
|
|
|
maxDelta = max(deltaValues)
|
|
|
|
minDelta = min(deltaValues)
|
|
|
|
assert minDelta > -129 and maxDelta < 128
|
|
|
|
if minDelta > -3 and maxDelta < 2:
|
|
|
|
self.DeltaFormat = 1
|
|
|
|
elif minDelta > -9 and maxDelta < 8:
|
|
|
|
self.DeltaFormat = 2
|
|
|
|
else:
|
|
|
|
self.DeltaFormat = 3
|
|
|
|
return self
|
2016-01-14 16:25:28 +01:00
|
|
|
|
|
|
|
|
|
|
|
def buildValue(value):
|
|
|
|
self = ValueRecord()
|
|
|
|
for k, v in value.items():
|
|
|
|
setattr(self, k, v)
|
|
|
|
return self
|