From 5cc911e559ef34ce0a7214b1e32c6f7ce2aaee30 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Fri, 20 Jan 2023 18:28:18 +0100 Subject: [PATCH 1/4] font-patcher: Use format for output Signed-off-by: Fini Jastrow --- font-patcher | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/font-patcher b/font-patcher index 2c5ec3982..114ee4a6f 100755 --- a/font-patcher +++ b/font-patcher @@ -1566,7 +1566,7 @@ def setup_arguments(): args = parser.parse_args() if args.makegroups and not FontnameParserOK: - sys.exit(projectName + ": FontnameParser module missing (bin/scripts/name_parser/Fontname*), can not --makegroups".format(projectName)) + sys.exit("{}: FontnameParser module missing (bin/scripts/name_parser/Fontname*), can not --makegroups".format(projectName)) # if you add a new font, set it to True here inside the if condition if args.complete: From ce6c161281029ad17b1ece139e7b37bada6b2d02 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Fri, 20 Jan 2023 16:18:34 +0100 Subject: [PATCH 2/4] font-patcher: Handle -l option in all metrics [why] The -l option tries to improve (especially) the powerline glyphs by making the baseline to baseline height (cell height) an even number. But it does so only for 2 of the three possible metrics. [how] Assuming the hight is identical for all metrics we just need to add '1' to all ascender values. [note] I'm not sure this does anything. After rounding an odd height might create a 'sharper' triangle tip, not an even height? Do not understand the real reason for the -l option. Signed-off-by: Fini Jastrow --- font-patcher | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/font-patcher b/font-patcher index 114ee4a6f..ed0d6ef7f 100755 --- a/font-patcher +++ b/font-patcher @@ -271,7 +271,7 @@ class font_patcher: self.assert_monospace() self.remove_ligatures() self.setup_patch_set() - self.setup_line_dimensions() + self.improve_line_dimensions() self.get_sourcefont_dimensions() self.sourceFont.encoding = 'UnicodeFull' # Update the font encoding to ensure that the Unicode glyphs are available self.onlybitmaps = self.sourceFont.onlybitmaps # Fetch this property before adding outlines. NOTE self.onlybitmaps initialized and never used @@ -902,23 +902,15 @@ class font_patcher: {'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleRules': None, 'Attributes': CUSTOM_ATTR} ] - def setup_line_dimensions(self): - # win_ascent and win_descent are used to set the line height for windows fonts. - # hhead_ascent and hhead_descent are used to set the line height for mac fonts. - # + def improve_line_dimensions(self): # Make the total line size even. This seems to make the powerline separators # center more evenly. if self.args.adjustLineHeight: if (self.sourceFont.os2_winascent + self.sourceFont.os2_windescent) % 2 != 0: + self.sourceFont.hhea_ascent += 1 + self.sourceFont.os2_typoascent += 1 self.sourceFont.os2_winascent += 1 - # Make the line size identical for windows and mac - # ! This is broken because hhea* is changed but os2_typo* is not - # ! On the other hand we need intact (i.e. original) typo values - # ! in get_sourcefont_dimensions() @TODO FIXME - self.sourceFont.hhea_ascent = self.sourceFont.os2_winascent - self.sourceFont.hhea_descent = -self.sourceFont.os2_windescent - def get_essential_references(self): """Find glyphs that are needed for the basic glyphs""" # Sometimes basic glyphs are constructed from multiple other glyphs. From 04c682fd9c8a4f4db6b78cc12ebaeebc749387b7 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Fri, 20 Jan 2023 16:29:40 +0100 Subject: [PATCH 3/4] font-patcher: Rewrite font height calculation [why] The initial font-patcher used the WIN font metrics to determine the cell height. What has been found was forced into HHEA metrics but without observing the USE_TYPO_METRICS flag. That has been changed to use the TYPO metric instead of the WIN metric when the font wants that. For that the gap value becomes important. This is the current code. It still has problems to detect the correct cell height. A more rigorous approach seem to be needed. [how] The baseline to baseline distance is what we need as 'cell height', to fill it completely with the powerline glyphs. This is a little bit complicated and not really specified, each font rendering application or engine can handle the font metrics differently. But there are some common approaches. So we try to come up with the correct and congruent height, comparing different metrics and issuing a warning on problematic fonts. Afterwards we make all metrics equal (even if they were not before), because our goal is clear now and we impose it onto all platforms. [note] Useful resources: * https://glyphsapp.com/learn/vertical-metrics * https://github.com/source-foundry/font-line Fixes: #1056 Signed-off-by: Fini Jastrow --- font-patcher | 110 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 31 deletions(-) diff --git a/font-patcher b/font-patcher index ed0d6ef7f..7e73088cf 100755 --- a/font-patcher +++ b/font-patcher @@ -6,7 +6,7 @@ from __future__ import absolute_import, print_function, unicode_literals # Change the script version when you edit this script: -script_version = "3.4.5" +script_version = "3.5.0" version = "2.3.0" projectName = "Nerd Fonts" @@ -271,8 +271,8 @@ class font_patcher: self.assert_monospace() self.remove_ligatures() self.setup_patch_set() - self.improve_line_dimensions() self.get_sourcefont_dimensions() + self.improve_line_dimensions() self.sourceFont.encoding = 'UnicodeFull' # Update the font encoding to ensure that the Unicode glyphs are available self.onlybitmaps = self.sourceFont.onlybitmaps # Fetch this property before adding outlines. NOTE self.onlybitmaps initialized and never used @@ -907,6 +907,7 @@ class font_patcher: # center more evenly. if self.args.adjustLineHeight: if (self.sourceFont.os2_winascent + self.sourceFont.os2_windescent) % 2 != 0: + # All three are equal before due to get_sourcefont_dimensions() self.sourceFont.hhea_ascent += 1 self.sourceFont.os2_typoascent += 1 self.sourceFont.os2_winascent += 1 @@ -924,18 +925,56 @@ class font_patcher: self.essential.add(self.sourceFont[r[0]].unicode) def get_sourcefont_dimensions(self): - # Initial font dimensions - self.font_dim = { - 'xmin' : 0, - 'ymin' : -self.sourceFont.os2_windescent, - 'xmax' : 0, - 'ymax' : self.sourceFont.os2_winascent, - 'width' : 0, - 'height': 0, - } - if self.sourceFont.os2_use_typo_metrics: - self.font_dim['ymin'] = self.sourceFont.os2_typodescent - self.font_dim['ymax'] = self.sourceFont.os2_typoascent + """ This gets the font dimensions (cell width and height), and makes them equal on all platforms """ + # Step 1 + # There are three ways to discribe the baseline to baseline distance + # (a.k.a. line spacing) of a font. That is all a kuddelmuddel + # and we try to sort this out here + # See also https://glyphsapp.com/learn/vertical-metrics + # See also https://github.com/source-foundry/font-line + hhea_height = self.sourceFont.hhea_ascent - self.sourceFont.hhea_descent + typo_height = self.sourceFont.os2_typoascent - self.sourceFont.os2_typodescent + win_height = self.sourceFont.os2_winascent + self.sourceFont.os2_windescent + win_gap = max(0, self.sourceFont.hhea_linegap - win_height + hhea_height) + hhea_btb = hhea_height + self.sourceFont.hhea_linegap + typo_btb = typo_height + self.sourceFont.os2_typolinegap + win_btb = win_height + win_gap + use_typo = self.sourceFont.os2_use_typo_metrics != 0 + + # We use either TYPO (1) or WIN (2) and compare with HHEA + # and use HHEA (0) if the fonts seems broken + our_btb = typo_btb if use_typo else win_btb + if our_btb == hhea_btb: + metrics = 1 if use_typo else 2 # conforming font + elif abs(our_btb - hhea_btb) / our_btb < 0.03: + print("{}: Font vertical metrics slightly off ({:.1f}%)".format(projectName, (our_btb - hhea_btb) / our_btb * 100.0)) + metrics = 1 if use_typo else 2 + else: + # Try the other metric + our_btb = typo_btb if not use_typo else win_btb + if our_btb == hhea_btb: + print("{}: Font vertical metrics probably wrong USE TYPO METRICS, assume opposite (i.e. {})".format(projectName, not use_typo)) + use_typo = not use_typo + self.sourceFont.os2_use_typo_metrics = 1 if use_typo else 0 + metrics = 1 if use_typo else 2 + else: + print("{}: WARNING Font vertical metrics inconsistent ({:.1f}%), using HHEA".format(projectName, (our_btb - hhea_btb) / our_btb * 100.0)) + our_btb = hhea_btb + metrics = 0 + + # print("FINI hhea {} typo {} win {} use {} {} {}".format(hhea_btb, typo_btb, win_btb, use_typo, our_btb != hhea_btb, self.sourceFont.fontname)) + + self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0} + + if metrics == 0: + self.font_dim['ymin'] = self.sourceFont.hhea_descent + half_gap(self.sourceFont.hhea_linegap, False) + self.font_dim['ymax'] = self.sourceFont.hhea_ascent + half_gap(self.sourceFont.hhea_linegap, True) + elif metrics == 1: + self.font_dim['ymin'] = self.sourceFont.os2_typodescent + half_gap(self.sourceFont.os2_typolinegap, False) + self.font_dim['ymax'] = self.sourceFont.os2_typoascent + half_gap(self.sourceFont.os2_typolinegap, True) + else: + self.font_dim['ymin'] = -self.sourceFont.os2_windescent + half_gap(win_gap, False) + self.font_dim['ymax'] = self.sourceFont.os2_winascent + half_gap(win_gap, True) # Calculate font height self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax'] @@ -950,26 +989,21 @@ class font_patcher: 'width' : self.sourceFont.em, 'height': self.sourceFont.descent + self.sourceFont.ascent, } + elif self.font_dim['height'] < 0: + sys.exit("{}: Can not detect sane font height".format(projectName)) - # Line gap add extra space on the bottom of the line which - # doesn't allow the powerline glyphs to fill the entire line. - # Put half of the gap into the 'cell', each top and bottom - gap = max(self.sourceFont.hhea_linegap, self.sourceFont.os2_typolinegap) # TODO probably wrong - if self.sourceFont.os2_use_typo_metrics: - gap = self.sourceFont.os2_typolinegap - self.sourceFont.hhea_linegap = 0 + # Make all metrics equal self.sourceFont.os2_typolinegap = 0 - if gap > 0: - gap_top = int(gap / 2) - gap_bottom = gap - gap_top - print("Redistributing line gap of {} ({} top and {} bottom)".format(gap, gap_top, gap_bottom)) - self.font_dim['ymin'] -= gap_bottom - self.font_dim['ymax'] += gap_top - self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax'] - self.sourceFont.os2_typoascent = self.sourceFont.os2_typoascent + gap_top - self.sourceFont.os2_typodescent = self.sourceFont.os2_typodescent - gap_bottom - # TODO Check what to do with win and hhea values + self.sourceFont.os2_typoascent = self.font_dim['ymax'] + self.sourceFont.os2_typodescent = self.font_dim['ymin'] + self.sourceFont.os2_winascent = self.sourceFont.os2_typoascent + self.sourceFont.os2_windescent = -self.sourceFont.os2_typodescent + self.sourceFont.hhea_ascent = self.sourceFont.os2_typoascent + self.sourceFont.hhea_descent = self.sourceFont.os2_typodescent + self.sourceFont.hhea_linegap = self.sourceFont.os2_typolinegap + self.sourceFont.os2_use_typo_metrics = 1 + # Step 2 # Find the biggest char width and advance width # 0x00-0x17f is the Latin Extended-A range warned = self.args.quiet or self.args.nonmono # Do not warn if quiet or proportional target @@ -1379,6 +1413,20 @@ class font_patcher: return None +def half_gap(gap, top): + """ Divides integer value into two new integers """ + # Line gap add extra space on the bottom of the line which + # doesn't allow the powerline glyphs to fill the entire line. + # Put half of the gap into the 'cell', each top and bottom + if gap <= 0: + return 0 + gap_top = int(gap / 2) + gap_bottom = gap - gap_top + if top: + print("Redistributing line gap of {} ({} top and {} bottom)".format(gap, gap_top, gap_bottom)) + return gap_top + return gap_bottom + def replace_font_name(font_name, replacement_dict): """ Replaces all keys with vals from replacement_dict in font_name. """ for key, val in replacement_dict.items(): From 621008773c1864002a0c178715c062b4b3c9d36b Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Sun, 22 Jan 2023 17:03:07 +0100 Subject: [PATCH 4/4] font-patcher: Use WIN metrics in all conflicting cases [why] When HHEA and (depending on USE-TYPO-METRIC) TYPO or WIN are not consistent it is unclear which metric we should trust. In #1056 the complete font bounding box (i.e. yMin and yMax) has been compared to the baseline to baseline distances, and in all these cases the WIN values seem to be best (preserve the glyph bounding box). font-line report fontname.ttf | grep metrics: ttfdump -t head fontname.ttf | grep "yM(in|ax)" [note] Roboto will still be clipped?! There seem to be ridiculously high glyphs in there. Did not check which. Signed-off-by: Fini Jastrow --- font-patcher | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/font-patcher b/font-patcher index 7e73088cf..91d0ccd7e 100755 --- a/font-patcher +++ b/font-patcher @@ -946,21 +946,11 @@ class font_patcher: our_btb = typo_btb if use_typo else win_btb if our_btb == hhea_btb: metrics = 1 if use_typo else 2 # conforming font - elif abs(our_btb - hhea_btb) / our_btb < 0.03: - print("{}: Font vertical metrics slightly off ({:.1f}%)".format(projectName, (our_btb - hhea_btb) / our_btb * 100.0)) - metrics = 1 if use_typo else 2 else: - # Try the other metric - our_btb = typo_btb if not use_typo else win_btb - if our_btb == hhea_btb: - print("{}: Font vertical metrics probably wrong USE TYPO METRICS, assume opposite (i.e. {})".format(projectName, not use_typo)) - use_typo = not use_typo - self.sourceFont.os2_use_typo_metrics = 1 if use_typo else 0 - metrics = 1 if use_typo else 2 - else: - print("{}: WARNING Font vertical metrics inconsistent ({:.1f}%), using HHEA".format(projectName, (our_btb - hhea_btb) / our_btb * 100.0)) - our_btb = hhea_btb - metrics = 0 + # We trust the WIN metric more, see experiments in #1056 + print("{}: WARNING Font vertical metrics inconsistent (HHEA {} / TYPO {} / WIN {}), using WIN".format(projectName, hhea_btb, typo_btb, win_btb)) + our_btb = win_btb + metrics = 1 # print("FINI hhea {} typo {} win {} use {} {} {}".format(hhea_btb, typo_btb, win_btb, use_typo, our_btb != hhea_btb, self.sourceFont.fontname))