Skip to content

Commit

Permalink
filters: sort glyphs by decreasing component depth to avoid order-dep…
Browse files Browse the repository at this point in the history
…endent issues

Fixes #621
  • Loading branch information
anthrotype committed Jun 22, 2022
1 parent 42257bc commit 5f4d591
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
13 changes: 10 additions & 3 deletions Lib/ufo2ft/filters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from fontTools.misc.loggingTools import Timer

from ufo2ft.util import _GlyphSet, _LazyFontName
from ufo2ft.util import _GlyphSet, _LazyFontName, getMaxComponentDepth

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -186,9 +186,16 @@ def __call__(self, font, glyphSet=None):
include = self.include
modified = context.modified

# process composite glyphs in decreasing component depth order (i.e. composites
# with more deeply nested components before shallower ones) to avoid
# order-dependent interferences while filtering glyphs with nested components
# https://github.com/googlefonts/ufo2ft/issues/621
orderedGlyphs = sorted(
glyphSet.keys(), key=lambda g: -getMaxComponentDepth(glyphSet[g], glyphSet)
)

with Timer() as t:
# we sort the glyph names to make loop deterministic
for glyphName in sorted(glyphSet.keys()):
for glyphName in orderedGlyphs:
if glyphName in modified:
continue
glyph = glyphSet[glyphName]
Expand Down
25 changes: 25 additions & 0 deletions Lib/ufo2ft/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,3 +546,28 @@ def ensure_all_sources_have_names(doc: DesignSpaceDocument) -> None:
source.name = f"temp_master.{counter}"
counter += 1
used_names.add(source.name)


def getMaxComponentDepth(glyph, glyphSet, maxComponentDepth=0):
"""Return the height of a composite glyph's tree of components.
This is equal to the depth of its deepest node, where the depth
means the number of edges (component references) from the node
to the tree's root.
For glyphs that contain no components, only contours, this is 0.
Composite glyphs have max component depth of 1 or greater.
"""
if not glyph.components:
return maxComponentDepth

maxComponentDepth += 1

initialMaxComponentDepth = maxComponentDepth
for component in glyph.components:
componentDepth = getMaxComponentDepth(
glyphSet[component.baseGlyph], glyphSet, initialMaxComponentDepth
)
maxComponentDepth = max(maxComponentDepth, componentDepth)

return maxComponentDepth

0 comments on commit 5f4d591

Please sign in to comment.