diff --git a/bin/scripts/name_parser/FontnameParser.py b/bin/scripts/name_parser/FontnameParser.py index 2c5aff4..fbfc7ef 100644 --- a/bin/scripts/name_parser/FontnameParser.py +++ b/bin/scripts/name_parser/FontnameParser.py @@ -271,12 +271,34 @@ class FontnameParser: self.logger.error('====-< {:18} too long ({:2} > {:2}): {}'.format(entry_id, len(name), max_len, name)) return name + def check_weights(self, font): + """ Check weight metadata for consistency """ + # Some weights are hidden in styles + ignore_token = list(FontnameTools.known_widths) + list(FontnameTools.known_slopes) + ignore_token += [ m + s + for s in list(FontnameTools.known_widths) + for m in list(FontnameTools.known_modifiers) ] + restored_weight_token = [ w for w in self.style_token + self.weight_token if w not in ignore_token ] + weight = ''.join(restored_weight_token) + os2_weight = font.os2_weight + ps_weight = FontnameTools.weight_string_to_number(font.weight) + name_weight = FontnameTools.weight_string_to_number(weight) + if name_weight is None: + self.logger.error('Can not parse name for weight: {}'.format(restored_weight_token)) + return + if abs(os2_weight - ps_weight) > 50 or abs(os2_weight - name_weight) > 50: + self.logger.warning('Possible problem with the weight metadata detected, check with --debug') + self.logger.debug('Weight approximations: OS2/PS/Name: {}/{}/{} (from {}/\'{}\'/\'{}\')'.format( + os2_weight, ps_weight, name_weight, + font.os2_weight, font.weight, weight)) + def rename_font(self, font): """Rename the font to include all information we found (font is fontforge font object)""" font.fondname = None font.fontname = self.psname() font.fullname = self.fullname() font.familyname = self.ps_familyname() + self.check_weights(font) # We have to work around several issues in fontforge: # diff --git a/bin/scripts/name_parser/FontnameTools.py b/bin/scripts/name_parser/FontnameTools.py index 0e664f6..52825b7 100644 --- a/bin/scripts/name_parser/FontnameTools.py +++ b/bin/scripts/name_parser/FontnameTools.py @@ -175,7 +175,7 @@ class FontnameTools: """Filter out characters that are not allowed in Postscript names""" # The name string must be restricted to the printable ASCII subset, codes 33 to 126, # except for the 10 characters '[', ']', '(', ')', '{', '}', '<', '>', '/', '%' - out = "" + out = '' for c in name: if c in '[](){}<>/%' or ord(c) < 33 or ord(c) > 126: continue @@ -247,6 +247,9 @@ class FontnameTools: 'Light': ('Lt', 'Light'), ' ': (), # Just for CodeClimate :-/ } + known_styles = [ # Keywords that end up as style (i.e. a RIBBI set) + 'Bold', 'Italic', 'Regular', 'Normal' + ] known_widths = { # can take modifiers 'Compressed': ('Cm', 'Comp'), 'Extended': ('Ex', 'Extd'), @@ -268,6 +271,51 @@ class FontnameTools: 'Semi': ('Sm', 'Sem'), 'Extra': ('X', 'Ext'), } + equivalent_weights = { + 100: ('thin', 'hairline'), + 200: ('extralight', 'ultralight'), + 300: ('light', ), + 350: ('semilight', ), + 400: ('regular', 'normal', 'book', 'text', 'nord', 'retina'), + 500: ('medium', ), + 600: ('semibold', 'demibold', 'demi'), + 700: ('bold', ), + 800: ('extrabold', 'ultrabold'), + 900: ('black', 'heavy', 'poster', 'extrablack', 'ultrablack'), + } + + @staticmethod + def weight_string_to_number(w): + """ Convert a common string approximation to a PS/2 weight value """ + if not len(w): + return 400 + for num, strs in FontnameTools.equivalent_weights.items(): + if w.lower() in strs: + return num + return None + + @staticmethod + def weight_to_string(w): + """ Convert a PS/2 weight value to the common string approximation """ + if w < 150: + str = 'Thin' + elif w < 250: + str = 'Extra-Light' + elif w < 350: + str = 'Light' + elif w < 450: + str = 'Regular' + elif w < 550: + str = 'Medium' + elif w < 650: + str = 'Semi-Bold' + elif w < 750: + str = 'Bold' + elif w < 850: + str = 'Extra-Bold' + else: + str = 'Black' + return str @staticmethod def is_keep_regular(basename): @@ -342,8 +390,7 @@ class FontnameTools: for s in list(FontnameTools.known_weights2) + list(FontnameTools.known_widths) for m in list(FontnameTools.known_modifiers) + [''] if m != s ] + list(FontnameTools.known_weights1) + list(FontnameTools.known_slopes) - styles = [ 'Bold', 'Italic', 'Regular', 'Normal', ] - weights = [ w for w in weights if w not in styles ] + weights = [ w for w in weights if w not in FontnameTools.known_styles ] # Some font specialities: other = [ '-', 'Book', 'For', 'Powerline', @@ -355,7 +402,7 @@ class FontnameTools: ] ( style, weight_token ) = FontnameTools.get_name_token(style, weights) - ( style, style_token ) = FontnameTools.get_name_token(style, styles) + ( style, style_token ) = FontnameTools.get_name_token(style, FontnameTools.known_styles) ( style, other_token ) = FontnameTools.get_name_token(style, other) while 'Regular' in style_token and len(style_token) > 1: # Correct situation where "Regular" and something else is given diff --git a/font-patcher b/font-patcher index de29483..5efec62 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.5.1" +script_version = "4.5.2" version = "3.0.2" projectName = "Nerd Fonts" @@ -444,6 +444,9 @@ class font_patcher: # Adjust flags that can not be changed via fontforge if re.search('\\.[ot]tf$', self.args.font, re.IGNORECASE) and re.search('\\.[ot]tf$', outfile, re.IGNORECASE): + if not os.path.isfile(outfile) or os.path.getsize(outfile) < 1: + logger.critical("Something went wrong and Fontforge did not generate the new font - look for messages above") + sys.exit(1) try: source_font = TableHEADWriter(self.args.font) dest_font = TableHEADWriter(outfile) @@ -1144,7 +1147,7 @@ class font_patcher: if our_btb == hhea_btb: metrics = Metric.TYPO if use_typo else Metric.WIN # conforming font elif abs(our_btb - hhea_btb) / our_btb < 0.03: - logger.info("Font vertical metrics slightly off (%.1f%)", (our_btb - hhea_btb) / our_btb * 100.0) + logger.info("Font vertical metrics slightly off (%.1f)", (our_btb - hhea_btb) / our_btb * 100.0) metrics = Metric.TYPO if use_typo else Metric.WIN else: # Try the other metric