Merge pull request #2784 from fonttools/varglyphset-cff
[ttVarGlyphSet] Support CFF
This commit is contained in:
commit
633bc2732f
@ -1125,8 +1125,9 @@ class CharStrings(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray,
|
def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray,
|
||||||
isCFF2=None):
|
isCFF2=None, varStore=None):
|
||||||
self.globalSubrs = globalSubrs
|
self.globalSubrs = globalSubrs
|
||||||
|
self.varStore = varStore
|
||||||
if file is not None:
|
if file is not None:
|
||||||
self.charStringsIndex = SubrsIndex(
|
self.charStringsIndex = SubrsIndex(
|
||||||
file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2)
|
file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2)
|
||||||
@ -1516,6 +1517,7 @@ class CharStringsConverter(TableConverter):
|
|||||||
file = parent.file
|
file = parent.file
|
||||||
isCFF2 = parent._isCFF2
|
isCFF2 = parent._isCFF2
|
||||||
charset = parent.charset
|
charset = parent.charset
|
||||||
|
varStore = getattr(parent, "VarStore", None)
|
||||||
globalSubrs = parent.GlobalSubrs
|
globalSubrs = parent.GlobalSubrs
|
||||||
if hasattr(parent, "FDArray"):
|
if hasattr(parent, "FDArray"):
|
||||||
fdArray = parent.FDArray
|
fdArray = parent.FDArray
|
||||||
@ -1529,7 +1531,7 @@ class CharStringsConverter(TableConverter):
|
|||||||
private = parent.Private
|
private = parent.Private
|
||||||
file.seek(value) # Offset(0)
|
file.seek(value) # Offset(0)
|
||||||
charStrings = CharStrings(
|
charStrings = CharStrings(
|
||||||
file, charset, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2)
|
file, charset, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2, varStore=varStore)
|
||||||
return charStrings
|
return charStrings
|
||||||
|
|
||||||
def write(self, parent, value):
|
def write(self, parent, value):
|
||||||
@ -1551,7 +1553,7 @@ class CharStringsConverter(TableConverter):
|
|||||||
# there is no fdArray.
|
# there is no fdArray.
|
||||||
private, fdSelect, fdArray = parent.Private, None, None
|
private, fdSelect, fdArray = parent.Private, None, None
|
||||||
charStrings = CharStrings(
|
charStrings = CharStrings(
|
||||||
None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
|
None, None, parent.GlobalSubrs, private, fdSelect, fdArray, varStore=getattr(parent, "VarStore", None))
|
||||||
charStrings.fromXML(name, attrs, content)
|
charStrings.fromXML(name, attrs, content)
|
||||||
return charStrings
|
return charStrings
|
||||||
|
|
||||||
|
@ -263,12 +263,13 @@ class CharStringCompileError(Exception): pass
|
|||||||
|
|
||||||
class SimpleT2Decompiler(object):
|
class SimpleT2Decompiler(object):
|
||||||
|
|
||||||
def __init__(self, localSubrs, globalSubrs, private=None):
|
def __init__(self, localSubrs, globalSubrs, private=None, blender=None):
|
||||||
self.localSubrs = localSubrs
|
self.localSubrs = localSubrs
|
||||||
self.localBias = calcSubrBias(localSubrs)
|
self.localBias = calcSubrBias(localSubrs)
|
||||||
self.globalSubrs = globalSubrs
|
self.globalSubrs = globalSubrs
|
||||||
self.globalBias = calcSubrBias(globalSubrs)
|
self.globalBias = calcSubrBias(globalSubrs)
|
||||||
self.private = private
|
self.private = private
|
||||||
|
self.blender = blender
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
@ -277,6 +278,7 @@ class SimpleT2Decompiler(object):
|
|||||||
self.hintCount = 0
|
self.hintCount = 0
|
||||||
self.hintMaskBytes = 0
|
self.hintMaskBytes = 0
|
||||||
self.numRegions = 0
|
self.numRegions = 0
|
||||||
|
self.vsIndex = 0
|
||||||
|
|
||||||
def execute(self, charString):
|
def execute(self, charString):
|
||||||
self.callingStack.append(charString)
|
self.callingStack.append(charString)
|
||||||
@ -410,17 +412,28 @@ class SimpleT2Decompiler(object):
|
|||||||
def op_roll(self, index):
|
def op_roll(self, index):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
# TODO(behdad): move to T2OutlineExtractor and add a 'setVariation'
|
|
||||||
# method that takes VarStoreData and a location
|
|
||||||
def op_blend(self, index):
|
def op_blend(self, index):
|
||||||
if self.numRegions == 0:
|
if self.numRegions == 0:
|
||||||
self.numRegions = self.private.getNumRegions()
|
self.numRegions = self.private.getNumRegions()
|
||||||
numBlends = self.pop()
|
numBlends = self.pop()
|
||||||
numOps = numBlends * (self.numRegions + 1)
|
numOps = numBlends * (self.numRegions + 1)
|
||||||
|
if self.blender is None:
|
||||||
del self.operandStack[-(numOps-numBlends):] # Leave the default operands on the stack.
|
del self.operandStack[-(numOps-numBlends):] # Leave the default operands on the stack.
|
||||||
|
else:
|
||||||
|
argi = len(self.operandStack) - numOps
|
||||||
|
end_args = tuplei = argi + numBlends
|
||||||
|
while argi < end_args:
|
||||||
|
next_ti = tuplei + self.numRegions
|
||||||
|
deltas = self.operandStack[tuplei:next_ti]
|
||||||
|
delta = self.blender(self.vsIndex, deltas)
|
||||||
|
self.operandStack[argi] += delta
|
||||||
|
tuplei = next_ti
|
||||||
|
argi += 1
|
||||||
|
self.operandStack[end_args:] = []
|
||||||
|
|
||||||
def op_vsindex(self, index):
|
def op_vsindex(self, index):
|
||||||
vi = self.pop()
|
vi = self.pop()
|
||||||
|
self.vsIndex = vi
|
||||||
self.numRegions = self.private.getNumRegions(vi)
|
self.numRegions = self.private.getNumRegions(vi)
|
||||||
|
|
||||||
|
|
||||||
@ -456,8 +469,8 @@ t1Operators = [
|
|||||||
|
|
||||||
class T2WidthExtractor(SimpleT2Decompiler):
|
class T2WidthExtractor(SimpleT2Decompiler):
|
||||||
|
|
||||||
def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None):
|
def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None, blender=None):
|
||||||
SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private)
|
SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private, blender)
|
||||||
self.nominalWidthX = nominalWidthX
|
self.nominalWidthX = nominalWidthX
|
||||||
self.defaultWidthX = defaultWidthX
|
self.defaultWidthX = defaultWidthX
|
||||||
|
|
||||||
@ -498,9 +511,9 @@ class T2WidthExtractor(SimpleT2Decompiler):
|
|||||||
|
|
||||||
class T2OutlineExtractor(T2WidthExtractor):
|
class T2OutlineExtractor(T2WidthExtractor):
|
||||||
|
|
||||||
def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None):
|
def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None, blender=None):
|
||||||
T2WidthExtractor.__init__(
|
T2WidthExtractor.__init__(
|
||||||
self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private)
|
self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private, blender)
|
||||||
self.pen = pen
|
self.pen = pen
|
||||||
self.subrLevel = 0
|
self.subrLevel = 0
|
||||||
|
|
||||||
@ -986,11 +999,11 @@ class T2CharString(object):
|
|||||||
decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private)
|
decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private)
|
||||||
decompiler.execute(self)
|
decompiler.execute(self)
|
||||||
|
|
||||||
def draw(self, pen):
|
def draw(self, pen, blender=None):
|
||||||
subrs = getattr(self.private, "Subrs", [])
|
subrs = getattr(self.private, "Subrs", [])
|
||||||
extractor = self.outlineExtractor(pen, subrs, self.globalSubrs,
|
extractor = self.outlineExtractor(pen, subrs, self.globalSubrs,
|
||||||
self.private.nominalWidthX, self.private.defaultWidthX,
|
self.private.nominalWidthX, self.private.defaultWidthX,
|
||||||
self.private)
|
self.private, blender)
|
||||||
extractor.execute(self)
|
extractor.execute(self)
|
||||||
self.width = extractor.width
|
self.width = extractor.width
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ from fontTools.ttLib.ttGlyphSet import (
|
|||||||
_TTGlyphSet, _TTGlyph,
|
_TTGlyphSet, _TTGlyph,
|
||||||
_TTGlyphCFF, _TTGlyphGlyf,
|
_TTGlyphCFF, _TTGlyphGlyf,
|
||||||
_TTVarGlyphSet,
|
_TTVarGlyphSet,
|
||||||
|
_TTVarGlyphCFF, _TTVarGlyphGlyf,
|
||||||
)
|
)
|
||||||
from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
|
from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
|
||||||
from io import BytesIO, StringIO
|
from io import BytesIO, StringIO
|
||||||
@ -703,14 +704,17 @@ class TTFont(object):
|
|||||||
if (preferCFF and any(tb in self for tb in ["CFF ", "CFF2"]) or
|
if (preferCFF and any(tb in self for tb in ["CFF ", "CFF2"]) or
|
||||||
("glyf" not in self and any(tb in self for tb in ["CFF ", "CFF2"]))):
|
("glyf" not in self and any(tb in self for tb in ["CFF ", "CFF2"]))):
|
||||||
table_tag = "CFF2" if "CFF2" in self else "CFF "
|
table_tag = "CFF2" if "CFF2" in self else "CFF "
|
||||||
if location:
|
glyphs = list(self[table_tag].cff.values())[0].CharStrings
|
||||||
raise NotImplementedError # TODO
|
if location and 'fvar' in self:
|
||||||
glyphs = _TTGlyphSet(self,
|
glyphs = _TTVarGlyphSet(self, glyphs, _TTVarGlyphCFF,
|
||||||
list(self[table_tag].cff.values())[0].CharStrings, _TTGlyphCFF)
|
location, normalized)
|
||||||
|
else:
|
||||||
|
glyphs = _TTGlyphSet(self, glyphs, _TTGlyphCFF)
|
||||||
|
|
||||||
if glyphs is None and "glyf" in self:
|
if glyphs is None and "glyf" in self:
|
||||||
if location and 'gvar' in self:
|
if location and 'gvar' in self:
|
||||||
glyphs = _TTVarGlyphSet(self, location=location, normalized=normalized)
|
glyphs = _TTVarGlyphSet(self, self["glyf"], _TTVarGlyphGlyf,
|
||||||
|
location, normalized)
|
||||||
else:
|
else:
|
||||||
glyphs = _TTGlyphSet(self, self["glyf"], _TTGlyphGlyf)
|
glyphs = _TTGlyphSet(self, self["glyf"], _TTGlyphGlyf)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from fontTools.misc.fixedTools import otRound
|
from fontTools.misc.fixedTools import otRound
|
||||||
from copy import copy
|
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
|
||||||
@ -18,8 +19,8 @@ class _TTGlyphSet(object):
|
|||||||
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):
|
||||||
@ -34,7 +35,8 @@ class _TTGlyphSet(object):
|
|||||||
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)
|
||||||
@ -45,6 +47,7 @@ class _TTGlyphSet(object):
|
|||||||
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
|
||||||
@ -56,7 +59,7 @@ class _TTGlyph(object):
|
|||||||
attributes.
|
attributes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, glyphset, glyph, horizontalMetrics, verticalMetrics=None):
|
def __init__(self, glyphset, glyph, horizontalMetrics=None, verticalMetrics=None):
|
||||||
"""Construct a new _TTGlyph.
|
"""Construct a new _TTGlyph.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -66,7 +69,10 @@ class _TTGlyph(object):
|
|||||||
"""
|
"""
|
||||||
self._glyphset = glyphset
|
self._glyphset = glyphset
|
||||||
self._glyph = glyph
|
self._glyph = glyph
|
||||||
|
if horizontalMetrics:
|
||||||
self.width, self.lsb = horizontalMetrics
|
self.width, self.lsb = horizontalMetrics
|
||||||
|
else:
|
||||||
|
self.width, self.lsb = None, None
|
||||||
if verticalMetrics:
|
if verticalMetrics:
|
||||||
self.height, self.tsb = verticalMetrics
|
self.height, self.tsb = verticalMetrics
|
||||||
else:
|
else:
|
||||||
@ -80,13 +86,15 @@ class _TTGlyph(object):
|
|||||||
|
|
||||||
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.
|
||||||
@ -106,20 +114,22 @@ class _TTGlyphGlyf(_TTGlyph):
|
|||||||
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, location, normalized=False):
|
|
||||||
self._ttFont = font
|
self._ttFont = font
|
||||||
self._glyphs = font['glyf']
|
self._glyphs = glyphs
|
||||||
|
self._glyphType = glyphType
|
||||||
|
|
||||||
if not normalized:
|
if not normalized:
|
||||||
from fontTools.varLib.models import normalizeLocation, piecewiseLinearMap
|
from fontTools.varLib.models import normalizeLocation, piecewiseLinearMap
|
||||||
|
|
||||||
axes = {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in font['fvar'].axes}
|
axes = {
|
||||||
|
a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
|
||||||
|
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():
|
||||||
@ -135,13 +145,13 @@ class _TTVarGlyphSet(_TTGlyphSet):
|
|||||||
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 _TTVarGlyphGlyf(self._ttFont, 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]
|
||||||
@ -155,7 +165,7 @@ def _setCoordinates(glyph, coord, glyfTable):
|
|||||||
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
|
||||||
@ -178,16 +188,47 @@ def _setCoordinates(glyph, coord, glyfTable):
|
|||||||
|
|
||||||
|
|
||||||
class _TTVarGlyph(_TTGlyph):
|
class _TTVarGlyph(_TTGlyph):
|
||||||
def __init__(self, ttFont, glyphName, location):
|
def __init__(self, glyphSet, glyphName, location):
|
||||||
self._ttFont = ttFont
|
|
||||||
|
super().__init__(glyphSet._glyphs, glyphSet._glyphs[glyphName])
|
||||||
|
|
||||||
|
self._glyphSet = glyphSet
|
||||||
|
self._ttFont = glyphSet._ttFont
|
||||||
|
self._glyphs = glyphSet._glyphs
|
||||||
self._glyphName = glyphName
|
self._glyphName = glyphName
|
||||||
self._location = location
|
self._location = location
|
||||||
# draw() fills these in
|
|
||||||
self.width = self.height = self.lsb = self.tsb = None
|
|
||||||
|
class _TTVarGlyphCFF(_TTVarGlyph):
|
||||||
|
def draw(self, pen):
|
||||||
|
varStore = self._glyphs.varStore
|
||||||
|
if varStore is None:
|
||||||
|
blender = None
|
||||||
|
else:
|
||||||
|
from fontTools.varLib.varStore import VarStoreInstancer
|
||||||
|
|
||||||
|
vsInstancer = getattr(self._glyphSet, "vsInstancer", None)
|
||||||
|
if vsInstancer is None:
|
||||||
|
self._glyphSet.vsInstancer = vsInstancer = VarStoreInstancer(
|
||||||
|
varStore.otVarStore, self._ttFont["fvar"].axes, self._location
|
||||||
|
)
|
||||||
|
blender = vsInstancer.interpolateFromDeltas
|
||||||
|
self._glyph.draw(pen, blender)
|
||||||
|
|
||||||
|
self.width = self._ttFont["hmtx"][self._glyphName][0]
|
||||||
|
if "HVAR" in self._ttFont:
|
||||||
|
hvar = self._ttFont["HVAR"].table
|
||||||
|
varidx = self._ttFont.getGlyphID(self._glyphName)
|
||||||
|
if hvar.AdvWidthMap is not None:
|
||||||
|
varidx = hvar.AdvWidthMap.mapping[self._glyphName]
|
||||||
|
vsInstancer = VarStoreInstancer(
|
||||||
|
hvar.VarStore, self._ttFont["fvar"].axes, self._location
|
||||||
|
)
|
||||||
|
delta = vsInstancer[varidx]
|
||||||
|
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)
|
||||||
|
|
||||||
@ -199,12 +240,14 @@ class _TTVarGlyphGlyf(_TTVarGlyph):
|
|||||||
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(self._glyphName, hMetrics, vMetrics)
|
coordinates, _ = glyf._getCoordinatesAndControls(
|
||||||
|
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)
|
||||||
@ -213,8 +256,12 @@ class _TTVarGlyphGlyf(_TTVarGlyph):
|
|||||||
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(self._glyphName, hMetrics, vMetrics)
|
origCoords, control = glyf._getCoordinatesAndControls(
|
||||||
endPts = control[1] if control[0] >= 1 else list(range(len(control[1])))
|
self._glyphName, hMetrics, vMetrics
|
||||||
|
)
|
||||||
|
endPts = (
|
||||||
|
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
|
||||||
|
|
||||||
|
@ -448,9 +448,9 @@ class MergeOutlineExtractor(CFFToCFF2OutlineExtractor):
|
|||||||
into a CFF2 variable font charstring."""
|
into a CFF2 variable font charstring."""
|
||||||
|
|
||||||
def __init__(self, pen, localSubrs, globalSubrs,
|
def __init__(self, pen, localSubrs, globalSubrs,
|
||||||
nominalWidthX, defaultWidthX, private=None):
|
nominalWidthX, defaultWidthX, private=None, blender=None):
|
||||||
super().__init__(pen, localSubrs,
|
super().__init__(pen, localSubrs,
|
||||||
globalSubrs, nominalWidthX, defaultWidthX, private)
|
globalSubrs, nominalWidthX, defaultWidthX, private, blender)
|
||||||
|
|
||||||
def countHints(self):
|
def countHints(self):
|
||||||
args = self.popallWidth()
|
args = self.popallWidth()
|
||||||
|
BIN
Tests/ttLib/data/I.otf
Normal file
BIN
Tests/ttLib/data/I.otf
Normal file
Binary file not shown.
@ -6,82 +6,116 @@ import pytest
|
|||||||
|
|
||||||
|
|
||||||
class TTGlyphSetTest(object):
|
class TTGlyphSetTest(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getpath(testfile):
|
def getpath(testfile):
|
||||||
path = os.path.dirname(__file__)
|
path = os.path.dirname(__file__)
|
||||||
return os.path.join(path, "data", testfile)
|
return os.path.join(path, "data", testfile)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"location, expected",
|
"fontfile, location, expected",
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
|
"I.ttf",
|
||||||
None,
|
None,
|
||||||
[
|
[
|
||||||
('moveTo', ((175, 0),)),
|
("moveTo", ((175, 0),)),
|
||||||
('lineTo', ((367, 0),)),
|
("lineTo", ((367, 0),)),
|
||||||
('lineTo', ((367, 1456),)),
|
("lineTo", ((367, 1456),)),
|
||||||
('lineTo', ((175, 1456),)),
|
("lineTo", ((175, 1456),)),
|
||||||
('closePath', ())
|
("closePath", ()),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
"I.ttf",
|
||||||
{},
|
{},
|
||||||
[
|
[
|
||||||
('moveTo', ((175, 0),)),
|
("moveTo", ((175, 0),)),
|
||||||
('lineTo', ((367, 0),)),
|
("lineTo", ((367, 0),)),
|
||||||
('lineTo', ((367, 1456),)),
|
("lineTo", ((367, 1456),)),
|
||||||
('lineTo', ((175, 1456),)),
|
("lineTo", ((175, 1456),)),
|
||||||
('closePath', ())
|
("closePath", ()),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{'wght': 100},
|
"I.ttf",
|
||||||
|
{"wght": 100},
|
||||||
[
|
[
|
||||||
('moveTo', ((175, 0),)),
|
("moveTo", ((175, 0),)),
|
||||||
('lineTo', ((271, 0),)),
|
("lineTo", ((271, 0),)),
|
||||||
('lineTo', ((271, 1456),)),
|
("lineTo", ((271, 1456),)),
|
||||||
('lineTo', ((175, 1456),)),
|
("lineTo", ((175, 1456),)),
|
||||||
('closePath', ())
|
("closePath", ()),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{'wght': 1000},
|
"I.ttf",
|
||||||
|
{"wght": 1000},
|
||||||
[
|
[
|
||||||
('moveTo', ((128, 0),)),
|
("moveTo", ((128, 0),)),
|
||||||
('lineTo', ((550, 0),)),
|
("lineTo", ((550, 0),)),
|
||||||
('lineTo', ((550, 1456),)),
|
("lineTo", ((550, 1456),)),
|
||||||
('lineTo', ((128, 1456),)),
|
("lineTo", ((128, 1456),)),
|
||||||
('closePath', ())
|
("closePath", ()),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{'wght': 1000, 'wdth': 25},
|
"I.ttf",
|
||||||
|
{"wght": 1000, "wdth": 25},
|
||||||
[
|
[
|
||||||
('moveTo', ((140, 0),)),
|
("moveTo", ((140, 0),)),
|
||||||
('lineTo', ((553, 0),)),
|
("lineTo", ((553, 0),)),
|
||||||
('lineTo', ((553, 1456),)),
|
("lineTo", ((553, 1456),)),
|
||||||
('lineTo', ((140, 1456),)),
|
("lineTo", ((140, 1456),)),
|
||||||
('closePath', ())
|
("closePath", ()),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{'wght': 1000, 'wdth': 50},
|
"I.ttf",
|
||||||
|
{"wght": 1000, "wdth": 50},
|
||||||
[
|
[
|
||||||
('moveTo', ((136, 0),)),
|
("moveTo", ((136, 0),)),
|
||||||
('lineTo', ((552, 0),)),
|
("lineTo", ((552, 0),)),
|
||||||
('lineTo', ((552, 1456),)),
|
("lineTo", ((552, 1456),)),
|
||||||
('lineTo', ((136, 1456),)),
|
("lineTo", ((136, 1456),)),
|
||||||
('closePath', ())
|
("closePath", ()),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
]
|
(
|
||||||
|
"I.otf",
|
||||||
|
{"wght": 1000},
|
||||||
|
[
|
||||||
|
("moveTo", ((179, 74),)),
|
||||||
|
("lineTo", ((28, 59),)),
|
||||||
|
("lineTo", ((28, 0),)),
|
||||||
|
("lineTo", ((367, 0),)),
|
||||||
|
("lineTo", ((367, 59),)),
|
||||||
|
("lineTo", ((212, 74),)),
|
||||||
|
("lineTo", ((179, 74),)),
|
||||||
|
("closePath", ()),
|
||||||
|
("moveTo", ((179, 578),)),
|
||||||
|
("lineTo", ((212, 578),)),
|
||||||
|
("lineTo", ((367, 593),)),
|
||||||
|
("lineTo", ((367, 652),)),
|
||||||
|
("lineTo", ((28, 652),)),
|
||||||
|
("lineTo", ((28, 593),)),
|
||||||
|
("lineTo", ((179, 578),)),
|
||||||
|
("closePath", ()),
|
||||||
|
("moveTo", ((98, 310),)),
|
||||||
|
("curveTo", ((98, 205), (98, 101), (95, 0))),
|
||||||
|
("lineTo", ((299, 0),)),
|
||||||
|
("curveTo", ((296, 103), (296, 207), (296, 311))),
|
||||||
|
("lineTo", ((296, 342),)),
|
||||||
|
("curveTo", ((296, 447), (296, 551), (299, 652))),
|
||||||
|
("lineTo", ((95, 652),)),
|
||||||
|
("curveTo", ((98, 549), (98, 445), (98, 342))),
|
||||||
|
("lineTo", ((98, 310),)),
|
||||||
|
("closePath", ()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
def test_glyphset(
|
def test_glyphset(self, fontfile, location, expected):
|
||||||
self, location, expected
|
font = TTFont(self.getpath(fontfile))
|
||||||
):
|
|
||||||
# TODO: also test loading CFF-flavored fonts
|
|
||||||
font = TTFont(self.getpath("I.ttf"))
|
|
||||||
glyphset = font.getGlyphSet(location=location)
|
glyphset = font.getGlyphSet(location=location)
|
||||||
|
|
||||||
assert isinstance(glyphset, ttGlyphSet._TTGlyphSet)
|
assert isinstance(glyphset, ttGlyphSet._TTGlyphSet)
|
||||||
@ -96,17 +130,22 @@ class TTGlyphSetTest(object):
|
|||||||
assert len(glyphset) == 2
|
assert len(glyphset) == 2
|
||||||
|
|
||||||
pen = RecordingPen()
|
pen = RecordingPen()
|
||||||
glyph = glyphset['I']
|
glyph = glyphset["I"]
|
||||||
|
|
||||||
assert glyphset.get("foobar") is None
|
assert glyphset.get("foobar") is None
|
||||||
|
|
||||||
assert isinstance(glyph, ttGlyphSet._TTGlyph)
|
assert isinstance(glyph, ttGlyphSet._TTGlyph)
|
||||||
|
is_glyf = fontfile.endswith(".ttf")
|
||||||
if location:
|
if location:
|
||||||
assert isinstance(glyph, ttGlyphSet._TTVarGlyphGlyf)
|
glyphType = (
|
||||||
|
ttGlyphSet._TTVarGlyphGlyf if is_glyf else ttGlyphSet._TTVarGlyphCFF
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
assert isinstance(glyph, ttGlyphSet._TTGlyphGlyf)
|
glyphType = ttGlyphSet._TTGlyphGlyf if is_glyf else ttGlyphSet._TTGlyphCFF
|
||||||
|
assert isinstance(glyph, glyphType)
|
||||||
|
|
||||||
glyph.draw(pen)
|
glyph.draw(pen)
|
||||||
actual = pen.value
|
actual = pen.value
|
||||||
|
|
||||||
|
print(actual)
|
||||||
assert actual == expected, (location, actual, expected)
|
assert actual == expected, (location, actual, expected)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user