1
0
mirror of https://github.com/ryanoasis/nerd-fonts.git synced 2024-12-25 20:18:01 +02:00

font-patcher: Introduce less severe family name shortening

[why]
We want to keep "Nerd Font" in the font name if possible and instead
shorten the weight part with accepted abbreviations. But these abbrevs
are hard to read and sometimes a more mild abbreviating might be
sufficient to get the desired name length.

[how]
Introduce a new shortening method for the weight parts of a family name.
It takes a longer word (often un-shortened) when a weight stands on its
own, but when a modifier is used together with the weight the more
aggressive two-letter abbreviations are used.

That new shortening method becomes the default and all the functions get
a new parameter to enforce completely aggressive shortening, i.e. always
use the shortest possible form.

The new way to shorten is exposed all the way out to the font-patcher
user who can select the shortening method as parameter to the
--makegroups option. That option is undocumented because I expect some
changes later on, still.

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
This commit is contained in:
Fini Jastrow 2023-04-19 15:57:50 +02:00
parent ff2be6af81
commit 9be4835c29
3 changed files with 74 additions and 55 deletions

View File

@ -10,7 +10,7 @@ class FontnameParser:
def __init__(self, filename, logger):
"""Parse a font filename and store the results"""
self.parse_ok = False
self.use_short_families = (False, False) # ( camelcase name, short styles )
self.use_short_families = (False, False, False) # ( camelcase name, short styles, aggressive )
self.keep_regular_in_family = None # None = auto, True, False
self.suppress_preferred_if_identical = True
self.family_suff = ''
@ -65,13 +65,13 @@ class FontnameParser:
self.short_family_suff = short_family.strip()
return self
def enable_short_families(self, camelcase_name, prefix):
def enable_short_families(self, camelcase_name, prefix, aggressive):
"""Enable short styles in Family when (original) font name starts with prefix; enable CamelCase basename in (Typog.) Family"""
# camelcase_name is boolean
# prefix is either a string or False/True
if isinstance(prefix, str):
prefix = self._basename.startswith(prefix)
self.use_short_families = ( camelcase_name, prefix )
self.use_short_families = ( camelcase_name, prefix, aggressive )
return self
def add_name_substitution_table(self, table):
@ -145,15 +145,15 @@ class FontnameParser:
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
(name, rest) = self._shortened_name()
if self.use_short_families[1]:
[ weights, styles ] = FontnameTools.short_styles([ weights, styles ])
[ weights, styles ] = FontnameTools.short_styles([ weights, styles ], self.use_short_families[2])
return FontnameTools.concat(name, rest, self.other_token, self.short_family_suff, weights, styles)
def psname(self):
"""Get the SFNT PostScriptName (ID 6)"""
# This is almost self.family() + '-' + self.subfamily()
(name, rest) = self._shortened_name()
styles = FontnameTools.short_styles(self.style_token)
weights = FontnameTools.short_styles(self.weight_token)
styles = FontnameTools.short_styles(self.style_token, self.use_short_families[2])
weights = FontnameTools.short_styles(self.weight_token, self.use_short_families[2])
fam = FontnameTools.camel_casify(FontnameTools.concat(name, rest, self.other_token, self.ps_fontname_suff))
sub = FontnameTools.camel_casify(FontnameTools.concat(weights, styles))
if len(sub) > 0:
@ -190,7 +190,7 @@ class FontnameParser:
other = self.other_token
weight = self.weight_token
if self.use_short_families[1]:
[ other, weight ] = FontnameTools.short_styles([ other, weight ])
[ other, weight ] = FontnameTools.short_styles([ other, weight ], self.use_short_families[2])
return FontnameTools.concat(name, rest, other, self.short_family_suff, weight)
def subfamily(self):

View File

