googlefonts / fontmake

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

Wrong contour direction for variable TTF from cubic outlines #1093

Closed yanone closed 2 weeks ago

yanone commented 2 weeks ago

Bitter (and its Italic) have wrong contour direction in a few glyphs each.

There are other issues open about contour direction but they don't match my setup here.

While the Fontbakery status below is a WARN, I see this as a real bug that needs fixing regardless.

 >> com.google.fonts/check/outline_direction
    Check the direction of the outermost contour in each glyph
    with Bitter.ttf

    Rationale:                                                                

    In TrueType fonts, the outermost contour of a glyph should be oriented counter-clockwise, while the inner contours      
    should be oriented clockwise. Getting the path direction wrong can lead to rendering issues in some software.           

    More info: https://github.com/fonttools/fontbakery/issues/2056

    WARN The following glyphs have a counter-clockwise outer contour:                                                       

          * Q.alt has a counter-clockwise outer contour                                                                     

          * q.sc.alt has a counter-clockwise outer contour                                                                  

          * uni0414 (U+0414) has a counter-clockwise outer contour                                                          

          * uni051A.alt has a counter-clockwise outer contour [code: ccw-outer-contour]                                     

    Result: WARN

 >> com.google.fonts/check/outline_direction
    Check the direction of the outermost contour in each glyph
    with Bitter-Italic.ttf

    Rationale:                                                                

    In TrueType fonts, the outermost contour of a glyph should be oriented counter-clockwise, while the inner contours      
    should be oriented clockwise. Getting the path direction wrong can lead to rendering issues in some software.           

    More info: https://github.com/fonttools/fontbakery/issues/2056

    WARN The following glyphs have a counter-clockwise outer contour:                                                       

          * uni0414 (U+0414) has a counter-clockwise outer contour                                                          

          * uni044B (U+044B) has a counter-clockwise outer contour                                                          

          * uni044C (U+044C) has a counter-clockwise outer contour                                                          

          * uni048D (U+048D) has a counter-clockwise outer contour                                                          

          * uni04F9 (U+04F9) has a counter-clockwise outer contour [code: ccw-outer-contour]                                

    Result: WARN

I manually checked the sources for all of these and they look good. Note that all of the affected glyphs have inside-corners. But there are many more glyphs with inside-corners, so the existence of the corners cannot be recognized as a defining feature for this issue.

I tried relocating the start point as sometimes that helps. No effect.

Here's one such glyph that comes out wrong in the binaries:

Bildschirmfoto 2024-05-16 um 11 59 25

anthrotype commented 2 weeks ago

have you actually considered the possibility that fontbakery's check is wrong?

anthrotype commented 2 weeks ago

I looked at the ttx dump of the Q.alt glyph (see below), and it looks to me that the contour direction is as expected, i.e. with the outermost contour in clockwise direction; however, what may be the root of the issue in fontbakery's check (not fontmake) is that, in the case of Q.alt (unlike, say, the regular Q), the order of the contours is such that the innermost contour comes before the outermost contour. IMO, the contour order is (or should be) irrelevant, what counts is that outermost is clockwise for TT (counter-clockwise for PS). The fontbakery's check shoud be modified to not depend on contour order.

    <TTGlyph name="Q.alt" xMin="79" yMin="-154" xMax="751" yMax="698">
      <contour>
        <pt x="378" y="14" on="1"/>
        <pt x="455" y="14" on="0"/>
        <pt x="588" y="90" on="0"/>
        <pt x="669" y="241" on="0"/>
        <pt x="669" y="353" on="1"/>
        <pt x="669" y="462" on="0"/>
        <pt x="591" y="606" on="0"/>
        <pt x="464" y="678" on="0"/>
        <pt x="388" y="678" on="1"/>
        <pt x="312" y="678" on="0"/>
        <pt x="181" y="604" on="0"/>
        <pt x="101" y="455" on="0"/>
        <pt x="101" y="342" on="1"/>
        <pt x="101" y="231" on="0"/>
        <pt x="177" y="86" on="0"/>
        <pt x="304" y="14" on="0"/>
      </contour>
      <contour>
        <pt x="389" y="698" on="1"/>
        <pt x="448" y="698" on="0"/>
        <pt x="556" y="657" on="0"/>
        <pt x="641" y="573" on="0"/>
        <pt x="691" y="444" on="0"/>
        <pt x="691" y="356" on="1"/>
        <pt x="691" y="245" on="0"/>
        <pt x="614" y="89" on="0"/>
        <pt x="483" y="2" on="0"/>
        <pt x="403" y="-5" on="1"/>
        <pt x="401" y="-2" on="1"/>
        <pt x="472" y="-45" on="0"/>
        <pt x="568" y="-104" on="0"/>
        <pt x="637" y="-134" on="0"/>
        <pt x="666" y="-134" on="1"/>
        <pt x="683" y="-134" on="0"/>
        <pt x="720" y="-122" on="0"/>
        <pt x="743" y="-111" on="1"/>
        <pt x="751" y="-128" on="1"/>
        <pt x="726" y="-141" on="0"/>
        <pt x="686" y="-154" on="0"/>
        <pt x="666" y="-154" on="1"/>
        <pt x="635" y="-154" on="0"/>
        <pt x="557" y="-120" on="0"/>
        <pt x="445" y="-53" on="0"/>
        <pt x="362" y="-2" on="1"/>
        <pt x="373" y="-6" on="1"/>
        <pt x="294" y="-5" on="0"/>
        <pt x="160" y="72" on="0"/>
        <pt x="79" y="225" on="0"/>
        <pt x="79" y="341" on="1"/>
        <pt x="79" y="431" on="0"/>
        <pt x="129" y="565" on="0"/>
        <pt x="216" y="654" on="0"/>
        <pt x="328" y="698" on="0"/>
      </contour>
      <instructions/>
    </TTGlyph>
