From d3ee35db8e086616cc5b08c34f907a9017ee944a Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Sat, 11 Nov 2023 03:37:43 +0100 Subject: [PATCH 1/2] name-parser: Unify abbreviations in family names [why] Because the newer Preferred/Typographic names ID 16 and ID 17 have not a very demanding length limit we add the long form of the name addendum (i.e. Nerd Font, Nerd Font Mono, Nerd Font Propo). In the more restricted old names ID 1 and ID 2 we use the short forms (i.e. NF, NFM, NFP). This seems to be problematic with Visual Studio (Community) 2022 and the fonts can be selected but are not really used. The Postscript family name is never shortened which seems to be of no consequence, but still is different than the other. [how] When creating the Preferred/Typographic Family (ID 16) we check the shortening mode first and abbreviate the parts as needed and alike ID 1. This will also change the filenames, because they base on the SFNT table. We can not change that without changing the whole mechanism. [note] Also add new tool that lists all names of fonts, including the Postscript ones. Fixes: #1242 Signed-off-by: Fini Jastrow --- bin/scripts/name_parser/FontnameParser.py | 7 ++- bin/scripts/name_parser/query_name | 60 +++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100755 bin/scripts/name_parser/query_name diff --git a/bin/scripts/name_parser/FontnameParser.py b/bin/scripts/name_parser/FontnameParser.py index 5768c42b1..02424cc33 100644 --- a/bin/scripts/name_parser/FontnameParser.py +++ b/bin/scripts/name_parser/FontnameParser.py @@ -183,7 +183,12 @@ class FontnameParser: def preferred_family(self): """Get the SFNT Preferred Familyname (ID 16)""" (name, rest) = self._shortened_name() - pfn = FontnameTools.concat(name, rest, self.other_token, self.family_suff) + other = self.other_token + weights = self.weight_token + aggressive = self.use_short_families[2] + if self.use_short_families[1]: + [ other, weights ] = FontnameTools.short_styles([ other, weights ], aggressive) + pfn = FontnameTools.concat(name, rest, other, self.short_family_suff) if self.suppress_preferred_if_identical and pfn == self.family(): # Do not set if identical to ID 1 return '' diff --git a/bin/scripts/name_parser/query_name b/bin/scripts/name_parser/query_name new file mode 100755 index 000000000..10a50b144 --- /dev/null +++ b/bin/scripts/name_parser/query_name @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# coding=utf8 +# +# Usually called via +# $ fontforge query_name fontfile.tff 2>/dev/null + +import sys +import os.path +import fontforge + +###### Some helpers + +def get_sfnt_dict(font): + """Extract SFNT table as nice dict""" + return { k: v for l, k, v in font.sfnt_names } + +###### Let's go! + +if len(sys.argv) < 2: + print('Usage: {} font_name [font_name ...]\n'.format(sys.argv[0])) + sys.exit(1) + +print('Examining {} font files'.format(len(sys.argv) - 1)) +add_line = False + +for filename in sys.argv[1:]: + fullfile = os.path.basename(filename) + fname = os.path.splitext(fullfile)[0] + + font = fontforge.open(filename, 1) + sfnt = get_sfnt_dict(font) + psname = font.fontname + aname = font.fondname + full = font.fullname + fam = font.familyname + font.close() + + sfnt_full = sfnt['Fullname'] + sfnt_fam = sfnt['Family'] + sfnt_subfam = sfnt['SubFamily'] + sfnt_pfam = sfnt['Preferred Family'] if 'Preferred Family' in sfnt else '' + sfnt_psubfam = sfnt['Preferred Styles'] if 'Preferred Styles' in sfnt else '' + sfnt_psname = sfnt['PostScriptName'] if 'PostScriptName' in sfnt else '' + + if add_line: + print() + else: + add_line = True + + print('======== {} ========'.format(fname)) + print('SFNT Fullname ID 4 {}'.format(sfnt_full)) + print('SFNT Family ID 1 {}'.format(sfnt_fam)) + print('SFNT SubFamily ID 2 {}'.format(sfnt_subfam)) + print('SFNT Pref Family ID 16 {}'.format(sfnt_pfam)) + print('SFNT Pref Styles ID 17 {}'.format(sfnt_psubfam)) + print('SFNT PS Name ID 6 {}'.format(sfnt_psname)) + print('PS fontname {}'.format(psname)) + print('PS fullname {}'.format(full)) + print('PS familyname {}'.format(fam)) + print('fondname {}'.format(aname)) From 784e8925754ef5012b8e64aacde9df11e68f0a21 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Sat, 11 Nov 2023 04:23:30 +0100 Subject: [PATCH 2/2] name-parser: Revert file name change [why] It is better to have a not-abbreviated file name so that one can make sense out of the name parts, especially when doing a partial patch. With the previous commit we ended up with all abbreviated names. The filename length is hopefully not limited, at least not as severe as the SFNT table entries. [how] We need to store the answers somewhere because the naming is only understood by the FontnameParser object which we throw away soon. As fallback we still can parse the SFNT table, for example when the old renaming is used. Signed-off-by: Fini Jastrow --- bin/scripts/name_parser/FontnameParser.py | 9 ++++++++ font-patcher | 28 +++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/bin/scripts/name_parser/FontnameParser.py b/bin/scripts/name_parser/FontnameParser.py index 02424cc33..f4b64aabb 100644 --- a/bin/scripts/name_parser/FontnameParser.py +++ b/bin/scripts/name_parser/FontnameParser.py @@ -180,6 +180,15 @@ class FontnameParser: sub = FontnameTools.postscript_char_filter(sub) return self._make_ps_name(fam + sub, False) + def long_family(self): + """Get unabbreviated Familyname""" + (name, rest) = self._shortened_name() + return FontnameTools.concat(name, rest, self.other_token, self.family_suff) + + def long_subfamily(self): + """Get unabbreviated Styles""" + return FontnameTools.concat(self.weight_token, self.style_token) + def preferred_family(self): """Get the SFNT Preferred Familyname (ID 16)""" (name, rest) = self._shortened_name() diff --git a/font-patcher b/font-patcher index 7de865011..27032a68b 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 = "4.7.0" +script_version = "4.7.1" version = "3.0.2" projectName = "Nerd Fonts" @@ -306,16 +306,20 @@ def get_old_average_x_width(font): s += font[g].width * weights[g] return int(s / 1000) -def create_filename(fonts): +def create_filename(patcher, fonts): """ Determine filename from font object(s) """ + pfam = patcher.long_family + psubfam = patcher.long_subfamily sfnt = { k: v for l, k, v in fonts[0].sfnt_names } - sfnt_pfam = sfnt.get('Preferred Family', sfnt['Family']) - sfnt_psubfam = sfnt.get('Preferred Styles', sfnt['SubFamily']) + if not pfam: + pfam = sfnt.get('Preferred Family', sfnt['Family']) + if not psubfam or len(psubfam) < 1: + psubfam = sfnt.get('Preferred Styles', sfnt['SubFamily']) if len(fonts) > 1: - return sfnt_pfam - if len(sfnt_psubfam) > 0: - sfnt_psubfam = '-' + sfnt_psubfam - return (sfnt_pfam + sfnt_psubfam).replace(' ', '') + return pfam.replace(' ', '') + if len(psubfam) > 0: + psubfam = '-' + psubfam + return (pfam + psubfam).replace(' ', '') class font_patcher: @@ -333,6 +337,8 @@ class font_patcher: self.essential = set() self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True) self.xavgwidth = [] # list of ints + self.long_family = None + self.long_subfamily = None def patch(self, font): self.sourceFont = font @@ -426,11 +432,11 @@ class font_patcher: break outfile = os.path.normpath(os.path.join( sanitize_filename(self.args.outputdir, True), - sanitize_filename(create_filename(sourceFonts)) + ".ttc")) + sanitize_filename(create_filename(self, sourceFonts)) + ".ttc")) sourceFonts[0].generateTtc(outfile, sourceFonts[1:], flags=gen_flags, layer=layer) message = " Generated {} fonts\n \===> '{}'".format(len(sourceFonts), outfile) else: - fontname = create_filename(sourceFonts) + fontname = create_filename(self, sourceFonts) if not fontname: fontname = sourceFont.cidfontname outfile = os.path.normpath(os.path.join( @@ -749,6 +755,8 @@ class font_patcher: # inject_suffix(family, ps_fontname, short_family) n.inject_suffix(verboseAdditionalFontNameSuffix, ps_suffix, short_family) n.rename_font(font) + self.long_family = n.long_family() + self.long_subfamily = n.long_subfamily() font.comment = projectInfo font.fontlog = projectInfo