Simple script: Find and complete real ascender and descender values in all open fonts
Ramiro Espinoza
Posts: 839
Hi there,
This script will find the highest and lowest Y values in all open fonts (typically a family) and use them to fill the OpenType specific font metrics values. Works in Fontlab + Robofab (and probably Robofont too).
This script will find the highest and lowest Y values in all open fonts (typically a family) and use them to fill the OpenType specific font metrics values. Works in Fontlab + Robofab (and probably Robofont too).
#FLM: ReType Complete Real Asc/Desc V2.0 # Find and fill real asc and desc values in all open fonts from robofab.world import CurrentFont,AllFonts max_Y_values = [] min_Y_values = [] for font in AllFonts(): for x in font: max_Y_values.append(x.box[3]) min_Y_values.append(x.box[1]) WinAscend = max(max_Y_values) WinDescend = min(min_Y_values) Ascender = font.info.ascender Descender = font.info.descender Gap = (Ascender + abs(Descender)) - (WinAscend + abs(WinDescend)) for font in AllFonts(): font.info.openTypeOS2WinAscent = WinAscend font.info.openTypeOS2WinDescent = abs(WinDescend) font.info.openTypeOS2TypoAscender = Ascender font.info.openTypeOS2TypoDescender = Descender font.info.openTypeHheaAscender = WinAscend font.info.openTypeHheaDescender = -abs(WinDescend) font.info.openTypeHheaLineGap = 0 font.info.openTypeOS2TypoLineGap = abs(Gap) print print "In all open fonts:" print "The maximun Y value is: "+ str(max(max_Y_values)) print "The minimum Y value is: "+ str(min(min_Y_values)) print "TypoLineGap is: " + str(Gap)
Tagged:
1
Comments
-
Hi Ramiro, maybe it would be good to leave out certain glyphs (like aringacute/Aringacute for example)?
there is another script by @Jens Kutilek (based on Karsten Lücke’s Calculations) for calculating the vertical metrics as well:
https://github.com/jenskutilek/RoboFont/blob/master/scripts/SetVerticalMetrics.py
1 -
Hi Lukas,
I sort of forgot the 'Aringacute' issue because I don't include it anymore in my fonts.
But you are right, this situation should be addressed. Below is the same script but with a line that exclude 'Aringacute' from the list. IMO, 'aringacute' is not usually a problem. I will check later Jens' script (surely way much better than mine).#FLM: ReType Complete Real Asc/Desc V2.1 # Find and fill real asc and desc values in all open fonts from robofab.world import CurrentFont,AllFonts max_Y_values = [] min_Y_values = [] for font in AllFonts(): for x in font: if x.name != 'Aringacute': max_Y_values.append(x.box[3]) min_Y_values.append(x.box[1]) WinAscend = max(max_Y_values) WinDescend = min(min_Y_values) Ascender = font.info.ascender Descender = font.info.descender Gap = (Ascender + abs(Descender)) - (WinAscend + abs(WinDescend)) for font in AllFonts(): font.info.openTypeOS2WinAscent = WinAscend font.info.openTypeOS2WinDescent = abs(WinDescend) font.info.openTypeOS2TypoAscender = Ascender font.info.openTypeOS2TypoDescender = Descender font.info.openTypeHheaAscender = WinAscend font.info.openTypeHheaDescender = -abs(WinDescend) font.info.openTypeHheaLineGap = 0 font.info.openTypeOS2TypoLineGap = abs(Gap) print print "In all open fonts:" print "The maximun Y value is: "+ str(max(max_Y_values)) print "The minimum Y value is: "+ str(min(min_Y_values)) print "TypoLineGap is: " + str(Gap)
1 -
The script is useful, but instead of letting the design dictate those values I suggest planning those values from the very beginning of the design process and use it as a restrain. In particular if you are adding Vietnamese or multiscript typefaces.
5 -
Here is a slightly better and a little more complex, de-robofabed script.
It will also get dimensions of composite characters and utilizes a banList of omitted glyphs.#FLM: Set Vertical Metrics 1.2 #(based on ReType.com Complete Real Asc/Desc V2.1) # ------------------------ # (C) Vassil Kateliev, 2016 (http://www.kateliev.com) # No warranties. By using this you agree # that you use it at your own risk! from FL import * # - Global configuration banList = 'Aring Abreve'.split(' ') # List of omitted glyphs workWithComponents = True # Process composite characters - non destructive # - Functions def getFontYBounds(font, banList, workWithComponents = False): ''' Returns tuple(min_Y, max_Y) values for given [font] omitting glyph names (str) listed in [banList]. ''' def getYBounds(glyph): return (glyph.GetBoundingRect().y, glyph.GetBoundingRect().height + glyph.GetBoundingRect().y) fontMinY, fontMaxY = [], [] for gID in range(len(font)): currGlyph = font[gID] if currGlyph.name not in banList and not len(currGlyph.components): yBounds = getYBounds(currGlyph) elif currGlyph.name not in banList and len(currGlyph.components) and workWithComponents: tempGlyph = Glyph(currGlyph) currGlyph.Decompose() # Workaround! tempGlyph is an orphan so it could not be decomposed yBounds = getYBounds(currGlyph) currGlyph.Assign(tempGlyph) # Repair that mess! fontMinY.append(int(yBounds[0])); fontMaxY.append(int(yBounds[1])) return (min(fontMinY), max(fontMaxY)) # - Run ------------------- # - Get values fontBounds = zip(*[getFontYBounds(fl[fID], banList, workWithComponents) for fID in range(len(fl))]) fontInfoBounds = zip(*[(fl[fID].descender[0], fl[fID].ascender[0]) for fID in range(len(fl))]) WinAscend, WinDescend = max(fontBounds[1]), min(fontBounds[0]) Ascender, Descender = max(fontInfoBounds[1]), min(fontInfoBounds[0]) Gap = abs((Ascender + abs(Descender)) - (WinAscend + abs(WinDescend))) # - Process all open fonts for fID in range(len(fl)): currentFont = fl[fID] # - Report print '\nProcessing:\t%s\n-----\nFont feature\tOLD\tNEW' %currentFont.full_name print 'WIN Ascent:\t%s \t%s' %(currentFont.ttinfo.os2_us_win_ascent, WinAscend) print 'WIN Descent:\t%s \t%s' %(currentFont.ttinfo.os2_us_win_descent, abs(WinDescend)) print 'Typo Ascent:\t%s \t%s' %(currentFont.ttinfo.os2_s_typo_ascender, Ascender) print 'Typo Descent:\t%s \t%s' %(currentFont.ttinfo.os2_s_typo_descender, Descender) print 'Typo Line Gap:\t%s \t%s' %(currentFont.ttinfo.os2_s_typo_line_gap,Gap) print 'HHEA Ascent\t%s \t%s' %(currentFont.ttinfo.hhea_ascender, WinAscend) print 'HHEA Descent:\t%s \t%s' %(currentFont.ttinfo.hhea_descender, -abs(WinDescend)) print 'HHEA Line Gap:\t%s \t%s\n' %(currentFont.ttinfo.hhea_line_gap, 0) # - Process currentFont.ttinfo.os2_us_win_ascent = WinAscend currentFont.ttinfo.os2_us_win_descent = abs(WinDescend) currentFont.ttinfo.os2_s_typo_ascender = Ascender currentFont.ttinfo.os2_s_typo_descender = Descender currentFont.ttinfo.hhea_ascender = WinAscend currentFont.ttinfo.hhea_descender = -abs(WinDescend) currentFont.ttinfo.hhea_line_gap = 0 currentFont.ttinfo.os2_s_typo_line_gap = Gap print 'End.'
1 -
PabloImpallari said:The script is useful, but instead of letting the design dictate those values I suggest planning those values from the very beginning of the design process and use it as a restrain. In particular if you are adding Vietnamese or multiscript typefaces.
I agree—and when planning these values from the beginning, you should consider that you need to either allow for such things as Vietnamese stacked accents from the beginning (even if they aren't in your current plans), else be prepared that if you add such things later, you may have to break vertical metrics compatibility with the older version of the font.5 -
The way I use this sort of scripts doesn't exclude the kind of planning Pablo and the rest are mentioning. It would be silly not to have an idea of the vertical boundaries of a family I have designed. But with a script you can quickly determined the highest and lowest points in the heaviest weights and see if everything is within the planned limits.
And I frequently work on fonts from other designers and therefore need to asses the family to enter the correct values. I am not producing Vietnamese fonts and I don't need to care much about stacked diacritics.1 -
Thanks for this gang! Quick way to port this to Glyphs?
0 -
[…] when planning these values from the beginning, you should consider that you need to either allow for such things as Vietnamese stacked accents from the beginning […]I could be wrong, but wasn’t bit 7 (USE_TYPO_METRICS) in the in OS/2 table v.4 not specified to get rid of this problem, i.e., that one can use the largest WinAscent/Descent values to prevent clipping without influencing the line spacing?0
-
Stuart Sandler said:Thanks for this gang! Quick way to port this to Glyphs?0
-
Stuart Sandler said:Thanks for this gang! Quick way to port this to Glyphs?1
-
This script does not use RoboFab any more. I adapted it to run in Glyphs. I hope I didn't mess it up
#FLM: Set Vertical Metrics 1.2 #(based on ReType.com Complete Real Asc/Desc V2.1) # ------------------------ # (C) Vassil Kateliev, 2016 (http://www.kateliev.com) # Adapted for Glyphs by Georg Seifert # No warranties. By using this you agree # that you use it at your own risk! import GlyphsApp # - Global configuration banList = 'Aring Abreve'.split(' ') # List of omitted glyphs workWithComponents = True # Process composite characters - non destructive # - Functions def getFontYBounds(font, banList, workWithComponents = False): ''' Returns tuple(min_Y, max_Y) values for given [font] omitting glyph names (str) listed in [banList]. ''' def getYBounds(layer): bounds = layer.bounds return (NSMinY(bounds), NSMaxY(bounds)) fontMinY, fontMaxY = [], [] masterIDs = [m.id for m in font.masters] for currGlyph in font.glyphs: if currGlyph.name not in banList: for masterID in masterIDs: currLayer = currGlyph.layers[masterID] if not len(currLayer.components) or workWithComponents: yBounds = getYBounds(currLayer) fontMinY.append(int(yBounds[0])); fontMaxY.append(int(yBounds[1])) return (min(fontMinY), max(fontMaxY)) # - Run ------------------- # - Get values fontBounds = [] fontInfoBounds = [] for currFont in Glyphs.fonts: fontBounds.append(getFontYBounds(currFont, banList, workWithComponents)) for currMaster in currFont.masters: fontInfoBounds.append((currMaster.descender, currMaster.ascender)) fontBounds = zip(*fontBounds) fontInfoBounds = zip(*fontInfoBounds) WinAscend, WinDescend = max(fontBounds[1]), min(fontBounds[0]) Ascender, Descender = max(fontInfoBounds[1]), min(fontInfoBounds[0]) Gap = abs((Ascender + abs(Descender)) - (WinAscend + abs(WinDescend))) # - Process all open fonts for currFont in Glyphs.fonts: # - Report print '\nProcessing:\t%s\n-----\nFont feature\tOLD\tNEW' % currFont.familyName for currMaster in currFont.masters: print 'Master:\t%s' % currMaster.name if currMaster.customParameters["winAscent"]: print ' WIN Ascent:\t%s \t%s' % (currMaster.customParameters["winAscent"], WinAscend) if currMaster.customParameters["winDescent"]: print ' WIN Descent:\t%s \t%s' % (currMaster.customParameters["winDescent"], WinDescend) if currMaster.customParameters["typoAscender"]: print ' Typo Ascent:\t%s \t%s' % (currMaster.customParameters["typoAscender"], Ascender) else: print ' Typo Ascent:\t%s \t%s' % (currMaster.ascender, Ascender) if currMaster.customParameters["typoDescender"]: print ' Typo Descent:\t%s \t%s' % (currMaster.customParameters["typoDescender"], Descender) else: print ' Typo Descent:\t%s \t%s' % (currMaster.descender, Descender) if currMaster.customParameters["typoLineGap"]: print ' Typo Line Gap:\t%s \t%s' % (currMaster.customParameters["typoLineGap"], Gap) else: print ' Typo Line Gap:\t%s \t%s' % (currFont.upm * 1.2 - (currMaster.ascender + currMaster.descender), Gap) if currMaster.customParameters["hheaAscender"]: print ' HHEA Ascent\t%s \t%s' % (currMaster.customParameters["hheaAscender"], WinAscend) if currMaster.customParameters["hheaDescender"]: print ' HHEA Descent:\t%s \t%s' % (currMaster.customParameters["hheaDescender"], -abs(WinDescend)) if currMaster.customParameters["hheaLineGap"]: print ' HHEA Line Gap:\t%s \t%s\n' % (currMaster.customParameters["hheaLineGap"], 0) # - Process currMaster.customParameters["winAscent"] = WinAscend currMaster.customParameters["winDescent"] = -abs(WinDescend) if abs(currMaster.ascender - Ascender) > 0.5: currMaster.customParameters["typoAscender"] = Ascender if abs(currMaster.descender - Descender) > 0.5: currMaster.customParameters["typoDescender"] = Descender if abs((currFont.upm * 1.2 - (currMaster.ascender + currMaster.descender)) - Gap) > 0.5: currMaster.customParameters["typoLineGap"] = Gap currMaster.customParameters["hheaAscender"] = WinAscend currMaster.customParameters["hheaDescender"] = -abs(WinDescend) #currentFont.ttinfo.hhea_line_gap = 0 print 'End.'
3 -
Great work @Georg Seifert ! Love the tolerance check and adding MM functionality is a nice touch! (... makes me think about reworking my FL script to add MM)0
-
I've made some changes in my original script so it can store more information about the highest and lowest Y values. Now the info is stored in dictionaries and I can retrieve the value's glyph and font name.
Example:The maximun Y value is: 993 from Otilde in Guyot Text BlackThe minimum Y value is: -274 from Q in Guyot Text Black
You surely can improve it but it works for me.#FLM: ReType Complete Real Asc/Desc V 2.0 # Find and fill real asc and desc values in all open fonts import operator from robofab.world import AllFonts max_Y_values = {} min_Y_values = {} for font in AllFonts(): for x in font: if x.name != 'Aringacute': max_Y_values[x.name + " in " + font.info.postscriptFullName] = x.box[3] min_Y_values[x.name+ " in " + font.info.postscriptFullName] = x.box[1] WinAscend = max(max_Y_values.iteritems(), key=operator.itemgetter(1))[1] WinDescend = min(min_Y_values.iteritems(), key=operator.itemgetter(1))[1] Ascender = font.info.ascender Descender = font.info.descender Gap = (Ascender + abs(Descender)) - (WinAscend + abs(WinDescend)) for font in AllFonts(): font.info.openTypeOS2WinAscent = WinAscend font.info.openTypeOS2WinDescent = abs(WinDescend) font.info.openTypeOS2TypoAscender = Ascender font.info.openTypeOS2TypoDescender = Descender font.info.openTypeHheaAscender = WinAscend font.info.openTypeHheaDescender = -abs(WinDescend) font.info.openTypeHheaLineGap = 0 font.info.openTypeOS2TypoLineGap = abs(Gap) print print "In all open fonts:" print print "The maximun Y value is: " + str(WinAscend) + " from " + max(max_Y_values.iteritems(), key=operator.itemgetter(1))[0] print "The minimum Y value is: " + str(WinDescend) + " from " + min(min_Y_values.iteritems(), key=operator.itemgetter(1))[0] print "TypoLineGap is: " + str(abs(Gap))
0 -
which of the strategies from https://www.glyphsapp.com/tutorials/vertical-metrics does this use? Can it be made to work with the webfont strategy?
0 -
@Kristof Bastiaensen The strategy followed by my script is the 'Microsoft Strategy' described in your link but mine was taken from Karsten Lüecke's paper on font metrics: http://www.kltf.de/downloads/FontMetrics-kltf.pdf
Regarding a webfont font metrics strategy, it is quite simple to implement in the script but I am not sure which should be the most advisable policy. I used to follow the recommendation I found on a Google's page at: https://code.google.com/archive/p/googlefontdirectory/wikis/HowToGenerateWebNativeFonts.wiki but it's not available anymore and the recommendations were different to the one in the Glyphs' page.
For my web fonts I usually follow the following strategy:
First, calculate the winAscent and winDescent values, then:TypoAscender = winAscentTypoDescender = winDescenthheaAscender = winAscenthheaDescender = winDescenthheaLineGap = 0typoLineGap = 0
So, question to everybody, what is currently the best font metric policy for web fonts?1 -
Have a look at this thread: http://typedrawers.com/discussion/17051
-
Thanks! For what I see there is no consensus yet. Kristof, tell me what strategy you prefer and I will tweak the script to reproduce it (as soon as I have a little time).0
-
Ramiro, I have no idea, that's why I am asking I am trying to find the best strategy for a project that I am writing, so any info or algorithm is very useful! I find there isn't much information about these metrics in the opentype spec, and the info that I found on the internet is often contradictory.
EDIT: thanks for the links, they were what I wanted to know!
0 -
@Kristof Bastiaensen I would say: read the entries at http://typedrawers.com/discussion/1705 and pick the option that suit you best.
0
Categories
- All Categories
- 43 Introductions
- 3.7K Typeface Design
- 798 Font Technology
- 1K Technique and Theory
- 617 Type Business
- 444 Type Design Critiques
- 541 Type Design Software
- 30 Punchcutting
- 136 Lettering and Calligraphy
- 83 Technique and Theory
- 53 Lettering Critiques
- 483 Typography
- 301 History of Typography
- 114 Education
- 68 Resources
- 498 Announcements
- 79 Events
- 105 Job Postings
- 148 Type Releases
- 165 Miscellaneous News
- 269 About TypeDrawers
- 53 TypeDrawers Announcements
- 116 Suggestions and Bug Reports