googlefonts / fontmake

Compile fonts from sources (UFO, Glyphs) to binary (OpenType, TrueType).
Apache License 2.0
776 stars 93 forks source link

Generating fonts from Designspace failed: Tried to end an empty contour. #1001

Closed clauseggers closed 1 year ago

clauseggers commented 1 year ago

I’m trying to generate a VF from a Glyphs project, but I’m getting this error:

ERROR:root:In 'Playfair-2_1-Roman.glyphs' -> 'master_ufo/Playfair.designspace': Generating fonts from Designspace failed: Tried to end an empty contour.

--verbose DEBUG is not helpful. Is there a way to be clued in on what and/or where the issue specifically occur?

anthrotype commented 1 year ago

thanks. what's the full traceback for the error that you get when you run in --verbose DEBUG?

anthrotype commented 1 year ago

it must come from the TTGlyphPointPen.endPath method:

https://github.com/fonttools/fonttools/blob/0bf84f9c7b2f8804f944ee68a56b493a077fd776/Lib/fontTools/pens/ttGlyphPen.py#L329

@jenskutilek do you remember why you added that check for empty contours in the TTGlyphPointPen? I would think that it's ok if we simply ignore these when creating a TTGlyph, they should be harmless

clauseggers commented 1 year ago

The full log is 28 MB. Which snippet do you want? Here is the last part:

