Fonttools: Bad font data after instantiating variable font

Created on 6 Dec 2019  路  6Comments  路  Source: fonttools/fonttools

I'm coming across bad font data when using fontTools pens to interact with instantiated variable fonts. The problem can be reproduced in every contour in every Quadratic variable font I've tried.

When reading the point data with a pen, the point order is offset by one (which in this case I don't mind) and the last two lineTo points are joined together into a qCurve segment (which distorts the shape). The problem happens in both the instancer and mutator variations of the instantiateVariableFont function.

However, the font data in the TTF looks fine to me when I save it to a file and open it in an editor or read through a TTX dump.

This demo just happens to use Monserrat (to make sure that the problem wasn't in my own font) and I set the location to the default weight to make the output comparison more clear, but the problem happens at any location (and with any Quadratic TTF)

Can anyone think of what I might be doing wrong, or where the problem might be coming from?

from fontTools.varLib import mutator, instancer
from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import RecordingPen


def printReport(f):
    g = f.getGlyphSet()["I"]
    pen = RecordingPen()
    g.draw(pen)
    for i in pen.value:
        print(i)  

fontPath = "Montserrat-VF.ttf"
location = {"wght":100}

print("Default:")
f = TTFont(fontPath)
printReport(f)

print("\nmutator.instantiateVariableFont")
instM = mutator.instantiateVariableFont(f, location)
#instM.save("mutator.ttf")
printReport(instM)

print("\ninstancer.instantiateVariableFont")
instI = instancer.instantiateVariableFont(f, location)
#instI.save("instancer.ttf")
printReport(instI)

Output:

Default:
('moveTo', ((133, 700),))
('lineTo', ((153, 700),))
('lineTo', ((153, 0),))
('lineTo', ((133, 0),))
('lineTo', ((133, 700),))
('closePath', ())

mutator.instantiateVariableFont
('moveTo', ((153, 700),))
('lineTo', ((153, 0),))
('lineTo', ((133, 0),))
('qCurveTo', ((133, 700), (153, 700)))
('closePath', ())

instancer.instantiateVariableFont
('moveTo', ((153, 700),))
('lineTo', ((153, 0),))
('lineTo', ((133, 0),))
('qCurveTo', ((133, 700), (153, 700)))
('closePath', ())

Most helpful comment

Formally this bug is 16 years old! This code barely changed in the meantime, it only got moved around a bit.

All 6 comments

It's a regression that was introduced in fonttools 3.39.0. I'll try to dig deeper.

As of fonttools 3.39.0, when instantiating a vf, glyphs get a special flag that indicates some rasterizers that overlaps are ok. This flag is stored in the first coordinate's flags, which also has the flag for on-curve-ness. Before this change, a flags value could only ever be 0 or 1. The code that draws a glyph to a pen naively (buggily) assumed this, so it broke when encountering the overlap flag. #1772 fixes this.

Formally this bug is 16 years old! This code barely changed in the meantime, it only got moved around a bit.

To be clear: the data generated by varLib.mutator and varLib.instancer was perfectly fine, it was the glyph.draw() method that contained the bug.

Thank you Just, you're so fast!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

laerm0 picture laerm0  路  8Comments

justvanrossum picture justvanrossum  路  9Comments

jenskutilek picture jenskutilek  路  7Comments

anthrotype picture anthrotype  路  7Comments

cjdunn picture cjdunn  路  11Comments