anthrotype commented 2 weeks ago

(btw, apologies if my initial reaction might have come across as a bit rude.. blame my lack of sleep! 😅 🕊️ )

yanone commented 2 weeks ago

I see, thanks for digging this up.

Please bear with me though, because after sorting outlines by area (see WIP PR), now a whole bunch of other glyphs are reported as wrong direction.

Here's the dong from the source: Bildschirmfoto 2024-05-16 um 13 57 04 and here from the binary: Bildschirmfoto 2024-05-16 um 14 00 03 Note: since the startpoint wasn't visible in the outline above in Glyphs, I set the startpoint to be the same node as the source. I didn't change anything about the contour direction. And in any case, the bar at the bottom I didn't touch at all. This still looks like wrong contour direction. There are numerous other affected glyphs.

yanone commented 2 weeks ago

Also, the previous algo in Fontbakery was already trying to find the largest outline. Maybe the algo is a bit complicated, but it was indeed looking at the outer contour. The contour direction of Q.alt is wrong in the binaries.

yanone commented 2 weeks ago

Update: There's something wrong with the algo in Fontbakery, even after my edit. I need to somehow confirm the contour direction in the binary independent of Glyphs.app before I'm going crazy. I don't know which part not to trust atm. Will report back.

anthrotype commented 2 weeks ago

hey!

The contour direction of Q.alt is wrong in the binaries.

I kindly disagree :) see my ttx dump above (you should be able to repro with your local ttx command). The first contour of Q.alt is the smaller, inner one. The first (on-curve) point is <pt x="378" y="14" on="1"/>, the following points go towards the right (increasing x) and towards the top (increasing y), so they are going ... 🕐 ... counter-clockwise. Do we agree so far?

On the other hand, the second, outer contour goes clock-wise (as TrueType spec wants) for the same, inverted, reason as above: i.e. the first point is <pt x="389" y="698" on="1"/>, the second etc... you can tell that the points' x coordinates increase and the y coordinates decrease, which mean the contour direction is going clockwise

anthrotype commented 2 weeks ago

@yanone might be worth asking @simoncozens who according to git blame is the author of that FB check

anthrotype commented 2 weeks ago

I would not trust the starting points that you see when you open up a TTF inside Glyphs.app; there may be additional "massaging" of the outlines to turn the TTF glyf table into paths that Glyphs.app can display/edit. What you see in the ttx dump is the true starting point.

yanone commented 2 weeks ago

unnamed-1

This visual is from OTMaster. Yes, I agree that the outlines in the font are okay and Glyphs is messing stuff up.

@simoncozens and @anthrotype, please have a look at this greatly simplified algo for the contour direction. It simply sorts paths by area and looks at the biggest one. This brings the outlines reported for Bitter from 4 down to 0. Is there any situation where we don't want to look at the biggest path?

simoncozens commented 2 weeks ago

What about glyphs where there are more than one outer contour? (colon, semicolon, percent, etc.)

yanone commented 2 weeks ago

Valid point. Your version looked at all outside outlines?

I don't insist on my simplistic approach at all. Can you think of a way to improve the existing algo? It's definitely reporting false positives. I even confirmed from within the check that it reported the Q.alt's inner contour as wrong.

simoncozens commented 2 weeks ago

I'll take a look at the check using Bitter. It's either a bug in font bakery's outermost counter detection or in beziers' path direction algorithm. Either way looks like there is no fontmake problem.

yanone commented 2 weeks ago

It's the former, because the check reported correctly that the outline it found is in the wrong direction. But it was finding that for the inner contour.