Merge pull request #2788 from fonttools/varglyphset-spaces
[ttVarGlyphSet] Convert tabs to spaces
This commit is contained in:
commit
4abf2ee8c6
@ -6,273 +6,273 @@ from copy import copy
|
|||||||
|
|
||||||
class _TTGlyphSet(object):
|
class _TTGlyphSet(object):
|
||||||
|
|
||||||
"""Generic dict-like GlyphSet class that pulls metrics from hmtx and
|
"""Generic dict-like GlyphSet class that pulls metrics from hmtx and
|
||||||
glyph shape from TrueType or CFF.
|
glyph shape from TrueType or CFF.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ttFont, glyphs, glyphType):
|
def __init__(self, ttFont, glyphs, glyphType):
|
||||||
"""Construct a new glyphset.
|
"""Construct a new glyphset.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
font (TTFont): The font object (used to get metrics).
|
font (TTFont): The font object (used to get metrics).
|
||||||
glyphs (dict): A dictionary mapping glyph names to ``_TTGlyph`` objects.
|
glyphs (dict): A dictionary mapping glyph names to ``_TTGlyph`` objects.
|
||||||
glyphType (class): Either ``_TTGlyphCFF`` or ``_TTGlyphGlyf``.
|
glyphType (class): Either ``_TTGlyphCFF`` or ``_TTGlyphGlyf``.
|
||||||
"""
|
"""
|
||||||
self._glyphs = glyphs
|
self._glyphs = glyphs
|
||||||
self._hmtx = ttFont["hmtx"]
|
self._hmtx = ttFont["hmtx"]
|
||||||
self._vmtx = ttFont["vmtx"] if "vmtx" in ttFont else None
|
self._vmtx = ttFont["vmtx"] if "vmtx" in ttFont else None
|
||||||
self._glyphType = glyphType
|
self._glyphType = glyphType
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
return list(self._glyphs.keys())
|
return list(self._glyphs.keys())
|
||||||
|
|
||||||
def has_key(self, glyphName):
|
def has_key(self, glyphName):
|
||||||
return glyphName in self._glyphs
|
return glyphName in self._glyphs
|
||||||
|
|
||||||
__contains__ = has_key
|
__contains__ = has_key
|
||||||
|
|
||||||
def __getitem__(self, glyphName):
|
def __getitem__(self, glyphName):
|
||||||
horizontalMetrics = self._hmtx[glyphName]
|
horizontalMetrics = self._hmtx[glyphName]
|
||||||
verticalMetrics = self._vmtx[glyphName] if self._vmtx else None
|
verticalMetrics = self._vmtx[glyphName] if self._vmtx else None
|
||||||
return self._glyphType(
|
return self._glyphType(
|
||||||
self, self._glyphs[glyphName], horizontalMetrics, verticalMetrics
|
self, self._glyphs[glyphName], horizontalMetrics, verticalMetrics
|
||||||
)
|
)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._glyphs)
|
return len(self._glyphs)
|
||||||
|
|
||||||
def get(self, glyphName, default=None):
|
def get(self, glyphName, default=None):
|
||||||
try:
|
try:
|
||||||
return self[glyphName]
|
return self[glyphName]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
class _TTGlyph(object):
|
class _TTGlyph(object):
|
||||||
|
|
||||||
"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
|
"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
|
||||||
that it has .draw() and .drawPoints() methods that take a pen object as
|
that it has .draw() and .drawPoints() methods that take a pen object as
|
||||||
their only argument. Additionally there are 'width' and 'lsb' attributes,
|
their only argument. Additionally there are 'width' and 'lsb' attributes,
|
||||||
read from the 'hmtx' table.
|
read from the 'hmtx' table.
|
||||||
|
|
||||||
If the font contains a 'vmtx' table, there will also be 'height' and 'tsb'
|
If the font contains a 'vmtx' table, there will also be 'height' and 'tsb'
|
||||||
attributes.
|
attributes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, glyphset, glyph, horizontalMetrics=None, verticalMetrics=None):
|
def __init__(self, glyphset, glyph, horizontalMetrics=None, verticalMetrics=None):
|
||||||
"""Construct a new _TTGlyph.
|
"""Construct a new _TTGlyph.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
glyphset (_TTGlyphSet): A glyphset object used to resolve components.
|
glyphset (_TTGlyphSet): A glyphset object used to resolve components.
|
||||||
glyph (ttLib.tables._g_l_y_f.Glyph): The glyph object.
|
glyph (ttLib.tables._g_l_y_f.Glyph): The glyph object.
|
||||||
horizontalMetrics (int, int): The glyph's width and left sidebearing.
|
horizontalMetrics (int, int): The glyph's width and left sidebearing.
|
||||||
"""
|
"""
|
||||||
self._glyphset = glyphset
|
self._glyphset = glyphset
|
||||||
self._glyph = glyph
|
self._glyph = glyph
|
||||||
if horizontalMetrics:
|
if horizontalMetrics:
|
||||||
self.width, self.lsb = horizontalMetrics
|
self.width, self.lsb = horizontalMetrics
|
||||||
else:
|
else:
|
||||||
self.width, self.lsb = None, None
|
self.width, self.lsb = None, None
|
||||||
if verticalMetrics:
|
if verticalMetrics:
|
||||||
self.height, self.tsb = verticalMetrics
|
self.height, self.tsb = verticalMetrics
|
||||||
else:
|
else:
|
||||||
self.height, self.tsb = None, None
|
self.height, self.tsb = None, None
|
||||||
|
|
||||||
def draw(self, pen):
|
def draw(self, pen):
|
||||||
"""Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
|
"""Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
|
||||||
how that works.
|
how that works.
|
||||||
"""
|
"""
|
||||||
self._glyph.draw(pen)
|
self._glyph.draw(pen)
|
||||||
|
|
||||||
def drawPoints(self, pen):
|
def drawPoints(self, pen):
|
||||||
from fontTools.pens.pointPen import SegmentToPointPen
|
from fontTools.pens.pointPen import SegmentToPointPen
|
||||||
|
|
||||||
self.draw(SegmentToPointPen(pen))
|
self.draw(SegmentToPointPen(pen))
|
||||||
|
|
||||||
|
|
||||||
class _TTGlyphCFF(_TTGlyph):
|
class _TTGlyphCFF(_TTGlyph):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _TTGlyphGlyf(_TTGlyph):
|
class _TTGlyphGlyf(_TTGlyph):
|
||||||
def draw(self, pen):
|
def draw(self, pen):
|
||||||
"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
|
"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
|
||||||
how that works.
|
how that works.
|
||||||
"""
|
"""
|
||||||
glyfTable = self._glyphset._glyphs
|
glyfTable = self._glyphset._glyphs
|
||||||
glyph = self._glyph
|
glyph = self._glyph
|
||||||
offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
|
offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
|
||||||
glyph.draw(pen, glyfTable, offset)
|
glyph.draw(pen, glyfTable, offset)
|
||||||
|
|
||||||
def drawPoints(self, pen):
|
def drawPoints(self, pen):
|
||||||
"""Draw the glyph onto PointPen. See fontTools.pens.pointPen
|
"""Draw the glyph onto PointPen. See fontTools.pens.pointPen
|
||||||
for details how that works.
|
for details how that works.
|
||||||
"""
|
"""
|
||||||
glyfTable = self._glyphset._glyphs
|
glyfTable = self._glyphset._glyphs
|
||||||
glyph = self._glyph
|
glyph = self._glyph
|
||||||
offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
|
offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
|
||||||
glyph.drawPoints(pen, glyfTable, offset)
|
glyph.drawPoints(pen, glyfTable, offset)
|
||||||
|
|
||||||
|
|
||||||
class _TTVarGlyphSet(_TTGlyphSet):
|
class _TTVarGlyphSet(_TTGlyphSet):
|
||||||
def __init__(self, font, glyphs, glyphType, location, normalized):
|
def __init__(self, font, glyphs, glyphType, location, normalized):
|
||||||
self._ttFont = font
|
self._ttFont = font
|
||||||
self._glyphs = glyphs
|
self._glyphs = glyphs
|
||||||
self._glyphType = glyphType
|
self._glyphType = glyphType
|
||||||
|
|
||||||
if not normalized:
|
if not normalized:
|
||||||
from fontTools.varLib.models import normalizeLocation, piecewiseLinearMap
|
from fontTools.varLib.models import normalizeLocation, piecewiseLinearMap
|
||||||
|
|
||||||
axes = {
|
axes = {
|
||||||
a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
|
a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
|
||||||
for a in font["fvar"].axes
|
for a in font["fvar"].axes
|
||||||
}
|
}
|
||||||
location = normalizeLocation(location, axes)
|
location = normalizeLocation(location, axes)
|
||||||
if "avar" in font:
|
if "avar" in font:
|
||||||
avar = font["avar"]
|
avar = font["avar"]
|
||||||
avarSegments = avar.segments
|
avarSegments = avar.segments
|
||||||
new_location = {}
|
new_location = {}
|
||||||
for axis_tag, value in location.items():
|
for axis_tag, value in location.items():
|
||||||
avarMapping = avarSegments.get(axis_tag, None)
|
avarMapping = avarSegments.get(axis_tag, None)
|
||||||
if avarMapping is not None:
|
if avarMapping is not None:
|
||||||
value = piecewiseLinearMap(value, avarMapping)
|
value = piecewiseLinearMap(value, avarMapping)
|
||||||
new_location[axis_tag] = value
|
new_location[axis_tag] = value
|
||||||
location = new_location
|
location = new_location
|
||||||
del new_location
|
del new_location
|
||||||
|
|
||||||
self.location = location
|
self.location = location
|
||||||
|
|
||||||
def __getitem__(self, glyphName):
|
def __getitem__(self, glyphName):
|
||||||
if glyphName not in self._glyphs:
|
if glyphName not in self._glyphs:
|
||||||
raise KeyError(glyphName)
|
raise KeyError(glyphName)
|
||||||
return self._glyphType(self, glyphName, self.location)
|
return self._glyphType(self, glyphName, self.location)
|
||||||
|
|
||||||
|
|
||||||
def _setCoordinates(glyph, coord, glyfTable):
|
def _setCoordinates(glyph, coord, glyfTable):
|
||||||
# Handle phantom points for (left, right, top, bottom) positions.
|
# Handle phantom points for (left, right, top, bottom) positions.
|
||||||
assert len(coord) >= 4
|
assert len(coord) >= 4
|
||||||
if not hasattr(glyph, "xMin"):
|
if not hasattr(glyph, "xMin"):
|
||||||
glyph.recalcBounds(glyfTable)
|
glyph.recalcBounds(glyfTable)
|
||||||
leftSideX = coord[-4][0]
|
leftSideX = coord[-4][0]
|
||||||
rightSideX = coord[-3][0]
|
rightSideX = coord[-3][0]
|
||||||
topSideY = coord[-2][1]
|
topSideY = coord[-2][1]
|
||||||
bottomSideY = coord[-1][1]
|
bottomSideY = coord[-1][1]
|
||||||
|
|
||||||
for _ in range(4):
|
for _ in range(4):
|
||||||
del coord[-1]
|
del coord[-1]
|
||||||
|
|
||||||
if glyph.isComposite():
|
if glyph.isComposite():
|
||||||
assert len(coord) == len(glyph.components)
|
assert len(coord) == len(glyph.components)
|
||||||
glyph.components = [copy(comp) for comp in glyph.components]
|
glyph.components = [copy(comp) for comp in glyph.components]
|
||||||
for p, comp in zip(coord, glyph.components):
|
for p, comp in zip(coord, glyph.components):
|
||||||
if hasattr(comp, "x"):
|
if hasattr(comp, "x"):
|
||||||
comp.x, comp.y = p
|
comp.x, comp.y = p
|
||||||
elif glyph.numberOfContours == 0:
|
elif glyph.numberOfContours == 0:
|
||||||
assert len(coord) == 0
|
assert len(coord) == 0
|
||||||
else:
|
else:
|
||||||
assert len(coord) == len(glyph.coordinates)
|
assert len(coord) == len(glyph.coordinates)
|
||||||
glyph.coordinates = coord
|
glyph.coordinates = coord
|
||||||
|
|
||||||
glyph.recalcBounds(glyfTable)
|
glyph.recalcBounds(glyfTable)
|
||||||
|
|
||||||
horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
|
horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
|
||||||
verticalAdvanceWidth = otRound(topSideY - bottomSideY)
|
verticalAdvanceWidth = otRound(topSideY - bottomSideY)
|
||||||
leftSideBearing = otRound(glyph.xMin - leftSideX)
|
leftSideBearing = otRound(glyph.xMin - leftSideX)
|
||||||
topSideBearing = otRound(topSideY - glyph.yMax)
|
topSideBearing = otRound(topSideY - glyph.yMax)
|
||||||
return (
|
return (
|
||||||
horizontalAdvanceWidth,
|
horizontalAdvanceWidth,
|
||||||
leftSideBearing,
|
leftSideBearing,
|
||||||
verticalAdvanceWidth,
|
verticalAdvanceWidth,
|
||||||
topSideBearing,
|
topSideBearing,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class _TTVarGlyph(_TTGlyph):
|
class _TTVarGlyph(_TTGlyph):
|
||||||
def __init__(self, glyphSet, glyphName, location):
|
def __init__(self, glyphSet, glyphName, location):
|
||||||
|
|
||||||
super().__init__(glyphSet._glyphs, glyphSet._glyphs[glyphName])
|
super().__init__(glyphSet._glyphs, glyphSet._glyphs[glyphName])
|
||||||
|
|
||||||
self._glyphSet = glyphSet
|
self._glyphSet = glyphSet
|
||||||
self._ttFont = glyphSet._ttFont
|
self._ttFont = glyphSet._ttFont
|
||||||
self._glyphs = glyphSet._glyphs
|
self._glyphs = glyphSet._glyphs
|
||||||
self._glyphName = glyphName
|
self._glyphName = glyphName
|
||||||
self._location = location
|
self._location = location
|
||||||
|
|
||||||
|
|
||||||
class _TTVarGlyphCFF(_TTVarGlyph):
|
class _TTVarGlyphCFF(_TTVarGlyph):
|
||||||
def draw(self, pen):
|
def draw(self, pen):
|
||||||
varStore = self._glyphs.varStore
|
varStore = self._glyphs.varStore
|
||||||
if varStore is None:
|
if varStore is None:
|
||||||
blender = None
|
blender = None
|
||||||
else:
|
else:
|
||||||
from fontTools.varLib.varStore import VarStoreInstancer
|
from fontTools.varLib.varStore import VarStoreInstancer
|
||||||
|
|
||||||
vsInstancer = getattr(self._glyphSet, "vsInstancer", None)
|
vsInstancer = getattr(self._glyphSet, "vsInstancer", None)
|
||||||
if vsInstancer is None:
|
if vsInstancer is None:
|
||||||
self._glyphSet.vsInstancer = vsInstancer = VarStoreInstancer(
|
self._glyphSet.vsInstancer = vsInstancer = VarStoreInstancer(
|
||||||
varStore.otVarStore, self._ttFont["fvar"].axes, self._location
|
varStore.otVarStore, self._ttFont["fvar"].axes, self._location
|
||||||
)
|
)
|
||||||
blender = vsInstancer.interpolateFromDeltas
|
blender = vsInstancer.interpolateFromDeltas
|
||||||
self._glyph.draw(pen, blender)
|
self._glyph.draw(pen, blender)
|
||||||
|
|
||||||
self.width = self._ttFont["hmtx"][self._glyphName][0]
|
self.width = self._ttFont["hmtx"][self._glyphName][0]
|
||||||
if "HVAR" in self._ttFont:
|
if "HVAR" in self._ttFont:
|
||||||
hvar = self._ttFont["HVAR"].table
|
hvar = self._ttFont["HVAR"].table
|
||||||
varidx = self._ttFont.getGlyphID(self._glyphName)
|
varidx = self._ttFont.getGlyphID(self._glyphName)
|
||||||
if hvar.AdvWidthMap is not None:
|
if hvar.AdvWidthMap is not None:
|
||||||
varidx = hvar.AdvWidthMap.mapping[self._glyphName]
|
varidx = hvar.AdvWidthMap.mapping[self._glyphName]
|
||||||
vsInstancer = VarStoreInstancer(
|
vsInstancer = VarStoreInstancer(
|
||||||
hvar.VarStore, self._ttFont["fvar"].axes, self._location
|
hvar.VarStore, self._ttFont["fvar"].axes, self._location
|
||||||
)
|
)
|
||||||
delta = vsInstancer[varidx]
|
delta = vsInstancer[varidx]
|
||||||
self.width += delta
|
self.width += delta
|
||||||
|
|
||||||
|
|
||||||
class _TTVarGlyphGlyf(_TTVarGlyph):
|
class _TTVarGlyphGlyf(_TTVarGlyph):
|
||||||
def draw(self, pen):
|
def draw(self, pen):
|
||||||
self._drawWithPen(pen, isPointPen=False)
|
self._drawWithPen(pen, isPointPen=False)
|
||||||
|
|
||||||
def drawPoints(self, pen):
|
def drawPoints(self, pen):
|
||||||
self._drawWithPen(pen, isPointPen=True)
|
self._drawWithPen(pen, isPointPen=True)
|
||||||
|
|
||||||
def _drawWithPen(self, pen, isPointPen):
|
def _drawWithPen(self, pen, isPointPen):
|
||||||
from fontTools.varLib.iup import iup_delta
|
from fontTools.varLib.iup import iup_delta
|
||||||
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
||||||
from fontTools.varLib.models import supportScalar
|
from fontTools.varLib.models import supportScalar
|
||||||
|
|
||||||
glyf = self._ttFont["glyf"]
|
glyf = self._ttFont["glyf"]
|
||||||
hMetrics = self._ttFont["hmtx"].metrics
|
hMetrics = self._ttFont["hmtx"].metrics
|
||||||
vMetrics = getattr(self._ttFont.get("vmtx"), "metrics", None)
|
vMetrics = getattr(self._ttFont.get("vmtx"), "metrics", None)
|
||||||
|
|
||||||
variations = self._ttFont["gvar"].variations[self._glyphName]
|
variations = self._ttFont["gvar"].variations[self._glyphName]
|
||||||
coordinates, _ = glyf._getCoordinatesAndControls(
|
coordinates, _ = glyf._getCoordinatesAndControls(
|
||||||
self._glyphName, hMetrics, vMetrics
|
self._glyphName, hMetrics, vMetrics
|
||||||
)
|
)
|
||||||
origCoords, endPts = None, None
|
origCoords, endPts = None, None
|
||||||
for var in variations:
|
for var in variations:
|
||||||
scalar = supportScalar(self._location, var.axes)
|
scalar = supportScalar(self._location, var.axes)
|
||||||
if not scalar:
|
if not scalar:
|
||||||
continue
|
continue
|
||||||
delta = var.coordinates
|
delta = var.coordinates
|
||||||
if None in delta:
|
if None in delta:
|
||||||
if origCoords is None:
|
if origCoords is None:
|
||||||
origCoords, control = glyf._getCoordinatesAndControls(
|
origCoords, control = glyf._getCoordinatesAndControls(
|
||||||
self._glyphName, hMetrics, vMetrics
|
self._glyphName, hMetrics, vMetrics
|
||||||
)
|
)
|
||||||
endPts = (
|
endPts = (
|
||||||
control[1] if control[0] >= 1 else list(range(len(control[1])))
|
control[1] if control[0] >= 1 else list(range(len(control[1])))
|
||||||
)
|
)
|
||||||
delta = iup_delta(delta, origCoords, endPts)
|
delta = iup_delta(delta, origCoords, endPts)
|
||||||
coordinates += GlyphCoordinates(delta) * scalar
|
coordinates += GlyphCoordinates(delta) * scalar
|
||||||
|
|
||||||
glyph = copy(glyf[self._glyphName]) # Shallow copy
|
glyph = copy(glyf[self._glyphName]) # Shallow copy
|
||||||
width, lsb, height, tsb = _setCoordinates(glyph, coordinates, glyf)
|
width, lsb, height, tsb = _setCoordinates(glyph, coordinates, glyf)
|
||||||
self.width = width
|
self.width = width
|
||||||
self.lsb = lsb
|
self.lsb = lsb
|
||||||
self.height = height
|
self.height = height
|
||||||
self.tsb = tsb
|
self.tsb = tsb
|
||||||
offset = lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
|
offset = lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
|
||||||
if isPointPen:
|
if isPointPen:
|
||||||
glyph.drawPoints(pen, glyf, offset)
|
glyph.drawPoints(pen, glyf, offset)
|
||||||
else:
|
else:
|
||||||
glyph.draw(pen, glyf, offset)
|
glyph.draw(pen, glyf, offset)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user