125 lines
3.6 KiB
Python
Raw Normal View History

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
def buildCoverage(glyphs, glyphMap):
self = ot.Coverage()
self.glyphs = sorted(glyphs, key=glyphMap.__getitem__)
return self
# GSUB
def buildSingleSubst(mapping):
self = ot.SingleSubst()
self.mapping = dict(mapping)
return self
def buildMultipleSubst(mapping):
self = ot.MultipleSubst()
self.mapping = dict(mapping)
return self
def buildAlternateSubst(mapping):
self = ot.AlternateSubst()
self.alternates = dict(mapping)
return self
2016-01-14 12:27:39 +00:00
def _getLigatureKey(components):
"""Computes a key for ordering ligatures in a GSUB Type-4 lookup.
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)
def buildLigatureSubst(mapping):
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):
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
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
if deviceX is not None or deviceY is not None:
assert self.Format == 1, "Either point, or both of deviceX/deviceY, must be None."
self.XDeviceTable = deviceX
self.YDeviceTable = deviceY
self.Format = 3
return self
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)
return self
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
def buildValue(value):
self = ValueRecord()
for k, v in value.items():
setattr(self, k, v)
return self