mirror of
https://github.com/rsms/inter.git
synced 2024-11-17 07:47:33 +01:00
fontbuild: adds support for post-processing directives on a per-glyph basis. Add "!post:DIRECTIVE" in glyph notes. Only supported directive is "removeoverlaps"
This commit is contained in:
parent
783f38a700
commit
a8fc89d01f
1 changed files with 50 additions and 14 deletions
|
@ -25,10 +25,11 @@ from fontTools.pens.reverseContourPen import ReverseContourPen
|
|||
from glyphsLib.interpolation import apply_instance_data
|
||||
from mutatorMath.ufo.document import DesignSpaceDocumentReader
|
||||
from multiprocessing import Process, Queue
|
||||
# from ufo2ft.filters.removeOverlaps import RemoveOverlapsFilter
|
||||
from ufo2ft.filters.removeOverlaps import RemoveOverlapsFilter
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
stripItalic_re = re.compile(r'(?:^|\b)italic(?:\b|$)', re.I | re.U)
|
||||
findPost_re = re.compile(r'\!post:([^ ]+)', re.I | re.U)
|
||||
|
||||
|
||||
def stripItalic(name):
|
||||
|
@ -77,6 +78,26 @@ def composedGlyphIsNonTrivial(g, yAxisIsNonTrivial=False):
|
|||
return False
|
||||
|
||||
|
||||
knownDirectives = set([
|
||||
'removeoverlap',
|
||||
])
|
||||
|
||||
|
||||
def findGlyphDirectives(g): # -> set<string> | None
|
||||
directives = set()
|
||||
if g.note and len(g.note) > 0:
|
||||
for directive in findPost_re.findall(g.note):
|
||||
directive = directive.lower()
|
||||
if directive in knownDirectives:
|
||||
directives.add(directive)
|
||||
else:
|
||||
print(
|
||||
'unknown glyph directive !post:%s in glyph %s' % (directive, g.name),
|
||||
file=sys.stderr
|
||||
)
|
||||
return directives
|
||||
|
||||
|
||||
class VarFontProject(FontProject):
|
||||
def decompose_glyphs(self, ufos, glyph_filter=lambda g: True):
|
||||
"""Move components of UFOs' glyphs to their outlines."""
|
||||
|
@ -100,29 +121,42 @@ class VarFontProject(FontProject):
|
|||
# the contour direction of the component
|
||||
xx, xy, yx, yy = transformation[:4]
|
||||
if xx*yy - xy*yx < 0:
|
||||
pen = ReverseContourPen(pen)
|
||||
pen = ReverseContourPen(pen)
|
||||
component.draw(pen)
|
||||
|
||||
def build_interpolatable_ttfs(self, ufos, **kwargs):
|
||||
"""Build OpenType binaries with interpolatable TrueType outlines."""
|
||||
# We decompose any glyph with two or more components to make sure
|
||||
# that fontTools varLib is able to produce properly-slanting interpolation.
|
||||
|
||||
|
||||
decomposeGlyphs = set()
|
||||
removeOverlapsGlyphs = set()
|
||||
|
||||
for ufo in ufos:
|
||||
updateFontVersion(ufo)
|
||||
isItalic = ufo.info.italicAngle != 0
|
||||
for glyph in ufo:
|
||||
if glyph.components and composedGlyphIsNonTrivial(glyph, yAxisIsNonTrivial=isItalic):
|
||||
decomposeGlyphs.add(glyph.name)
|
||||
ufoname = basename(ufo.path)
|
||||
for g in ufo:
|
||||
directives = findGlyphDirectives(g)
|
||||
if g.components and composedGlyphIsNonTrivial(g, yAxisIsNonTrivial=isItalic):
|
||||
decomposeGlyphs.add(g.name)
|
||||
if 'removeoverlap' in directives:
|
||||
if g.components and len(g.components) > 0:
|
||||
decomposeGlyphs.add(g.name)
|
||||
removeOverlapsGlyphs.add(g)
|
||||
|
||||
self.decompose_glyphs(ufos, lambda g: g.name in decomposeGlyphs)
|
||||
|
||||
# for ufo in ufos:
|
||||
# filter = RemoveOverlapsFilter(backend='pathops')
|
||||
# filter.start()
|
||||
# for g in ufo:
|
||||
# filter.filter(g)
|
||||
if len(removeOverlapsGlyphs) > 0:
|
||||
rmoverlapFilter = RemoveOverlapsFilter(backend='pathops')
|
||||
rmoverlapFilter.start()
|
||||
for g in removeOverlapsGlyphs:
|
||||
log.info(
|
||||
'Removing overlaps in glyph "%s" of %s',
|
||||
g.name,
|
||||
basename(g.getParent().path)
|
||||
)
|
||||
rmoverlapFilter.filter(g)
|
||||
|
||||
self.save_otfs(ufos, ttf=True, interpolatable=True, **kwargs)
|
||||
|
||||
|
@ -268,6 +302,7 @@ class Main(object):
|
|||
|
||||
# parse CLI arguments
|
||||
args = argparser.parse_args(argv[1:i])
|
||||
logFormat = '%(funcName)s: %(message)s'
|
||||
if args.quiet:
|
||||
self.quiet = True
|
||||
if args.debug:
|
||||
|
@ -275,13 +310,14 @@ class Main(object):
|
|||
if args.verbose:
|
||||
fatal("--quiet and --verbose are mutually exclusive arguments")
|
||||
elif args.debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.DEBUG, format=logFormat)
|
||||
self.logLevelName = 'DEBUG'
|
||||
elif args.verbose:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logging.basicConfig(level=logging.INFO, format=logFormat)
|
||||
self.logLevelName = 'INFO'
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logFormat = '%(message)s'
|
||||
logging.basicConfig(level=logging.WARNING, format=logFormat)
|
||||
self.logLevelName = 'WARNING'
|
||||
|
||||
if args.chdir:
|
||||
|
|
Loading…
Reference in a new issue