@ -93,31 +93,38 @@ class FontnameTools:
return None
@staticmethod
def shorten_style_name(name):
def shorten_style_name(name, aggressive):
"""Substitude some known styles to short form"""
# If aggressive is False create the mild short form
# aggressive == True: Always use first form of everything
# aggressive == False:
# - has no modifier: use the second form
# - has modifier: use second form of mod plus first form of main
name_rest = name
name_pre = ''
form = 0 if aggressive else 1
for mod in FontnameTools.known_modifiers:
if name.startswith(mod) and len(name) > len(mod): # Second condition specifically for 'Demi'
name_pre = FontnameTools.known_modifiers[mod]
name_pre = FontnameTools.known_modifiers[mod][form]
name_rest = name[len(mod):]
break
form = 0 if aggressive or len(name_pre) else 1
subst = FontnameTools.find_in_dicts(name_rest, [ FontnameTools.known_weights2, FontnameTools.known_widths ])
if isinstance(subst, str):
return name_pre + subst
if isinstance(subst, tuple):
return name_pre + subst[form]
if not len(name_pre):
# The following sets do not allow modifiers
subst = FontnameTools.find_in_dicts(name_rest, [ FontnameTools.known_weights1, FontnameTools.known_slopes ])
if isinstance(subst, str):
return subst
if isinstance(subst, tuple):
return subst[form]
return name
@staticmethod
def short_styles(lists):
def short_styles(lists, aggressive):
"""Shorten all style names in a list or a list of lists"""
if not len(lists) or not isinstance(lists[0], list):
return list(map(FontnameTools.shorten_style_name, lists))
return [ list(map(FontnameTools.shorten_style_name, styles)) for styles in lists ]
return list(map(lambda x: FontnameTools.shorten_style_name(x, aggressive), lists))
return [ list(map(lambda x: FontnameTools.shorten_style_name(x, aggressive), styles)) for styles in lists ]
@staticmethod
def make_oblique_style(weights, styles):
@ -198,44 +205,52 @@ class FontnameTools:
]
# From https://adobe-type-tools.github.io/font-tech-notes/pdfs/5088.FontNames.pdf
# The first short variant is from the linked table.
# The second (longer) short variant is from diverse fonts like Noto.
# We can
# - use the long form
# - use the very short form (first)
# - use mild short form:
# - has no modifier: use the second form
# - has modifier: use second form of mod plus first form of main
known_weights1 = { # can not take modifiers
'Medium': 'Md',
'Nord': 'Nd',
'Book': 'Bk',
'Poster': 'Po',
'Demi': 'Dm', # Demi is sometimes used as a weight, sometimes as a modifier
'Regular': 'Rg',
'Display': 'DS',
'Super': 'Su',
'Retina': 'Rt',
'Medium': ('Md', 'Med'),
'Nord': ('Nd', 'Nord'),
'Book': ('Bk', 'Book'),
'Poster': ('Po', 'Poster'),
'Demi': ('Dm', 'Demi'), # Demi is sometimes used as a weight, sometimes as a modifier
'Regular': ('Rg', 'Reg'),
'Display': ('DS', 'Disp'),
'Super': ('Su', 'Sup'),
'Retina': ('Rt', 'Ret'),
}
known_weights2 = { # can take modifiers
'Black': 'Blk',
'Bold': 'Bd',
'Heavy': 'Hv',
'Thin': 'Th',
'Light': 'Lt',
'Black': ('Blk', 'Black'),
'Bold': ('Bd', 'Bold'),
'Heavy': ('Hv', 'Heavy'),
'Thin': ('Th', 'Thin'),
'Light': ('Lt', 'Light'),
}
known_widths = { # can take modifiers
'Compressed': 'Cm',
'Extended': 'Ex',
'Condensed': 'Cn',
'Narrow': 'Nr',
'Compact': 'Ct',
'Compressed': ('Cm', 'Comp'),
'Extended': ('Ex', 'Extd'),
'Condensed': ('Cn', 'Cond'),
'Narrow': ('Nr', 'Narrow'),
'Compact': ('Ct', 'Compact'),
}
known_slopes = {
'Inclined': 'Ic',
'Oblique': 'Obl',
'Italic': 'It',
'Upright': 'Up',
'Kursiv': 'Ks',
'Sloped': 'Sl',
'Inclined': ('Ic', 'Incl'),
'Oblique': ('Obl', 'Obl'),
'Italic': ('It', 'Italic'),
'Upright': ('Up', 'Uprght'),
'Kursiv': ('Ks', 'Kurs'),
'Sloped': ('Sl', 'Slop'),
}
known_modifiers = {
'Demi': 'Dm',
'Ultra': 'Ult',
'Semi': 'Sm',
'Extra': 'X',
'Demi': ('Dm', 'Dem'),
'Ultra': ('Ult', 'Ult'),
'Semi': ('Sm', 'Sem'),
'Extra': ('X', 'Ext'),
}
@staticmethod

