-
-
Save typesupply/687cbaf731b6d1f3b997bbc8846ce65a to your computer and use it in GitHub Desktop.
Test for ufoLib validation and speed improvements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This compares the speed of experimental changes in ufoLib | |
with the experimental ufoLib in fontTools. This requires the | |
"Roboto-Regular.ufo" font to be located next to this script. | |
""" | |
import os | |
import shutil | |
import timeit | |
import cProfile | |
from fontTools.pens.basePen import NullPen | |
from fontTools.ufoLib.objects import Font as FontToolsFont | |
import ufoLib | |
from defcon import Font as DefconFont | |
# ----------- | |
# Source Font | |
# ----------- | |
sourcePath = os.getcwd() | |
sourcePath = os.path.join(sourcePath, "Roboto-Regular.ufo") | |
# Make sure the source is UFO 3 | |
source = DefconFont(sourcePath) | |
if source.ufoFormatVersion < 3: | |
print("Converting source to UFO 3...") | |
source.save(formatVersion=3) | |
del source | |
# ------- | |
# Objects | |
# ------- | |
class UFOLibFont(object): | |
UFOReader = ufoLib.UFOReader | |
UFOWriter = ufoLib.UFOWriter | |
def __init__(self, path=None, validate=False): | |
self.validate = validate | |
self.lib = Lib() | |
self.groups = Groups() | |
self.kerning = Kerning() | |
self.info = Info() | |
self.defaultLayerName = None | |
self.layers = Layers() | |
if path: | |
self._read(path) | |
def _read(self, path): | |
reader = self.UFOReader(path, validate=self.validate) | |
reader.readInfo(self.info) | |
self.kerning.update(reader.readKerning()) | |
self.groups.update(reader.readGroups()) | |
self.lib.update(reader.readLib()) | |
self.defaultLayerName = reader.getDefaultLayerName() | |
for layerName in reader.getLayerNames(): | |
layer = Layer() | |
glyphSet = reader.getGlyphSet(layerName) | |
for glyphName in glyphSet.keys(): | |
glyph = Glyph() | |
glyphSet.readGlyph(glyphName, glyph, glyph.getPointPen()) | |
layer[glyphName] = glyph | |
self.layers[layerName] = layer | |
def save(self, path=None): | |
if self.validate is not None: | |
writer = self.UFOWriter(path, validate=self.validate) | |
else: | |
writer = self.UFOWriter(path) | |
writer.writeInfo(self.info) | |
writer.writeGroups(self.groups) | |
writer.writeKerning(self.kerning) | |
writer.writeLib(self.lib) | |
# no images...yet | |
# no data...yet | |
self.layers.save(writer, defaultLayerName=self.defaultLayerName) | |
writer.setModificationTime() | |
def keys(self): | |
return self.layers[self.defaultLayerName].keys() | |
def __iter__(self): | |
return iter(self.layers[self.defaultLayerName]) | |
def __len__(self): | |
return len(self.layers[self.defaultLayerName]) | |
def __getitem__(self, glyphName): | |
return self.layers[self.defaultLayerName][glyphName] | |
class Lib(dict): pass | |
class Info(object): pass | |
class Groups(dict): pass | |
class Kerning(dict): pass | |
class Layers(dict): | |
def save(self, writer, defaultLayerName): | |
for layerName, layer in self.items(): | |
isDefaultLayer = layerName == defaultLayerName | |
glyphSet = writer.getGlyphSet(layerName=layerName, defaultLayer=isDefaultLayer) | |
layer.save(glyphSet) | |
glyphSet.writeLayerInfo(layer) | |
writer.writeLayerContents(self.keys()) | |
class Layer(dict): | |
def __iter__(self): | |
for key in self.keys(): | |
yield self[key] | |
def save(self, glyphSet): | |
for glyphName, glyph in sorted(self.items()): | |
glyphSet.writeGlyph(glyph.name, glyph, glyph.drawPoints) | |
class Glyph(object): | |
def __init__(self): | |
self.name = None | |
self.width = 0 | |
self.unicodes = [] | |
self.lib = Lib() | |
self.contours = [] | |
self.components = [] | |
self.anchors = [] | |
self.guidelines = [] | |
@property | |
def unicode(self): | |
if self.unicodes: | |
return self.unicodes[0] | |
return None | |
def draw(self, pen): | |
pointPen = ufoLib.pointPen.PointToSegmentPen(pen) | |
self.drawPoints(pointPen) | |
def drawPoints(self, pointPen): | |
for contourIdentfier, contourData in self.contours: | |
pointPen.beginPath(identifier=contourIdentfier) | |
for point, segmentType, smooth, name, kwargs in contourData: | |
pointPen.addPoint(point, segmentType, smooth, name, **kwargs) | |
pointPen.endPath() | |
for baseGlyph, transformation, identifier in self.components: | |
pointPen.addComponent(baseGlyph, transformation, identifier=identifier) | |
def getPen(self): | |
pen = ufoLib.pointPen.SegmentToPointPen(self.getPointPen()) | |
return pen | |
def getPointPen(self): | |
return self | |
def beginPath(self, identifier=None): | |
self.contours.append([identifier, []]) | |
def addPoint(self, point, segmentType, smooth, name, **kwargs): | |
self.contours[-1][-1].append((point, segmentType, smooth, name, kwargs)) | |
def addComponent(self, baseGlyph, transformation, identifier=None): | |
self.components.append((baseGlyph, transformation, identifier)) | |
def endPath(self): | |
pass | |
# ----------- | |
# Speed Tests | |
# ----------- | |
NUMBER = 5 | |
print("Speed reading...") | |
def testRead(module, fontClass, validate): | |
def run(): | |
if validate is not None: | |
f = fontClass(sourcePath, validate=validate) | |
else: | |
f = fontClass(sourcePath) | |
for g in f: | |
g.draw(NullPen()) | |
r = timeit.Timer(run).timeit(number=NUMBER) / float(NUMBER) | |
print("%s: validate=%r %f" % (module, validate, r)) | |
testRead("ufoLib", UFOLibFont, True) | |
testRead("ufoLib", UFOLibFont, False) | |
testRead("fontTools", FontToolsFont, None) | |
print("Speed writing...") | |
def testWrite(module, fontClass, validate): | |
if validate is not None: | |
f = fontClass(sourcePath, validate=validate) | |
else: | |
f = fontClass(sourcePath) | |
for g in f: | |
g.draw(NullPen()) | |
savePaths = [os.path.splitext(sourcePath)[0] + "-test%d.ufo" % i for i in range(NUMBER)] | |
removePaths = list(savePaths) | |
def run(): | |
path = savePaths.pop(0) | |
f.save(path) | |
r = timeit.Timer(run).timeit(number=NUMBER) / float(NUMBER) | |
for path in removePaths: | |
shutil.rmtree(path) | |
print("%s: validate=%r %f" % (module, validate, r)) | |
testWrite("ufoLib", UFOLibFont, True) | |
testWrite("ufoLib", UFOLibFont, False) | |
testWrite("fontTools", FontToolsFont, None) | |
# ------------- | |
# Profile Tests | |
# ------------- | |
print("Profile writing...") | |
def profileWrite(): | |
f = UFOLibFont(sourcePath, validate=False) | |
for g in f: | |
g.draw(NullPen()) | |
savePath = os.path.splitext(sourcePath)[0] + "-test1.ufo" | |
if os.path.exists(savePath): | |
shutil.rmtree(savePath) | |
def run(): | |
f.save(savePath) | |
p = cProfile.Profile() | |
p.runcall(run) | |
p.print_stats(sort="tottime") | |
#shutil.rmtree(savePath) | |
profileWrite() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment