Merge pull request #2784 from fonttools/varglyphset-cff

[ttVarGlyphSet] Support CFF
This commit is contained in:
Just van Rossum 2022-08-29 19:26:14 +02:00 committed by GitHub
commit 633bc2732f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 213 additions and 108 deletions

View File

@ -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

View File

@ -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)
del self.operandStack[-(numOps-numBlends):] # Leave the default operands on the stack. if self.blender is None:
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

View File

@ -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)

View File

@ -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
@ -13,13 +14,13 @@ class _TTGlyphSet(object):
"""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):
@ -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,17 +59,20 @@ 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:
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
self.width, self.lsb = horizontalMetrics if 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]
@ -154,9 +164,9 @@ def _setCoordinates(glyph, coord, glyfTable):
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:
@ -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,12 +256,16 @@ 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
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

View File

@ -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

Binary file not shown.

View File

@ -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)