View File

@ -569,7 +569,7 @@ class font_patcher:
if not n.parse_ok:
logger.warning("Have only minimal naming information, check resulting name. Maybe omit --makegroups option")
n.drop_for_powerline()
n.enable_short_families(True, self.args.makegroups in [ 1, 3])
n.enable_short_families(True, self.args.makegroups in [ 2, 3, 5, 6, ], self.args.makegroups in [ 3, 6, ])
# All the following stuff is ignored in makegroups-mode
@ -723,7 +723,7 @@ class font_patcher:
font.appendSFNTName(str('English (US)'), str('Compatible Full'), font.fullname)
font.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
else:
short_family = projectNameAbbreviation + variant_abbrev if self.args.makegroups in [ 2, 3] else projectNameSingular + variant_full
short_family = projectNameAbbreviation + variant_abbrev if self.args.makegroups >= 4 else projectNameSingular + variant_full
# inject_suffix(family, ps_fontname, short_family)
n.inject_suffix(verboseAdditionalFontNameSuffix, ps_suffix, short_family)
n.rename_font(font)
@ -1805,14 +1805,18 @@ def setup_arguments():
parser.add_argument('-ext', '--extension', dest='extension', default="", type=str, nargs='?', help='Change font file type to create (e.g., ttf, otf)')
parser.add_argument('-out', '--outputdir', dest='outputdir', default=".", type=str, nargs='?', help='The directory to output the patched font file to')
parser.add_argument('--glyphdir', dest='glyphdir', default=__dir__ + "/src/glyphs/", type=str, nargs='?', help='Path to glyphs to be used for patching')
parser.add_argument('--makegroups', dest='makegroups', default=0, type=int, nargs='?', help='Use alternative method to name patched fonts (recommended)', const=3, choices=range(0, 4 + 1))
# --makegroup has an additional undocumented numeric specifier. '--makegroup' is in fact '--makegroup 3'.
# Possible values with examples:
# 0 - turned off, use old naming scheme
# 1 - turned on, shortening 'Bold' to 'Bd'
# 2 - turned on, shortening 'Nerd Font' to 'NF'
# 3 - turned on, shortening 'Nerd Font' to 'NF' and 'Bold' to 'Bd'
# 4 - turned on, no shortening
parser.add_argument('--makegroups', dest='makegroups', default=0, type=int, nargs='?', help='Use alternative method to name patched fonts (recommended)', const=1, choices=range(0, 6 + 1))
# --makegroup has an additional undocumented numeric specifier. '--makegroup' is in fact '--makegroup 1'.
# Original font name: Hugo Sans Mono ExtraCondensed Light Italic
# NF Fam agg.
# 0 turned off, use old naming scheme [-] [-] [-]
# 1 HugoSansMono Nerd Font ExtraCondensed Light Italic [ ] [ ] [ ]
# 2 HugoSansMono Nerd Font ExtCn Light Italic [ ] [X] [ ]
# 3 HugoSansMono Nerd Font XCn Lt It [ ] [X] [X]
# 4 HugoSansMono NF ExtraCondensed Light Italic [X] [ ] [ ]
# 5 HugoSansMono NF ExtCn Light Italic [X] [X] [ ]
# 6 HugoSansMono NF XCn Lt It [X] [X] [X]
parser.add_argument('--variable-width-glyphs', dest='nonmono', default=False, action='store_true', help='Do not adjust advance width (no "overhang")')
# progress bar arguments - https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse