fontbuild: fix issue with variable font where italic glyphs using components offset at the Y-axis would be incorrectly transformed. Also speeds up the glyphsync command
This commit is contained in:
parent
82e3852227
commit
f8d9bd31b0
1 changed files with 57 additions and 37 deletions
|
|
@ -23,6 +23,7 @@ from fontTools.pens.transformPen import TransformPen
|
||||||
from fontTools.pens.reverseContourPen import ReverseContourPen
|
from fontTools.pens.reverseContourPen import ReverseContourPen
|
||||||
from glyphsLib.interpolation import apply_instance_data
|
from glyphsLib.interpolation import apply_instance_data
|
||||||
from mutatorMath.ufo.document import DesignSpaceDocumentReader
|
from mutatorMath.ufo.document import DesignSpaceDocumentReader
|
||||||
|
from multiprocessing import Process, Queue
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
stripItalic_re = re.compile(r'(?:^|\b)italic(?:\b|$)', re.I | re.U)
|
stripItalic_re = re.compile(r'(?:^|\b)italic(?:\b|$)', re.I | re.U)
|
||||||
|
|
@ -51,9 +52,11 @@ def fatal(msg):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def composedGlyphIsNonTrivial(g):
|
def composedGlyphIsNonTrivial(g, yAxisIsNonTrivial=False):
|
||||||
# A non-trivial glyph is one that is composed from either multiple
|
# A non-trivial glyph is one that is composed from either multiple
|
||||||
# components or that uses component transformations.
|
# components or that uses component transformations.
|
||||||
|
# If yAxisIsNonTrivial is true, then any transformation over the Y axis
|
||||||
|
# is be considered non-trivial.
|
||||||
if g.components and len(g.components) > 0:
|
if g.components and len(g.components) > 0:
|
||||||
if len(g.components) > 1:
|
if len(g.components) > 1:
|
||||||
return True
|
return True
|
||||||
|
|
@ -67,6 +70,8 @@ def composedGlyphIsNonTrivial(g):
|
||||||
xScale, xyScale, yxScale, yScale, xOffset, yOffset = c.transformation
|
xScale, xyScale, yxScale, yScale, xOffset, yOffset = c.transformation
|
||||||
if xScale != 1 or xyScale != 0 or yxScale != 0 or yScale != 1:
|
if xScale != 1 or xyScale != 0 or yxScale != 0 or yScale != 1:
|
||||||
return True
|
return True
|
||||||
|
if yAxisIsNonTrivial and yOffset != 0:
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -103,8 +108,9 @@ class VarFontProject(FontProject):
|
||||||
|
|
||||||
decomposeGlyphs = set()
|
decomposeGlyphs = set()
|
||||||
for ufo in ufos:
|
for ufo in ufos:
|
||||||
|
isItalic = ufo.info.italicAngle != 0
|
||||||
for glyph in ufo:
|
for glyph in ufo:
|
||||||
if glyph.components and composedGlyphIsNonTrivial(glyph):
|
if glyph.components and composedGlyphIsNonTrivial(glyph, yAxisIsNonTrivial=isItalic):
|
||||||
decomposeGlyphs.add(glyph.name)
|
decomposeGlyphs.add(glyph.name)
|
||||||
|
|
||||||
self.decompose_glyphs(ufos, lambda g: g.name in decomposeGlyphs)
|
self.decompose_glyphs(ufos, lambda g: g.name in decomposeGlyphs)
|
||||||
|
|
@ -408,6 +414,41 @@ class Main(object):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _glyphsyncWriteUFO(self, font, weight, ufo_path):
|
||||||
|
# fixup font info
|
||||||
|
setFontInfo(font, weight, updateCreated=False)
|
||||||
|
|
||||||
|
# cleanup lib
|
||||||
|
lib = dict()
|
||||||
|
for key, value in font.lib.iteritems():
|
||||||
|
if key.startswith('com.schriftgestaltung'):
|
||||||
|
continue
|
||||||
|
if key == 'public.postscriptNames':
|
||||||
|
continue
|
||||||
|
lib[key] = value
|
||||||
|
font.lib.clear()
|
||||||
|
font.lib.update(lib)
|
||||||
|
|
||||||
|
# remove all but the primary (default) layer
|
||||||
|
layers = font.layers
|
||||||
|
defaultLayer = layers.defaultLayer
|
||||||
|
delLayerNames = set()
|
||||||
|
for layer in layers:
|
||||||
|
if layer != defaultLayer:
|
||||||
|
delLayerNames.add(layer.name)
|
||||||
|
for layerName in delLayerNames:
|
||||||
|
del layers[layerName]
|
||||||
|
|
||||||
|
# clear anchors
|
||||||
|
for g in font:
|
||||||
|
g.clearAnchors()
|
||||||
|
del g.lib['com.schriftgestaltung.Glyphs.lastChange']
|
||||||
|
|
||||||
|
# write UFO file
|
||||||
|
self.log("write %s" % relpath(ufo_path, os.getcwd()))
|
||||||
|
font.save(ufo_path)
|
||||||
|
|
||||||
|
|
||||||
def cmd_glyphsync(self, argv):
|
def cmd_glyphsync(self, argv):
|
||||||
argparser = argparse.ArgumentParser(
|
argparser = argparse.ArgumentParser(
|
||||||
usage='%(prog)s glyphsync <glyphsfile> [options]',
|
usage='%(prog)s glyphsync <glyphsfile> [options]',
|
||||||
|
|
@ -452,13 +493,14 @@ class Main(object):
|
||||||
|
|
||||||
# patch and write UFO files
|
# patch and write UFO files
|
||||||
# TODO: Only write out-of-date UFOs
|
# TODO: Only write out-of-date UFOs
|
||||||
|
procs = []
|
||||||
|
q = Queue()
|
||||||
for source in designspace.sources:
|
for source in designspace.sources:
|
||||||
# source : fontTools.designspaceLib.SourceDescriptor
|
# source : fontTools.designspaceLib.SourceDescriptor
|
||||||
# source.font : defcon.objects.font.Font
|
# source.font : defcon.objects.font.Font
|
||||||
ufo_path = pjoin(master_dir, source.filename.replace('InterUI', 'Inter-UI'))
|
ufo_path = pjoin(master_dir, source.filename.replace('InterUI', 'Inter-UI'))
|
||||||
# no need to also set the relative 'filename' attribute as that
|
# no need to also set the relative 'filename' attribute as that
|
||||||
# will be auto-updated on writing the designspace document
|
# will be auto-updated on writing the designspace document
|
||||||
|
|
||||||
if source.styleName == "Italic Italic":
|
if source.styleName == "Italic Italic":
|
||||||
# Workaround for Glyphs limitation
|
# Workaround for Glyphs limitation
|
||||||
# (Base italic master can't be called just Italic, so it's called
|
# (Base italic master can't be called just Italic, so it's called
|
||||||
|
|
@ -475,41 +517,16 @@ class Main(object):
|
||||||
else:
|
else:
|
||||||
# name "Inter UI Black" => "black"
|
# name "Inter UI Black" => "black"
|
||||||
source.name = source.styleName.lower().replace(' ', '')
|
source.name = source.styleName.lower().replace(' ', '')
|
||||||
|
|
||||||
# fixup font info
|
|
||||||
weight = int(source.location['Weight'])
|
|
||||||
setFontInfo(source.font, weight, updateCreated=False)
|
|
||||||
|
|
||||||
# cleanup lib
|
|
||||||
lib = dict()
|
|
||||||
for key, value in source.font.lib.iteritems():
|
|
||||||
if key.startswith('com.schriftgestaltung'):
|
|
||||||
continue
|
|
||||||
if key == 'public.postscriptNames':
|
|
||||||
continue
|
|
||||||
lib[key] = value
|
|
||||||
source.font.lib.clear()
|
|
||||||
source.font.lib.update(lib)
|
|
||||||
|
|
||||||
# remove all but the primary (default) layer
|
|
||||||
layers = source.font.layers
|
|
||||||
defaultLayer = layers.defaultLayer
|
|
||||||
delLayerNames = set()
|
|
||||||
for layer in layers:
|
|
||||||
if layer != defaultLayer:
|
|
||||||
delLayerNames.add(layer.name)
|
|
||||||
for layerName in delLayerNames:
|
|
||||||
del layers[layerName]
|
|
||||||
|
|
||||||
# clear anchors
|
|
||||||
for g in source.font:
|
|
||||||
g.clearAnchors()
|
|
||||||
del g.lib['com.schriftgestaltung.Glyphs.lastChange']
|
|
||||||
|
|
||||||
# write UFO file
|
|
||||||
source.path = ufo_path
|
source.path = ufo_path
|
||||||
self.log("write %s" % relpath(ufo_path, os.getcwd()))
|
weight = int(source.location['Weight'])
|
||||||
source.font.save(ufo_path)
|
|
||||||
|
p = Process(
|
||||||
|
target=self._glyphsyncWriteUFO,
|
||||||
|
args=(source.font, weight, ufo_path)
|
||||||
|
)
|
||||||
|
p.start()
|
||||||
|
procs.append(p)
|
||||||
|
|
||||||
# patch instance names
|
# patch instance names
|
||||||
for instance in designspace.instances:
|
for instance in designspace.instances:
|
||||||
|
|
@ -520,6 +537,9 @@ class Main(object):
|
||||||
self.log("write %s" % relpath(designspace_file, os.getcwd()))
|
self.log("write %s" % relpath(designspace_file, os.getcwd()))
|
||||||
designspace.write(designspace_file)
|
designspace.write(designspace_file)
|
||||||
|
|
||||||
|
for p in procs:
|
||||||
|
p.join()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_instancegen(self, argv):
|
def cmd_instancegen(self, argv):
|
||||||
|
|
|
||||||
Reference in a new issue