DEBUG:glyphsLib.filters.eraseOpenCorners:Considering line segment (941,0)-(941,70)
DEBUG:glyphsLib.filters.eraseOpenCorners:Crossing points (894, 2) and (821, 73) were not on same side of line segment
DEBUG:glyphsLib.filters.eraseOpenCorners:Considering line segment (803,156)-(803,452)
DEBUG:glyphsLib.filters.eraseOpenCorners:Crossing points (803, 84) and (803, 527) were not on same side of line segment
DEBUG:glyphsLib.filters.eraseOpenCorners:Considering line segment (911,538)-(911,608)
DEBUG:glyphsLib.filters.eraseOpenCorners:Crossing points (813, 535) and (872, 606) were not on same side of line segment
DEBUG:glyphsLib.filters.eraseOpenCorners:Considering line segment (502,608)-(502,538)
DEBUG:glyphsLib.filters.eraseOpenCorners:Crossing points (539, 606) and (600, 535) were not on same side of line segment
DEBUG:glyphsLib.filters.eraseOpenCorners:Considering line segment (610,452)-(610,156)
DEBUG:glyphsLib.filters.eraseOpenCorners:Crossing points (610, 527) and (610, 82) were not on same side of line segment
DEBUG:glyphsLib.filters.eraseOpenCorners:Considering line segment (472,70)-(472,0)
DEBUG:glyphsLib.filters.eraseOpenCorners:Crossing points (596, 73) and (514, 2) were not on same side of line segment
DEBUG:glyphsLib.filters.eraseOpenCorners:All done, count of segments now: 14
DEBUG:glyphsLib.filters.eraseOpenCorners:Segments: [((472, 0), (514, 2), (594, 3), (707, 3)), ((707, 3), (815, 3), (894, 2), (941, 0)), ((941, 0), (941, 70)), ((941, 70), (821, 73), (803, 84), (803, 156)), ((803, 156), (803, 452)), ((803, 452), (803, 527), (813, 535), (911, 538)), ((911, 538), (911, 608)), ((911, 608), (872, 606), (802, 605), (707, 605)), ((707, 605), (608, 605), (539, 606), (502, 608)), ((502, 608), (502, 538)), ((502, 538), (600, 535), (610, 527), (610, 452)), ((610, 452), (610, 156)), ((610, 156), (610, 82), (596, 73), (472, 70)), ((472, 70), (472, 0))]
DEBUG:ufo2ft.timer:Took 0.423s to run EraseOpenCornersFilter on 33 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointSemiCondensed
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointSemiExpanded
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointBlackSemiCondensed
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointBlackSemiExpanded
DEBUG:ufo2ft.timer:Took 0.003s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateSemiCondensed
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateSemiExpanded
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateBlackSemiCondensed
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateBlackSemiExpanded
DEBUG:ufo2ft.timer:Took 0.001s to run DecomposeComponentsFilter on 6 glyphs
INFO:cu2qu.ufo:New spline lengths: 1: 144, 2: 5262, 3: 2728, 4: 333, 5: 53, 6: 33
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointSemiCondensed
DEBUG:ufo2ft.timer:Took 0.027s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointSemiExpanded
DEBUG:ufo2ft.timer:Took 0.024s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointBlackSemiCondensed
DEBUG:ufo2ft.timer:Took 0.023s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-NeedlepointBlackSemiExpanded
DEBUG:ufo2ft.timer:Took 0.023s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateSemiCondensed
DEBUG:ufo2ft.timer:Took 0.023s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateSemiExpanded
DEBUG:ufo2ft.timer:Took 0.022s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateBlackSemiCondensed
DEBUG:ufo2ft.timer:Took 0.022s to run DecomposeComponentsFilter on 175 glyphs
INFO:ufo2ft.filters:Running DecomposeComponentsFilter on Playfair-AgateBlackSemiExpanded
DEBUG:ufo2ft.timer:Took 0.022s to run DecomposeComponentsFilter on 175 glyphs
DEBUG:ufo2ft.timer:Took 6.984s to preprocess UFO
INFO:ufo2ft:Building OpenType tables for Playfair-NeedlepointSemiCondensed
ERROR:root:In 'Playfair-2_1-Roman.glyphs' -> 'master_ufo/Playfair.designspace': Generating fonts from Designspace failed: Tried to end an empty contour.
Traceback (most recent call last):
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontmake/font_project.py", line 1127, in run_from_designspace
    self._run_from_designspace_interpolatable(
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontmake/font_project.py", line 1223, in _run_from_designspace_interpolatable
    self.build_variable_fonts(
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontmake/font_project.py", line 420, in build_variable_fonts
    fonts = ufo2ft.compileVariableTTFs(
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/__init__.py", line 626, in compileVariableTTFs
    vfNameToBaseUfo = _compileNeededSources(
                      ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/__init__.py", line 830, in _compileNeededSources
    ttfDesignSpace = compileInterpolatableFunc(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/__init__.py", line 405, in compileInterpolatableTTFsFromDS
    for source, ttf in zip(result.sources, ttfs):
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/__init__.py", line 322, in compileInterpolatableTTFs
    ttf = call_outline_compiler(
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontTools/misc/loggingTools.py", line 375, in wrapper
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/__init__.py", line 83, in call_outline_compiler
    return outlineCompiler.compile()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 159, in compile
    self.setupTable_head()
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 339, in setupTable_head
    xMin, yMin, xMax, yMax = self.fontBoundingBox
                             ^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 238, in fontBoundingBox
    self._fontBoundingBox = self.makeFontBoundingBox()
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 224, in makeFontBoundingBox
    for glyphBox in self.glyphBoundingBoxes.values():
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 212, in glyphBoundingBoxes
    self._glyphBoundingBoxes = self.makeGlyphsBoundingBoxes()
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 1467, in makeGlyphsBoundingBoxes
    ttGlyphs = self.getCompiledGlyphs()
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 192, in getCompiledGlyphs
    self._compiledGlyphs = self.compileGlyphs()
                           ^^^^^^^^^^^^^^^^^^^^
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufo2ft/outlineCompiler.py", line 1449, in compileGlyphs
    glyph.drawPoints(pen)
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufoLib2/objects/glyph.py", line 355, in drawPoints
    contour.drawPoints(pointPen)
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/ufoLib2/objects/contour.py", line 184, in drawPoints
    pointPen.endPath()
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontTools/pens/ttGlyphPen.py", line 329, in endPath
    raise PenError("Tried to end an empty contour.")
fontTools.pens.basePen.PenError: Tried to end an empty contour.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontmake/__main__.py", line 656, in main
    project.run_from_glyphs(inputs.glyphs_path, **args)
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontmake/font_project.py", line 839, in run_from_glyphs
    self.run_from_designspace(
  File "/Users/claus/.local/virtualenv/fontmake/venv/lib/python3.11/site-packages/fontmake/font_project.py", line 1142, in run_from_designspace
    raise FontmakeError(
fontmake.errors.FontmakeError: In 'Playfair-2_1-Roman.glyphs' -> 'master_ufo/Playfair.designspace': Generating fonts from Designspace failed: Tried to end an empty contour.
anthrotype commented 1 year ago

Thanks, I made this PR in fonttools to ignore such empty contours: https://github.com/fonttools/fonttools/pull/3145

it's weird that you end up with these. I hacked ufo2ft to print the name of the glyph where that exception occurs and it gave me "zero.dnom.afrc", but I can't see any empty contours in there.. probably because they are empty! 😅

I'd be curious to know how one ends up with these..

clauseggers commented 1 year ago

Alright. I had a look and zero.dnom.afrc has zero.numr.afrc placed as a component, and all the afrc glyphs have a zero advance width to make the feature work. When I opened the glyph all the layers were missing from the layers palette. Looked like corrupted data, which is possibly the fault of either Glyphs or git which I manhandled yesterday.

In any even, I deleted the glyph and made it again. Some of the other .dnom.afrc had incorrect advance width, so I fixed those too.

I don’t know what to say … we are dealing with Glyphs here.

So after this operation I got a lot further in the process.

jenskutilek commented 1 year ago

it must come from the TTGlyphPointPen.endPath method:

https://github.com/fonttools/fonttools/blob/0bf84f9c7b2f8804f944ee68a56b493a077fd776/Lib/fontTools/pens/ttGlyphPen.py#L329

@jenskutilek do you remember why you added that check for empty contours in the TTGlyphPointPen? I would think that it's ok if we simply ignore these when creating a TTGlyph, they should be harmless

Ha, you already said back then that the error may be too harsh :)

https://github.com/fonttools/fonttools/pull/2205#discussion_r630910722

I'm fine with dropping the assertion.