move it to ttLib.removeOverlaps module

This commit is contained in:
Cosimo Lupo 2020-09-23 19:13:58 +01:00
parent 8bbc3d509b
commit 2bcc103c36
No known key found for this signature in database
GPG Key ID: 179A8F0895A02F4F

View File

@ -1,37 +1,29 @@
#! /usr/bin/env python3 """ Simplify TrueType glyphs by merging overlapping contours/components.
# Example script to remove overlaps in TTF using skia-pathops. Requires https://github.com/fonttools/skia-pathops
# Overlapping components will be decomposed. """
import sys
from typing import Iterable, Optional, Mapping from typing import Iterable, Optional, Mapping
from fontTools.ttLib import ttFont from fontTools.ttLib import ttFont
from fontTools.ttLib.tables import _g_l_y_f from fontTools.ttLib.tables import _g_l_y_f
from fontTools.pens.recordingPen import DecomposingRecordingPen from fontTools.pens.recordingPen import DecomposingRecordingPen
from fontTools.pens.ttGlyphPen import TTGlyphPen from fontTools.pens.ttGlyphPen import TTGlyphPen
try: import pathops
import pathops
except ImportError:
sys.exit(
"This script requires the skia-pathops module. "
"`pip install skia-pathops` and then retry."
)
_TTGlyphMapping = Mapping[str, ttFont._TTGlyph] _TTGlyphMapping = Mapping[str, ttFont._TTGlyph]
def skpath_from_simple_glyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path: def skPathFromSimpleGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
path = pathops.Path() path = pathops.Path()
pathPen = path.getPen() pathPen = path.getPen()
glyphSet[glyphName].draw(pathPen) glyphSet[glyphName].draw(pathPen)
return path return path
def skpath_from_composite_glyph( def skPathFromCompositeGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
glyphName: str, glyphSet: _TTGlyphMapping
) -> pathops.Path:
# record TTGlyph outlines without components # record TTGlyph outlines without components
dcPen = DecomposingRecordingPen(glyphSet) dcPen = DecomposingRecordingPen(glyphSet)
glyphSet[glyphName].draw(dcPen) glyphSet[glyphName].draw(dcPen)
@ -42,7 +34,7 @@ def skpath_from_composite_glyph(
return path return path
def simple_glyph_from_skpath(path: pathops.Path) -> _g_l_y_f.Glyph: def ttfGlyphFromSkPath(path: pathops.Path) -> _g_l_y_f.Glyph:
# Skia paths have no 'components', no need for glyphSet # Skia paths have no 'components', no need for glyphSet
ttPen = TTGlyphPen(glyphSet=None) ttPen = TTGlyphPen(glyphSet=None)
path.draw(ttPen) path.draw(ttPen)
@ -53,21 +45,37 @@ def simple_glyph_from_skpath(path: pathops.Path) -> _g_l_y_f.Glyph:
return glyph return glyph
def remove_overlaps( def removeOverlaps(
font: ttFont.TTFont, glyphNames: Optional[Iterable[str]] = None font: ttFont.TTFont, glyphNames: Optional[Iterable[str]] = None
) -> None: ) -> None:
if glyphNames is None: """ Simplify glyphs in TTFont by merging overlapping contours.
glyphNames = font.getGlyphOrder()
Overlapping components are first decomposed to simple contours, then merged.
Currently this only works with TrueType fonts with 'glyf' table.
Raises NotImplementedError if 'glyf' table is absent.
Args:
font: input TTFont object, modified in place.
glyphNames: optional iterable of glyph names (str) to remove overlaps from.
By default, all glyphs in the font are processed.
"""
try:
glyfTable = font["glyf"]
except KeyError:
raise NotImplementedError("removeOverlaps currently only works with TTFs")
glyfTable = font["glyf"]
hmtxTable = font["hmtx"] hmtxTable = font["hmtx"]
glyphSet = font.getGlyphSet() glyphSet = font.getGlyphSet()
if glyphNames is None:
glyphNames = font.getGlyphOrder()
for glyphName in glyphNames: for glyphName in glyphNames:
if glyfTable[glyphName].isComposite(): if glyfTable[glyphName].isComposite():
path = skpath_from_composite_glyph(glyphName, glyphSet) path = skPathFromCompositeGlyph(glyphName, glyphSet)
else: else:
path = skpath_from_simple_glyph(glyphName, glyphSet) path = skPathFromSimpleGlyph(glyphName, glyphSet)
# duplicate path # duplicate path
path2 = pathops.Path(path) path2 = pathops.Path(path)
@ -77,24 +85,31 @@ def remove_overlaps(
# replace TTGlyph if simplified copy is different # replace TTGlyph if simplified copy is different
if path2 != path: if path2 != path:
glyfTable[glyphName] = glyph = simple_glyph_from_skpath(path2) glyfTable[glyphName] = glyph = ttfGlyphFromSkPath(path2)
# also ensure hmtx LSB == glyph.xMin so glyph origin is at x=0 # also ensure hmtx LSB == glyph.xMin so glyph origin is at x=0
width, lsb = hmtxTable[glyphName] width, lsb = hmtxTable[glyphName]
if lsb != glyph.xMin: if lsb != glyph.xMin:
hmtxTable[glyphName] = (width, glyph.xMin) hmtxTable[glyphName] = (width, glyph.xMin)
def main() -> None: def main(args=None):
if len(sys.argv) < 3: import sys
print("usage: remove-overlaps.py fontfile.ttf outfile.ttf [GLYPHNAMES ...]")
if args is None:
args = sys.argv[1:]
if len(args) < 2:
print(
f"usage: fonttools ttLib.removeOverlaps INPUT.ttf OUTPUT.ttf [GLYPHS ...]"
)
sys.exit(1) sys.exit(1)
src = sys.argv[1] src = args[0]
dst = sys.argv[2] dst = args[1]
glyphNames = sys.argv[3:] or None glyphNames = args[2:] or None
with ttFont.TTFont(src) as f: with ttFont.TTFont(src) as f:
remove_overlaps(f, glyphNames) removeOverlaps(f, glyphNames)
f.save(dst) f.save(dst)