afdko icon indicating copy to clipboard operation
afdko copied to clipboard

[makeotf] winAscent/winDescent calculation seems to be based on .notdef

Open frankrolf opened this issue 2 years ago • 3 comments

I’ve wondered about makeotf’s complaint WARNING: Negative internal leading: win.ascent + win.descent < unitsPerEm for a while, and decided to conduct some investigation.

Setting aside the fact that winAscent and winDescent often are set equivalent to TypoAscender/TypoDescender today, I noticed that makeotf complains in almost every case, unless the values are explicitly set using a OS/2 table override.

Attached is a little script, which writes a dummy UFO, draws a glyph with given ascender and descender, and makes an OTF. The bounds of the drawn glyph are printed, the OTF’s OS/2 table is read via ttx, and found winAscender/winDescender values are displayed.

scenario

default output (drawing into .notdef):

python3 winAscentDescentTest.py
┌──────────┐
│ 750/-250 │
└──────────┘
.notdef (0, -250, 500, 750)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

┌────────┐
│ 10/-10 │
└────────┘
.notdef (0, -10, 500, 10)
<usWinAscent value="10"/>
<usWinDescent value="10"/>

┌──────────┐
│ 100/-100 │
└──────────┘
.notdef (0, -100, 500, 100)
<usWinAscent value="100"/>
<usWinDescent value="100"/>

┌─────────┐
│ 250/500 │
└─────────┘
.notdef (0, 250, 500, 500)
<usWinAscent value="500"/>
<usWinDescent value="0"/>

┌──────────┐
│ 500/-250 │
└──────────┘
.notdef (0, -250, 500, 500)
<usWinAscent value="500"/>
<usWinDescent value="250"/>

┌────────┐
│ 1000/0 │
└────────┘
.notdef (0, 0, 500, 1000)
<usWinAscent value="1000"/>
<usWinDescent value="0"/>

This seems to indicate that the glyph we’re drawing is setting the winAscent/winDescent values.


alternate output (drawing into a):

python3 winAscentDescentTest.py -g a
┌──────────┐
│ 750/-250 │
└──────────┘
a (0, -250, 500, 750)
<usWinAscent value="700"/>
<usWinDescent value="0"/>

┌────────┐
│ 10/-10 │
└────────┘
a (0, -10, 500, 10)
<usWinAscent value="700"/>
<usWinDescent value="0"/>

┌──────────┐
│ 100/-100 │
└──────────┘
a (0, -100, 500, 100)
<usWinAscent value="700"/>
<usWinDescent value="0"/>

┌─────────┐
│ 250/500 │
└─────────┘
a (0, 250, 500, 500)
<usWinAscent value="700"/>
<usWinDescent value="0"/>

┌──────────┐
│ 500/-250 │
└──────────┘
a (0, -250, 500, 500)
<usWinAscent value="700"/>
<usWinDescent value="0"/>

┌────────┐
│ 1000/0 │
└────────┘
a (0, 0, 500, 1000)
<usWinAscent value="700"/>
<usWinDescent value="0"/>

This contradicts the above assumption – all fonts have the same WinAscent value (= bounds of the default .notdef)


with features.fea present:

table OS/2 {
	winAscent 750;
	winDescent 250;
} OS/2;
┌──────────┐
│ 750/-250 │
└──────────┘
.notdef (0, -250, 500, 750)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

┌────────┐
│ 10/-10 │
└────────┘
.notdef (0, -10, 500, 10)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

┌──────────┐
│ 100/-100 │
└──────────┘
.notdef (0, -100, 500, 100)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

┌─────────┐
│ 250/500 │
└─────────┘
.notdef (0, 250, 500, 500)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

┌──────────┐
│ 500/-250 │
└──────────┘
.notdef (0, -250, 500, 500)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

┌────────┐
│ 1000/0 │
└────────┘
.notdef (0, 0, 500, 1000)
<usWinAscent value="750"/>
<usWinDescent value="250"/>

As expected, all fonts have the winAscent/winDescent set in the table override.

conclusion

  • .notdef is always used for the calculation of the winAscent/winDescent values in the font binary
  • makeotf will always emit the above complaint, unless a feature override is set, which stipulates that |winAscent| + |winDescent| == UPM
  • changing winAscent/winDescent within the UFO does not make a difference

questions

  • should the winAscent/winDescent be read from the UFO’s footprint (f.bounds)? (recommendation: maybe)
  • should the winAscent/winDescent values be read if explicitly specified in the fontinfo.plist? (recommendation: definitely)
  • should the winAscent/winDescent values fall back to the vertical metrics in fontinfo.plist? (recommendation: yes, better than relying on the bounds of .notdef)
  • should makeotf even warn about the winAscent/winDescent values, given that vertical metrics are open for interpretation? (open for discussion)

winAscentDescentTest.py.zip

frankrolf avatar Mar 08 '22 23:03 frankrolf

Interesting results 😸

I agree with your recommendations. But if we are going to make changes to makeotf's behavior we will need to be careful to advertise it, as it's possible some users might be relying on the current behavior (even unknowingly) and an unexpected change could cause headaches. Triggering the new behavior with a new flag could avoid that, but then that's yet another flag to document and maintain.

josh-hadley avatar Mar 09 '22 00:03 josh-hadley

Probably the strongest point is that many values within the UFO are simply ignored by makeotf (also seen in #1470, for example). If the respective values are set, I think they should be used.

frankrolf avatar Mar 09 '22 13:03 frankrolf

Agree that .notdef shouldn't have any bearing on how winAscent and winDescent are set. I think any unknowing reliance on the current behavior is kind of on the user

ryanbugden avatar Oct 28 '22 18:10 ryanbugden