decompose composites only if components intersect; let pathops.PathPen decompose components
requires https://github.com/fonttools/skia-pathops/pull/32
This commit is contained in:
parent
d4fd5d6eb1
commit
da439c7c57
@ -3,11 +3,11 @@
|
|||||||
Requires https://github.com/fonttools/skia-pathops
|
Requires https://github.com/fonttools/skia-pathops
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import itertools
|
||||||
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.ttGlyphPen import TTGlyphPen
|
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
||||||
|
|
||||||
import pathops
|
import pathops
|
||||||
@ -16,22 +16,46 @@ import pathops
|
|||||||
_TTGlyphMapping = Mapping[str, ttFont._TTGlyph]
|
_TTGlyphMapping = Mapping[str, ttFont._TTGlyph]
|
||||||
|
|
||||||
|
|
||||||
def skPathFromSimpleGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
|
def skPathFromGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
|
||||||
path = pathops.Path()
|
path = pathops.Path()
|
||||||
pathPen = path.getPen()
|
pathPen = path.getPen(glyphSet=glyphSet)
|
||||||
glyphSet[glyphName].draw(pathPen)
|
glyphSet[glyphName].draw(pathPen)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def skPathFromCompositeGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
|
def skPathFromGlyphComponent(
|
||||||
# record TTGlyph outlines without components
|
component: _g_l_y_f.GlyphComponent, glyphSet: _TTGlyphMapping
|
||||||
dcPen = DecomposingRecordingPen(glyphSet)
|
):
|
||||||
glyphSet[glyphName].draw(dcPen)
|
baseGlyphName, transformation = component.getComponentInfo()
|
||||||
# replay recording onto a skia-pathops Path
|
path = skPathFromGlyph(baseGlyphName, glyphSet)
|
||||||
path = pathops.Path()
|
return path.transform(*transformation)
|
||||||
pathPen = path.getPen()
|
|
||||||
dcPen.replay(pathPen)
|
|
||||||
return path
|
def componentsOverlap(glyph: _g_l_y_f.Glyph, glyphSet: _TTGlyphMapping) -> bool:
|
||||||
|
if not glyph.isComposite():
|
||||||
|
raise ValueError("This method only works with TrueType composite glyphs")
|
||||||
|
if len(glyph.components) < 2:
|
||||||
|
return False # single component, no overlaps
|
||||||
|
|
||||||
|
component_paths = {}
|
||||||
|
|
||||||
|
def _get_nth_component_path(index: int) -> pathops.Path:
|
||||||
|
if index not in component_paths:
|
||||||
|
component_paths[index] = skPathFromGlyphComponent(
|
||||||
|
glyph.components[index], glyphSet
|
||||||
|
)
|
||||||
|
return component_paths[index]
|
||||||
|
|
||||||
|
return any(
|
||||||
|
pathops.op(
|
||||||
|
_get_nth_component_path(i),
|
||||||
|
_get_nth_component_path(j),
|
||||||
|
pathops.PathOp.INTERSECTION,
|
||||||
|
fix_winding=False,
|
||||||
|
keep_starting_points=False,
|
||||||
|
)
|
||||||
|
for i, j in itertools.combinations(range(len(glyph.components)), 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def ttfGlyphFromSkPath(path: pathops.Path) -> _g_l_y_f.Glyph:
|
def ttfGlyphFromSkPath(path: pathops.Path) -> _g_l_y_f.Glyph:
|
||||||
@ -66,6 +90,7 @@ def removeOverlaps(
|
|||||||
raise NotImplementedError("removeOverlaps currently only works with TTFs")
|
raise NotImplementedError("removeOverlaps currently only works with TTFs")
|
||||||
|
|
||||||
hmtxTable = font["hmtx"]
|
hmtxTable = font["hmtx"]
|
||||||
|
# wraps the underlying glyf Glyphs, takes care of interfacing with drawing pens
|
||||||
glyphSet = font.getGlyphSet()
|
glyphSet = font.getGlyphSet()
|
||||||
|
|
||||||
if glyphNames is None:
|
if glyphNames is None:
|
||||||
@ -84,10 +109,12 @@ def removeOverlaps(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
for glyphName in glyphNames:
|
for glyphName in glyphNames:
|
||||||
if glyfTable[glyphName].isComposite():
|
glyph = glyfTable[glyphName]
|
||||||
path = skPathFromCompositeGlyph(glyphName, glyphSet)
|
# decompose composite glyphs only if components overlap each other
|
||||||
else:
|
if glyph.isComposite() and not componentsOverlap(glyph, glyphSet):
|
||||||
path = skPathFromSimpleGlyph(glyphName, glyphSet)
|
continue
|
||||||
|
|
||||||
|
path = skPathFromGlyph(glyphName, glyphSet)
|
||||||
|
|
||||||
# remove overlaps
|
# remove overlaps
|
||||||
path2 = pathops.simplify(path, clockwise=path.clockwise)
|
path2 = pathops.simplify(path, clockwise=path.clockwise)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user