Fonttools: Is there a way to decompose components of a glyph in a TTF?

Created on 23 Jul 2020  路  8Comments  路  Source: fonttools/fonttools

I am attempting to decompose components in glyphs within a TTF, but having trouble.

Is there a way to do this?

It seems that there is nothing direct, and that I may need to use pens. I am pretty new to pens, and not sure I understand them completely.

I _think_ I would need to find a way to record points of each component to a pen object, and then draw those points into the ttGlyph with the .drawPoints() method. However, it doesn鈥檛 seem that the decomposingRecordingPen works with TTFont objects. I also can鈥檛 figure out whether I am using the ttGlyphPen correctly (can it even record points, or is it just a way to store points to later draw?).

def decomposeGlyph(font, glyphName):

    components = (font['glyf'][glyphName].getComponentNames(font['glyf']))

    ## BASIC IDEA (not yet working)
    # for component in glyph
        # record tt points of component base
        # draw points to glyph

    glyphset = font.getGlyphSet()

    for comp in components:
        glyph = glyphset[comp]
        pen = ttGlyphPen.TTGlyphPen(glyph)
        # font['glyf'][glyphName].drawPoints(pen, font['glyf'])
        font['glyf'][glyphName].draw(pen, font['glyf'])

It may or may not be a related goal, but I am also hoping to remove overlap in the glyphs in a TTF. If this is easy to point out, I would love to get a head start on it, but if it鈥檚 a totally separate thing, feel free to skip this part and I will look into it separately.

Thank you so much for any tips or insights!

Most helpful comment

This snippet works:

from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import DecomposingRecordingPen
from fontTools.pens.ttGlyphPen import TTGlyphPen


src = "font.ttf"
dst = "font-decomposed.ttf"

f = TTFont(src)

glyfTable = f["glyf"]
gs = f.getGlyphSet()
for glyphName in gs.keys():
    if not glyfTable[glyphName].isComposite():
        continue
    dcPen = DecomposingRecordingPen(gs)
    gs[glyphName].draw(dcPen)
    ttPen = TTGlyphPen(None)
    dcPen.replay(ttPen)
    glyfTable[glyphName] = ttPen.glyph()

f.save(dst)

All 8 comments

This snippet works:

from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import DecomposingRecordingPen
from fontTools.pens.ttGlyphPen import TTGlyphPen


src = "font.ttf"
dst = "font-decomposed.ttf"

f = TTFont(src)

glyfTable = f["glyf"]
gs = f.getGlyphSet()
for glyphName in gs.keys():
    if not glyfTable[glyphName].isComposite():
        continue
    dcPen = DecomposingRecordingPen(gs)
    gs[glyphName].draw(dcPen)
    ttPen = TTGlyphPen(None)
    dcPen.replay(ttPen)
    glyfTable[glyphName] = ttPen.glyph()

f.save(dst)

Regarding remove overlap: booleanOperations does not (yet) support quadratics, but I think skia-pathops does.

It should be easy to make a DecomposingRecordingPointPen. Would you like to give it a shot?
Yes, skia pathops supports quadratics. You can see how ufo2ft uses it in its removeOverlaps filter

Just's snippet now also removes overlaps using skia-pathops, see https://github.com/fonttools/fonttools/commit/3ad201cbe5865e3eda63631ff56b759bdb4162f0

I now realize that a decomposing filter pen would be nice to have, as it eliminates the need to record.

Thank you so much for the snippet, @justvanrossum! This is so helpful. Thanks also for adding it to the snippets of the project.

Huh, being new to pens, I'm still wrapping my head around quite how this works. Probably once I use it, it will start to make more sense. I will try to blog about my pen learnings at some point, to pay it forward.

Thanks, @anthrotype, for the additional insight! I will check out ufo2ft for a reference.

I will check out ufo2ft for a reference.

The way ufo2ft uses pathops.union() function will not work with a TTGlyph object as these don't have getPen() method (unlike defcon or ufoLib2 Glyph). I have already updated Just's script to remove overlaps using skia-pathops (via Path.simplify() which is the underlying method called upon by pathops.union). Just use that.

Awesome, thank you for the help on overlap removal! That is amazing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

justvanrossum picture justvanrossum  路  9Comments

ivanov-v picture ivanov-v  路  11Comments

typoman picture typoman  路  9Comments

black7375 picture black7375  路  4Comments

anthrotype picture anthrotype  路  7Comments