mirror of
https://github.com/ryanoasis/nerd-fonts.git
synced 2024-09-19 09:51:48 +02:00
Draft: Introduce a file name parser
DO NOT MERGE [why] A lot of the fonts have incorrect naming after patching. A completely different approach can help to come up with a consistent naming scheme. [how] See bin/scripts/name-parser/README.md Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
This commit is contained in:
parent
d939fd4f56
commit
6d86114a38
14 changed files with 3827 additions and 8 deletions
313
bin/scripts/name_parser/FontnameParser.py
Normal file
313
bin/scripts/name_parser/FontnameParser.py
Normal file
|
@ -0,0 +1,313 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import re
|
||||
from FontnameTools import FontnameTools
|
||||
|
||||
class FontnameParser:
|
||||
"""Parse a font name and generate all kinds of names"""
|
||||
|
||||
def __init__(self, filename):
|
||||
"""Parse a font filename and store the results"""
|
||||
self.parse_ok = False
|
||||
self.for_windows = False
|
||||
self.use_short_families = (False, False) # ( camelcase name, short styles )
|
||||
self.keep_regular_in_family = None # None = auto, True, False
|
||||
self.suppress_preferred_if_identical = True
|
||||
self.fullname_suff = ''
|
||||
self.fontname_suff = ''
|
||||
self.family_suff = ''
|
||||
self.name_subst = []
|
||||
[ self.parse_ok, self._basename, self.weight_token, self.style_token, self.other_token, self._rest ] = FontnameTools.parse_font_name(filename)
|
||||
self.basename = self._basename
|
||||
self.rest = self._rest
|
||||
self.add_name_substitution_table(FontnameTools.SIL_TABLE)
|
||||
|
||||
def _make_ps_mame(self, n):
|
||||
"""Helper to limit font name length in PS names"""
|
||||
if self.for_windows and len(n) > 31:
|
||||
print('Shortening too long PS family name')
|
||||
return n[:31]
|
||||
return n
|
||||
|
||||
def _shortened_name(self):
|
||||
"""Return a blank free basename-rest combination"""
|
||||
if not self.use_short_families[0]:
|
||||
return (self.basename, self.rest)
|
||||
else:
|
||||
return (FontnameTools.concat(self.basename, self.rest).replace(' ', ''), '')
|
||||
|
||||
def set_for_windows(self, for_windows):
|
||||
"""Create slightly different names, suitable for Windows use"""
|
||||
self.for_windows = for_windows
|
||||
return self
|
||||
|
||||
def set_keep_regular_in_family(self, keep):
|
||||
"""Familyname may contain 'Regular' where it should normally be suppressed"""
|
||||
self.keep_regular_in_family = keep
|
||||
|
||||
def set_suppress_preferred(self, suppress):
|
||||
"""Suppress ID16/17 if it is identical to ID1/2 (True is default)"""
|
||||
self.suppress_preferred_if_identical = suppress
|
||||
|
||||
def inject_suffix(self, fullname, fontname, family):
|
||||
"""Add a custom additonal string that shows up in the resulting names"""
|
||||
self.fullname_suff = fullname.strip()
|
||||
self.fontname_suff = fontname.replace(' ', '')
|
||||
self.family_suff = family.strip()
|
||||
return self
|
||||
# font-patcher behavior:
|
||||
# verboseSuff = "Nerd Font"
|
||||
# shortSuff = win ? "NF" : "Nerd Font"
|
||||
# verboseSuff += "Plus Font Awesome"
|
||||
# shortSuff += "A"
|
||||
# OR when complete:
|
||||
# shortSuff = "Nerd Font Complete"
|
||||
# verboseSuff = "Nerd Font Complete"
|
||||
# AND
|
||||
# shortSuff += "M"
|
||||
# verboseSuff += "Mono"
|
||||
#
|
||||
# fullname += verboseSuff
|
||||
# fontname += shortSuff
|
||||
# if win familyname += "NF"
|
||||
# else familyname += "Nerd Font"
|
||||
# if win fullname += "Windows Compatible"
|
||||
# if !win familyname += "Mono"
|
||||
#
|
||||
# THUS:
|
||||
# fontname => shortSuff
|
||||
# fullname => verboseSuff {{ we do the following already: }} + win ? "Windows Compatible" : ""
|
||||
# family => win ? "NF" : "Nerd Font" + mono ? "Mono" : ""
|
||||
|
||||
def enable_short_families(self, camelcase_name, prefix):
|
||||
"""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
|
||||
if type(prefix) == str:
|
||||
prefix = self._basename.startswith(prefix)
|
||||
self.use_short_families = ( camelcase_name, prefix )
|
||||
return self
|
||||
|
||||
def add_name_substitution_table(self, table):
|
||||
"""Have some fonts renamed, takes list of tuples (regex, replacement)"""
|
||||
# The regex will be anchored to name begin and used case insensitive
|
||||
# Replacement can have regex matches, mind to catch the correct source case
|
||||
self.name_subst = table
|
||||
self.basename = self._basename
|
||||
self.rest = self._rest
|
||||
base_and_rest = self._basename + (' ' + self._rest if len(self._rest) else '')
|
||||
for regex, replacement in self.name_subst:
|
||||
m = re.match(regex, base_and_rest, re.IGNORECASE)
|
||||
if not m:
|
||||
continue
|
||||
i = len(self._basename) - len(m.group(0))
|
||||
if i < 0:
|
||||
self.basename = m.expand(replacement)
|
||||
self.rest = self._rest[-(i+1):].lstrip()
|
||||
else:
|
||||
self.basename = m.expand(replacement) + self._basename[len(m.group(0)):]
|
||||
break
|
||||
return self
|
||||
|
||||
def drop_for_powerline(self):
|
||||
"""Remove 'for Powerline' from all names (can not be undone)"""
|
||||
if 'Powerline' in self.other_token:
|
||||
idx = self.other_token.index('Powerline')
|
||||
self.other_token.pop(idx)
|
||||
if idx > 0 and self.other_token[idx - 1] == 'For':
|
||||
self.other_token.pop(idx - 1)
|
||||
self._basename = re.sub(r'(\b|for\s?)?powerline\b', '', self._basename, 1, re.IGNORECASE).strip()
|
||||
self.add_name_substitution_table(self.name_subst) # re-evaluate
|
||||
return self
|
||||
|
||||
### Following the creation of the name parts:
|
||||
#
|
||||
# Relevant websites
|
||||
# https://www.fonttutorials.com/how-to-name-font-family/
|
||||
# https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids
|
||||
# https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fss
|
||||
# https://docs.microsoft.com/en-us/typography/opentype/spec/head#macstyle
|
||||
|
||||
# Example (mind that they group 'semibold' as classic-group-of-4 Bold, while we will always only take bold as Bold):
|
||||
# Adobe Caslon Pro Regular ID1: Adobe Caslon Pro ID2: Regular
|
||||
# Adobe Caslon Pro Italic ID1: Adobe Caslon Pro ID2: Italic
|
||||
# Adobe Caslon Pro Semibold ID1: Adobe Caslon Pro ID2: Bold ID16: Adobe Caslon Pro ID17: Semibold
|
||||
# Adobe Caslon Pro Semibold Italic ID1: Adobe Caslon Pro ID2: Bold Italic ID16: Adobe Caslon Pro ID17: Semibold Italic
|
||||
# Adobe Caslon Pro Bold ID1: Adobe Caslon Pro Bold ID2: Regular ID16: Adobe Caslon Pro ID17: Bold
|
||||
# Adobe Caslon Pro Bold Italic ID1: Adobe Caslon Pro Bold ID2: Italic ID16: Adobe Caslon Pro ID17: Bold Italic
|
||||
|
||||
# fontname === preferred_family + preferred_styles
|
||||
# fontname === family + subfamily
|
||||
#
|
||||
# familybase = basename + rest + other (+ suffix)
|
||||
# ID 1/2 just have self.style in the subfamily, all the rest ends up in the family
|
||||
# ID 16/17 have self.style and self.weight in the subfamily, the rest ends up in the family
|
||||
|
||||
def fullname(self):
|
||||
"""Get the SFNT Fullname (ID 4)"""
|
||||
if self.for_windows:
|
||||
win = 'Windows Compatible'
|
||||
else:
|
||||
win = ''
|
||||
styles = self.style_token
|
||||
weights = self.weight_token
|
||||
if self.keep_regular_in_family == None:
|
||||
keep_regular = FontnameTools.is_keep_regular(self._basename + ' ' + self._rest)
|
||||
else:
|
||||
keep_regular = self.keep_regular_in_family
|
||||
if ('Regular' in styles
|
||||
and (not keep_regular
|
||||
or len(self.weight_token) > 0)): # This is actually a malformed font name
|
||||
styles = list(self.style_token)
|
||||
styles.remove('Regular')
|
||||
# For naming purposes we want Oblique to be part of the styles
|
||||
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
|
||||
return FontnameTools.concat(self.basename, self.rest, self.other_token, self.fullname_suff, win, weights, styles)
|
||||
|
||||
def psname(self):
|
||||
"""Get the SFNT PostScriptName (ID 6)"""
|
||||
# This is almost self.family() + '-' + self.subfamily() but without short styles
|
||||
fam = FontnameTools.camel_casify(FontnameTools.concat(self.basename, self.rest, self.other_token, self.fontname_suff))
|
||||
sub = FontnameTools.camel_casify(FontnameTools.concat(self.weight_token, self.style_token))
|
||||
if len(sub) > 0:
|
||||
sub = '-' + sub
|
||||
out = FontnameTools.postscript_char_filter(fam + sub)
|
||||
# The name string must be no longer than 63 characters
|
||||
return out[:63]
|
||||
|
||||
def preferred_family(self):
|
||||
"""Get the SFNT Preferred Familyname (ID 16)"""
|
||||
if self.suppress_preferred_if_identical and len(self.weight_token) == 0:
|
||||
# Do not set if identical to ID 1
|
||||
return ''
|
||||
(name, rest) = self._shortened_name()
|
||||
return FontnameTools.concat(name, rest, self.other_token, self.family_suff)
|
||||
|
||||
def preferred_styles(self):
|
||||
"""Get the SFNT Preferred Styles (ID 17)"""
|
||||
styles = self.style_token
|
||||
weights = self.weight_token
|
||||
if self.suppress_preferred_if_identical and len(weights) == 0:
|
||||
# Do not set if identical to ID 2
|
||||
return ''
|
||||
# For naming purposes we want Oblique to be part of the styles
|
||||
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
|
||||
return FontnameTools.concat(weights, styles)
|
||||
|
||||
def family(self):
|
||||
"""Get the SFNT Familyname (ID 1)"""
|
||||
# We use the short form of the styles to save on number of chars
|
||||
(name, rest) = self._shortened_name()
|
||||
other = self.other_token
|
||||
weight = self.weight_token
|
||||
if self.use_short_families[1]:
|
||||
other = FontnameTools.short_styles(other)
|
||||
weight = FontnameTools.short_styles(weight)
|
||||
return FontnameTools.concat(name, rest, other, self.family_suff, weight)
|
||||
|
||||
def subfamily(self):
|
||||
"""Get the SFNT SubFamily (ID 2)"""
|
||||
if len(self.style_token) == 0:
|
||||
if 'Oblique' in self.weight_token:
|
||||
return FontnameTools.concat(self.style_token, 'Italic')
|
||||
return 'Regular'
|
||||
if 'Oblique' in self.weight_token and not 'Italic' in self.style_token:
|
||||
return FontnameTools.concat(self.style_token, 'Italic')
|
||||
return FontnameTools.concat(self.style_token)
|
||||
|
||||
def ps_familyname(self):
|
||||
"""Get the PS Familyname"""
|
||||
return self._make_ps_mame(self.family())
|
||||
|
||||
def ps_fontname(self):
|
||||
"""Get the PS fontname"""
|
||||
# This Adobe restriction is classically ignored
|
||||
# if len(n) > 29:
|
||||
# print('Shortening too long PS fontname')
|
||||
# return n[:29]
|
||||
return self._make_ps_mame(self.psname())
|
||||
|
||||
def macstyle(self, style):
|
||||
"""Modify a given macStyle value for current name, just bits 0 and 1 touched"""
|
||||
b = style & (~3)
|
||||
b |= 1 if 'Bold' in self.style_token else 0
|
||||
b |= 2 if 'Italic' in self.style_token else 0
|
||||
return b
|
||||
|
||||
def fs_selection(self, fs):
|
||||
"""Modify a given fsSelection value for current name, bits 0, 5, 6, 8, 9 touched"""
|
||||
ITALIC = 1 << 0; BOLD = 1 << 5; REGULAR = 1 << 6; WWS = 1 << 8; OBLIQUE = 1 << 9
|
||||
b = fs & (~(ITALIC | BOLD | REGULAR | WWS | OBLIQUE))
|
||||
if 'Bold' in self.style_token:
|
||||
b |= BOLD
|
||||
# Ignore Italic if we have Oblique
|
||||
if 'Oblique' in self.weight_token:
|
||||
b |= OBLIQUE
|
||||
elif 'Italic' in self.style_token:
|
||||
b |= ITALIC
|
||||
# Regular is just the basic weight
|
||||
if len(self.weight_token) == 0:
|
||||
b |= REGULAR
|
||||
b |= WWS # We assert this by our naming process
|
||||
return b
|
||||
|
||||
def rename_font(self, font):
|
||||
"""Rename the font to include all information we found (font is fontforge font object)"""
|
||||
font.fontname = self.ps_fontname()
|
||||
font.fullname = self.fullname()
|
||||
font.familyname = self.ps_familyname()
|
||||
|
||||
# We have to work around several issues in fontforge:
|
||||
#
|
||||
# a. Remove some entries from SFNT table; fontforge has no API function for that
|
||||
#
|
||||
# b. Fontforge does not allow to set SubFamily (and other) to any value:
|
||||
#
|
||||
# Fontforge lets you set any value, unless it is the default value. If it
|
||||
# is the default value it does not set anything. It also does not remove
|
||||
# a previously existing non-default value. Why it is done this way is
|
||||
# unclear:
|
||||
# fontforge/python.c SetSFNTName() line 11431
|
||||
# return( 1 ); /* If they set it to the default, there's nothing to do */
|
||||
#
|
||||
# Then is the question: What is the default? It is taken from the
|
||||
# currently set fontname (??!). The fontname is parsed and everything
|
||||
# behind the dash is the default SubFamily:
|
||||
# fontforge/tottf.c DefaultTTFEnglishNames()
|
||||
# fontforge/splinefont.c _GetModifiers()
|
||||
#
|
||||
# To fix this without touching Fontforge we need to set the SubFamily
|
||||
# directly in the SFNT table:
|
||||
#
|
||||
# c. Fontforge has the bug that it allows to write empty-string to a SFNT field
|
||||
# and it is actually embedded as empty string, but empty strings are not
|
||||
# shown if you query the sfnt_names *rolleyes*
|
||||
|
||||
sfnt_list = []
|
||||
TO_DEL = ['Family', 'SubFamily', 'Fullname', 'Postscriptname', 'Preferred Family', 'Preferred Styles', 'Compatible Full']
|
||||
for l, k, v in list(font.sfnt_names):
|
||||
if not k in TO_DEL:
|
||||
sfnt_list += [( l, k, v )]
|
||||
|
||||
sfnt_list += [( 'English (US)', 'Family', self.family() )]
|
||||
sfnt_list += [( 'English (US)', 'SubFamily', self.subfamily() )]
|
||||
sfnt_list += [( 'English (US)', 'Fullname', self.fullname() )]
|
||||
sfnt_list += [( 'English (US)', 'PostScriptName', self.psname() )]
|
||||
|
||||
p_fam = self.preferred_family()
|
||||
if len(p_fam):
|
||||
sfnt_list += [( 'English (US)', 'Preferred Family', p_fam )]
|
||||
p_sty = self.preferred_styles()
|
||||
if len(p_sty):
|
||||
sfnt_list += [( 'English (US)', 'Preferred Styles', p_sty )]
|
||||
|
||||
font.sfnt_names = tuple(sfnt_list)
|
||||
|
||||
font.macstyle = self.macstyle(font.macstyle)
|
||||
|
||||
# TODO: fsSelection, unfortunately fontforge does not support that directly
|
||||
# but has some automaton to deduce it from macstyle, which means loosing information
|
||||
# https://github.com/fontforge/fontforge/issues/2131
|
||||
# https://github.com/jsomedon/Fix-fsSelection-bits-for-SF-fonts/blob/main/fix_fsSelection.sh
|
||||
# Well, lets ignore it for now, as we always did ;)
|
285
bin/scripts/name_parser/FontnameTools.py
Normal file
285
bin/scripts/name_parser/FontnameTools.py
Normal file
|
@ -0,0 +1,285 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
class FontnameTools:
|
||||
"""Deconstruct a font filename to get standardized name parts"""
|
||||
|
||||
@staticmethod
|
||||
def front_upper(word):
|
||||
"""Capitalize a string (but keep case of subsequent chars)"""
|
||||
return word[:1].upper() + word[1:]
|
||||
|
||||
@staticmethod
|
||||
def camel_casify(word):
|
||||
"""Remove blanks and use CamelCase for the new word"""
|
||||
return ''.join(map(FontnameTools.front_upper, word.split(' ')))
|
||||
|
||||
@staticmethod
|
||||
def camel_explode(word):
|
||||
"""Explode CamelCase -> Camel Case"""
|
||||
# But do not explode "JetBrains" etc at string start...
|
||||
excludes = [
|
||||
'JetBrains',
|
||||
'DejaVu',
|
||||
'OpenDyslexicAlta',
|
||||
'OpenDyslexicMono',
|
||||
'OpenDyslexic',
|
||||
'DaddyTimeMono',
|
||||
'InconsolataGo',
|
||||
'ProFontWindows',
|
||||
'ProFont',
|
||||
'ProggyClean',
|
||||
]
|
||||
m = re.match('(' + '|'.join(excludes) + ')(.*)', word)
|
||||
(prefix, word) = m.group(1,2) if m != None else ('', word)
|
||||
if len(word) == 0:
|
||||
return prefix
|
||||
parts = re.split('(?<=[a-z0-9])(?=[A-Z])', word)
|
||||
if len(prefix):
|
||||
parts.insert(0, prefix)
|
||||
return ' '.join(parts)
|
||||
|
||||
@staticmethod
|
||||
def drop_empty(l):
|
||||
"""Remove empty strings from list of strings"""
|
||||
return [x for x in l if len(x) > 0]
|
||||
|
||||
@staticmethod
|
||||
def concat(*all_things):
|
||||
"""Flatten list of (strings or lists of strings) to a blank-separated string"""
|
||||
all = []
|
||||
for thing in all_things:
|
||||
if type(thing) == str:
|
||||
all.append(thing)
|
||||
else:
|
||||
all += thing
|
||||
return ' '.join(FontnameTools.drop_empty(all))
|
||||
|
||||
@staticmethod
|
||||
def unify_style_names(style_name):
|
||||
"""Substitude some known token with standard wording"""
|
||||
known_names = {
|
||||
# Source of the table is the current sourcefonts
|
||||
# Left side needs to be lower case
|
||||
'-': '',
|
||||
'book': '',
|
||||
'text': '',
|
||||
'ce': 'CE',
|
||||
'(ttf)': '(TTF)',
|
||||
#'semibold': 'Demi',
|
||||
'ob': 'Oblique',
|
||||
'it': 'Italic',
|
||||
'i': 'Italic',
|
||||
'b': 'Bold',
|
||||
'normal': 'Regular',
|
||||
'c': 'Condensed',
|
||||
'r': 'Regular',
|
||||
'm': 'Medium',
|
||||
'l': 'Light',
|
||||
}
|
||||
if style_name in known_names:
|
||||
return known_names[style_name.lower()]
|
||||
return style_name
|
||||
|
||||
@staticmethod
|
||||
def shorten_style_name(name):
|
||||
"""Substitude some known styles to short form"""
|
||||
known_names = {
|
||||
# Chiefly from Noto
|
||||
'SemiCondensed': 'SemCond',
|
||||
'Condensed': 'Cond',
|
||||
'ExtraCondensed': 'ExtCond',
|
||||
'SemiBold': 'SemBd',
|
||||
'ExtraBold': 'ExtBd',
|
||||
'Medium': 'Med',
|
||||
'ExtraLight': 'ExtLt',
|
||||
'Black': 'Blk',
|
||||
}
|
||||
if name in known_names:
|
||||
return known_names[name]
|
||||
return name
|
||||
|
||||
@staticmethod
|
||||
def short_styles(styles):
|
||||
"""Shorten all style names in a list"""
|
||||
return list(map(FontnameTools.shorten_style_name, styles))
|
||||
@staticmethod
|
||||
def make_oblique_style(weights, styles):
|
||||
"""Move "Oblique" from weights to styles for font naming purposes"""
|
||||
if 'Oblique' in weights:
|
||||
weights = list(weights)
|
||||
weights.remove('Oblique')
|
||||
styles = list(styles)
|
||||
styles.append('Oblique')
|
||||
return (weights, styles)
|
||||
|
||||
@staticmethod
|
||||
def get_name_token(name, tokens, allow_regex_token = False):
|
||||
"""Try to find any case insensitive token from tokens in the name, return tuple with found token-list and rest"""
|
||||
# The default mode (allow_regex_token = False) will try to find any verbatim string in the
|
||||
# tokens list (case insensitive matching) and give that tokens list item back with
|
||||
# unchanged case (i.e. [ 'Bold' ] will match "bold" and return it as [ 'Bold', ]
|
||||
# In the regex mode (allow_regex_token = True) it will use the tokens elements as
|
||||
# regexes and return the original (i.e. from name) case.
|
||||
#
|
||||
# Token are always used in a regex and may not capture, use non capturing
|
||||
# grouping if needed (?: ... )
|
||||
lower_tokens = [ t.lower() for t in tokens ]
|
||||
not_matched = ""
|
||||
all_tokens = []
|
||||
j = 1
|
||||
regex = re.compile('(.*?)(' + '|'.join(tokens) + ')(.*)', re.IGNORECASE)
|
||||
while j:
|
||||
j = regex.match(name)
|
||||
if not j:
|
||||
break
|
||||
if len(j.groups()) != 3:
|
||||
sys.exit('Malformed regex in FontnameTools.get_name_token()')
|
||||
not_matched += ' ' + j.groups()[0] # Blanc prevents unwanted concatenation of unmatched substrings
|
||||
tok = j.groups()[1].lower()
|
||||
if tok in lower_tokens:
|
||||
tok = tokens[lower_tokens.index(tok)]
|
||||
tok = FontnameTools.unify_style_names(tok)
|
||||
if len(tok):
|
||||
all_tokens.append(tok)
|
||||
name = j.groups()[2] # Recurse rest
|
||||
not_matched += ' ' + name
|
||||
return ( not_matched.strip(), all_tokens )
|
||||
|
||||
@staticmethod
|
||||
def postscript_char_filter(name):
|
||||
"""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 = ""
|
||||
for c in name:
|
||||
if c in '[](){}<>/%' or ord(c) < 33 or ord(c) > 126:
|
||||
continue
|
||||
out += c
|
||||
return out
|
||||
|
||||
SIL_TABLE = [
|
||||
( '(s)ource', r'\1auce' ),
|
||||
( '(h)ermit', r'\1urmit' ),
|
||||
( '(h)asklig', r'\1asklug' ),
|
||||
( '(s)hare', r'\1hure' ),
|
||||
( 'IBM[- ]?plex', r'Blex' ), # We do not keep the case here
|
||||
( '(t)erminus', r'\1erminess' ),
|
||||
( '(l)iberation', r'\1iteration' ),
|
||||
( 'iA([- ]?)writer', r'iM\1Writing' ),
|
||||
( '(a)nka/(c)oder', r'\1na\2onder' ),
|
||||
( '(c)ascadia( ?)(c)ode', r'\1askaydia\2\3ove' ),
|
||||
( '(c)ascadia( ?)(m)ono', r'\1askaydia\2\3ono' ),
|
||||
( '(m)plus', r'\1+'), # Added this, because they use a plus symbol :->
|
||||
( 'Gohufont', r'GohuFont'), # Correct to CamelCase
|
||||
# Noone cares that font names starting with a digit are forbidden:
|
||||
# ( '(3270)', r'Ibeam\1'),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def is_keep_regular(basename):
|
||||
"""This has been decided by the font designers, we need to mimic that (for comparison purposes)"""
|
||||
KEEP_REGULAR = [
|
||||
'Agave',
|
||||
'Arimo',
|
||||
'Aurulent',
|
||||
'Cascadia',
|
||||
'Cousine',
|
||||
'Fantasque',
|
||||
'Fira',
|
||||
|
||||
'Overpass',
|
||||
'Lilex',
|
||||
'Inconsolata$', # not InconsolataGo
|
||||
'IAWriter',
|
||||
'Meslo',
|
||||
'Monoid',
|
||||
'Mononoki',
|
||||
'Hack',
|
||||
'JetBrains Mono',
|
||||
'Noto Sans',
|
||||
'Noto Serif',
|
||||
'Victor',
|
||||
]
|
||||
for kr in KEEP_REGULAR:
|
||||
if (basename.rstrip() + '$').startswith(kr): return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _parse_simple_font_name(name):
|
||||
"""Parse a filename that does not follow the 'FontFamilyName-FontStyle' pattern"""
|
||||
# No dash in name, maybe we have blanc separated filename?
|
||||
if ' ' in name:
|
||||
return FontnameTools.parse_font_name(name.replace(' ', '-'))
|
||||
# Do we have a number-name boundary?
|
||||
p = re.split('(?<=[0-9])(?=[a-zA-Z])', name)
|
||||
if len(p) > 1:
|
||||
return FontnameTools.parse_font_name('-'.join(p))
|
||||
# Or do we have CamelCase?
|
||||
n = FontnameTools.camel_explode(name)
|
||||
if n != name:
|
||||
return FontnameTools.parse_font_name(n.replace(' ', '-'))
|
||||
return (False, FontnameTools.camel_casify(name), [], [], [], '')
|
||||
|
||||
@staticmethod
|
||||
def parse_font_name(name):
|
||||
"""Expects a filename following the 'FontFamilyName-FontStyle' pattern and returns ... parts"""
|
||||
name = re.sub(r'\bsemi-narrow\b', 'SemiNarrow', name, 1, re.IGNORECASE) # Just for "3270 Semi-Narrow" :-/
|
||||
name = re.sub('[_\s]+', ' ', name)
|
||||
matches = re.match(r'([^-]+)(?:-(.*))?', name)
|
||||
familyname = FontnameTools.camel_casify(matches.group(1))
|
||||
style = matches.group(2)
|
||||
|
||||
if not style:
|
||||
return FontnameTools._parse_simple_font_name(name)
|
||||
|
||||
# These are the FontStyle keywords we know, in three categories
|
||||
# Weights end up as Typographic Family parts ('after the dash')
|
||||
# Styles end up as Family parts (for classic grouping of four)
|
||||
# Others also end up in Typographic Family ('before the dash')
|
||||
weights = [ 'Thin', 'Light', 'ExtraLight', 'SemiBold', 'Demi',
|
||||
'SemiLight', 'Medium', 'Black', 'ExtraBold', 'Heavy',
|
||||
'Oblique', 'Condensed', 'SemiCondensed', 'ExtraCondensed',
|
||||
'Narrow', 'SemiNarrow', 'Retina', ]
|
||||
styles = [ 'Bold', 'Italic', 'Regular', 'Normal', ]
|
||||
# Some font specialities:
|
||||
other = [
|
||||
'-', 'Book', 'For', 'Powerline',
|
||||
'Text', # Plex
|
||||
'IIx', # Profont IIx
|
||||
'LGC', # Inconsolata LGC
|
||||
r'\(TTF\)', # Terminus (TTF)
|
||||
r'\bCE\b', # ProggycleanTT CE
|
||||
r'[12][cmp]n?', # MPlus
|
||||
r'(?:uni-)?1[14]', # GohuFont uni
|
||||
]
|
||||
|
||||
# Sometimes used abbreviations
|
||||
weight_abbrevs = [ 'ob', 'c', 'm', 'l', ]
|
||||
style_abbrevs = [ 'it', 'r', 'b', 'i', ]
|
||||
|
||||
( style, weight_token ) = FontnameTools.get_name_token(style, weights)
|
||||
( style, style_token ) = FontnameTools.get_name_token(style, styles)
|
||||
( style, other_token ) = FontnameTools.get_name_token(style, other, True)
|
||||
if len(style) < 4:
|
||||
( style, weight_token_abbrevs ) = FontnameTools.get_name_token(style, weight_abbrevs)
|
||||
( style, style_token_abbrevs ) = FontnameTools.get_name_token(style, style_abbrevs)
|
||||
weight_token += weight_token_abbrevs
|
||||
style_token += style_token_abbrevs
|
||||
while 'Regular' in style_token and len(style_token) > 1:
|
||||
# Correct situation where "Regular" and something else is given
|
||||
style_token.remove('Regular')
|
||||
|
||||
# Recurse to see if unmatched stuff between dashes can belong to familyname
|
||||
matches2 = re.match(r'(\w+)-(.*)', style)
|
||||
if matches2:
|
||||
return FontnameTools.parse_font_name(familyname + matches2.group(1) + '-' + matches2.group(2))
|
||||
|
||||
style = re.sub(r'(^|\s)\d+(\.\d+)+(\s|$)', r'\1\3', style) # Remove (free standing) version numbers
|
||||
style_parts = FontnameTools.drop_empty(style.split(' '))
|
||||
style = ' '.join(map(FontnameTools.front_upper, style_parts))
|
||||
familyname = FontnameTools.camel_explode(familyname)
|
||||
return (True, familyname, weight_token, style_token, other_token, style)
|
194
bin/scripts/name_parser/README.md
Normal file
194
bin/scripts/name_parser/README.md
Normal file
|
@ -0,0 +1,194 @@
|
|||
## Creating Consistently Grouped Patched Fonts
|
||||
|
||||
This is a small sub-project to font-patcher that uses a little bit more knowledge
|
||||
to come up with font names and name parts. In applications multiple fonts are grouped
|
||||
under a 'Family'. Each member of the Family has a different 'SubFamily' or 'Style'.
|
||||
|
||||
Consider a font named 'Times' that has two variants: normal and bold. For this font the
|
||||
Family would be 'Times' and the 'Style' would be 'Regular' (i.e normal) in one file and
|
||||
'Bold' in the other file.
|
||||
|
||||
With this information applications are able to group all 'Times' together and additionally choose the
|
||||
'Bold' font if the user pushes the 'B' button on the font style dialog in that application.
|
||||
|
||||
### Motivation
|
||||
|
||||
Quite a number of patched fonts have inconsistent or simply wrong font grouping. The naming in
|
||||
general is sometimes surprising and not following naming conventions. This is in part due to
|
||||
the font-patcher, but in part the source fonts are already strange.
|
||||
This results in invisible (but installed) fonts in some applications, inconsistent naming
|
||||
(Familyname differs from Fullname) and not correctly working bold/italic selectors in some applications.
|
||||
|
||||
And we would like to have the information within the names sorted in a consistent way.
|
||||
usually a font name consists of these parts (in this order):
|
||||
|
||||
1. Name base (e.g. `Noto`)
|
||||
2. Variant (e.g. `Sans`)
|
||||
3. Subvariant (e.g. `Display`)
|
||||
4. Weight (e.g. `Black`)
|
||||
5. Style (e.g. `Italic`)
|
||||
|
||||
This is important because we want to add subvariant information, namely the `Nerd Font` part.
|
||||
|
||||
Example:
|
||||
|
||||
* (old) `Iosevka Term Light Italic Nerd Font`
|
||||
* (new) `Iosevka Term Nerd Font Light Italic`
|
||||
|
||||
### The Plan
|
||||
|
||||
To solve these issues the font name parts have to be analyzed more thoroughly and then categorized.
|
||||
These categories are then used to assemble the names in correct order. The simple (not
|
||||
typographically aware) applications shall always get groups of at most four styles, and these
|
||||
are Regular, Bold, Italic, and Bold-Italic. Other styles turn up as Families, because this is the
|
||||
only way they would work in these more simple applications.
|
||||
|
||||
Typographically aware applications, on the other hand, get all styles grouped under one Family name.
|
||||
|
||||
First experiments showed that the full information can usually be restored already from the file
|
||||
names that our source fonts have.
|
||||
|
||||
This new naming is complete optional (but recommended). Give the option `--parser to` font-patcher
|
||||
and it will try to come up with reasonable grouping and naming. Leave the option out and it will
|
||||
work as it always did.
|
||||
|
||||
### The Tests
|
||||
|
||||
In this directory there are two tests.
|
||||
|
||||
1. The first test checks the basics of the algorithm. It takes the filenames of all fonts in
|
||||
`src/unpatched\_fonts`, then it calculates the naming and compares it to the original
|
||||
naming in the font files. Ideally they would be equal.
|
||||
2. The second test does a 'production run'. It patches each font in `src/unpatched_fonts/`
|
||||
and patches it two times: Once without `--parser` and once with. Then it compares the
|
||||
naming, and it also shows the original font naming (for comparison).
|
||||
|
||||
All tests base on these assumptions
|
||||
|
||||
* Fullname must be roughly equal
|
||||
* Fontname must be roughly equal
|
||||
* Familyname must roughly equal, order of all words does not matter
|
||||
_(Order of words is ignored with test 2 only)_
|
||||
* SubFamilyname must be equal, order of words does not matter
|
||||
_(First word must be equal, order of other words is ignored with test 2 only)_
|
||||
* Typographic names can be empty if the correct typographic name would be equal to the ordinary name
|
||||
* Tests are done case insensitive
|
||||
* Some special exemptions are made (see `lenient_cmp()` in test scripts)
|
||||
|
||||
#### Test 1
|
||||
|
||||
`fontforge name_parser_test1 ../../../src/unpatched-fonts/**/*.[ot]tf 2>/dev/null`
|
||||
|
||||
This test takes the filename of a font, parses it and generates names from it. Then the actual
|
||||
font is opened and the generated names are compared with the stored names. This test is used
|
||||
to test the algorithm itself. Of course no SIL table is active as we want to preserve the original
|
||||
names.
|
||||
|
||||
The output shows all the names, always two lines: first the generated names, then the readout
|
||||
names. If there are differences the generated names are tagged with `+` and the readout ones
|
||||
with `-`. If there are differences the actually different name part is marked with an `X`.
|
||||
|
||||
The differences have reasons, and there is a file with textual explanations for them. So far
|
||||
all differences are 'ok'. A new run of the script will compare all differences with the stored
|
||||
ones and alert the user if a new difference is detected (or a difference vanished). In this
|
||||
way changes of the algorithm can be tested with a wide base of inputs.
|
||||
|
||||
#### Test 2
|
||||
|
||||
`fontforge name_parser_test2 ../../../src/unpatched-fonts/**/*.[ot]tf 2>/dev/null`
|
||||
|
||||
This test compares actually patched fonts. Every font in `src/unpatched_fonts/` is patched two
|
||||
times: First with the 'old/classic' `font-patcher` naming, and second with the new naming
|
||||
algorithm in action (by specifying `--parser`). Again the name parts are compared with some
|
||||
lenience and an output generated like test 1 does.
|
||||
|
||||
Also again a file with known differences (with explanations) is read, and any new or vanished
|
||||
differences are reported. In the report an additional line is given, tagged with `>`, that
|
||||
contains the names of the original font, for human interpretation (often the reason
|
||||
for a difference is obvious, because the classic `font-patcher` dropped information.
|
||||
|
||||
_Note: Fonts `NotoColorEmoji` and `Lilex-VF` are not patchable, and thus ignored_
|
||||
_Note: Fonts `iosevka-heavyoblique`, `iosevka-term-heavyoblique`, `iosevka-mediumoblique` crash my machine and are ignored_
|
||||
|
||||
### Differences
|
||||
|
||||
The naming of the patched fonts, if `--parse` is applied, will be different. Of course, that is the goal.
|
||||
What are the differences in particular:
|
||||
|
||||
* `Nerd Font` is not added in the end, but after the extended base name before the style
|
||||
* The SubFamily contains only 4 Styles max: Regular, Bold, Italic, Bold-Italic
|
||||
* The Noto fonts retain their abbreviated style names in the Family information
|
||||
* `Nerd Font Mono` fonts get a `M` in windows mode (I believe that has been left out accidentally before)
|
||||
|
||||
Apart from these general things, all changes are documented in detail in the `name_parser_test2` issues file.
|
||||
Here is an overview over all the things that get renamed and why:
|
||||
|
||||
| Occurences | Description |
|
||||
|------------|-------------|
|
||||
| 511 | Add weight/style to family |
|
||||
| 43 | The fonts name is M+ not Mplus |
|
||||
| 36 | Drop unneeded Typogr.Family/Typogr.Style |
|
||||
| 26 | 'Term' is missing from Family |
|
||||
| 22 | Change regular-equivalent name to Regular |
|
||||
| 19 | Put Oblique into own SubFamily (and mark it as italic) |
|
||||
| 5 | Drop Regular from Style |
|
||||
| 4 | We handle (TTF) as sub-name |
|
||||
| 4 | Fullname has been missing 'Nerd Font' |
|
||||
| 4 | Bold / Bold-Italic are just a styles of Regular |
|
||||
| 2 | Original font broken (Light in Family) |
|
||||
| 2 | Classify Medium as own weigt and not Bold |
|
||||
| 2 | Bold and Italic are styles of a basefont |
|
||||
| 1 | Weight Condensed does not belong to base name |
|
||||
| 1 | Use only Regular/Bold/Italic in SubFamily |
|
||||
| 1 | Handle Retina as Weight and not Style |
|
||||
| 1 | Do not call Semibold Light-Bold |
|
||||
|
||||
From the count we see that almost all fonts are affected by incorrect Family naming.
|
||||
|
||||
### Further steps
|
||||
|
||||
One can examine all the (current) naming differences in the `name_parser_test2.known_issues`
|
||||
file. The Explanation is followed by three lines of names: source-file, patched-with-parser,
|
||||
and patched-classic.
|
||||
|
||||
The Explanation sorts most differences into common groups. This helps to weed out
|
||||
explanations that might do not need much attention.
|
||||
|
||||
### Helper scripts
|
||||
|
||||
There are some helper scripts that help examining the font files. Of course there are other,
|
||||
more professional tools to dump font information, but here we get all we need in a concise
|
||||
way:
|
||||
* `query_names` `font_name [font_name ...]`
|
||||
* `query_panose` `font_name`
|
||||
* `query_sftn` `[<sfnt-name>] font_name`
|
||||
* `query_version` `font_name`
|
||||
|
||||
They can be invoked like this `$ fontforge query_sfnt foo.ttf`.
|
||||
|
||||
### Appendix: The `name_parser_test*.known_issues` files
|
||||
|
||||
All differences of 'old' to 'new' naming (if not one of the very general kind like resorting of
|
||||
the words) are documented in the `known_issues` files. For each difference a reason is given.
|
||||
|
||||
The files consist of entries that spans 3 (for test 1) or 4 (for test 2) lines.
|
||||
| Line starts with | Contents |
|
||||
|------------------|----------|
|
||||
| # | Reson for the difference (or `AUTOGENERATED`) |
|
||||
| > | Naming fo the original/source font (only test 2) |
|
||||
| + | Naming with `--parser` (new naming) |
|
||||
| - | Naming classically generated by font-patcher |
|
||||
|
||||
After any test run a `known_issues.new` file is generated. It contains all the issues
|
||||
from the `known_issues` file that were detected. Original issues that are not
|
||||
existing anymore are at the bottom of the new file, clearly marked as such. If new
|
||||
(previously unexplained) issues were detected they show up with the `AUTOGENERATED`
|
||||
reason.
|
||||
|
||||
After adding new fonts or replacing font files the test can be rerun. If there are issues
|
||||
in the `.new` file they should be documented there, and the `.new` file replace the
|
||||
original `known_issues` file (after removing possible 'obsolete' issues that are listed in
|
||||
the bottom of the new file).
|
||||
|
||||
In this way one can tweak the parser code and compare very easily what a change
|
||||
means for all the fonts, which will break or be repaired.
|
19
bin/scripts/name_parser/frequency
Normal file
19
bin/scripts/name_parser/frequency
Normal file
|
@ -0,0 +1,19 @@
|
|||
cat name_parser_test2.known_issues | grep '####' | sed -E 's/#### //;s/\] /]\n/g;s/ \[[0-9]+\]//g' | sort | uniq -c | sort -nr
|
||||
|
||||
511 Add weight/style to family
|
||||
43 The fonts name is M+ not Mplus
|
||||
36 Drop unneeded Typogr.Family/Typogr.Style
|
||||
26 'Term' is missing from Family
|
||||
22 Change regular-equivalent name to Regular
|
||||
19 Put Oblique into own SubFamily (and mark it as italic)
|
||||
5 Drop Regular from Style
|
||||
4 We handle (TTF) as sub-name
|
||||
4 Fullname has been missing 'Nerd Font'
|
||||
4 Bold / Bold-Italic are just a styles of Regular
|
||||
2 Original font broken (Light in Family)
|
||||
2 Classify Medium as own weigt and not Bold
|
||||
2 Bold and Italic are styles of a basefont
|
||||
1 Weight Condensed does not belong to base name
|
||||
1 Use only Regular/Bold/Italic in SubFamily
|
||||
1 Handle Retina as Weight and not Style
|
||||
1 Do not call Semibold Light-Bold
|
160
bin/scripts/name_parser/name_parser_test1
Normal file
160
bin/scripts/name_parser/name_parser_test1
Normal file
|
@ -0,0 +1,160 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os.path
|
||||
import fontforge
|
||||
from FontnameParser import FontnameParser
|
||||
|
||||
###### Some helpers
|
||||
|
||||
def get_sfnt_dict(font):
|
||||
"""Extract SFNT table as nice dict"""
|
||||
d = []
|
||||
for i, el in enumerate(font.sfnt_names):
|
||||
d += [(el[1], el[2])]
|
||||
return dict(d)
|
||||
|
||||
def format_names(header, *stuff):
|
||||
"""Unify outputs (with header)"""
|
||||
f = '{:1.1}|{:50.50} |{:1.1}| {:50.50} |{:1.1}| {:30.30} |{:1.1}| {:30.30} |{:1.1}| {:30.30} |{:1.1}| {:.30}'
|
||||
if header:
|
||||
d = '------------------------------------------------------------'
|
||||
return f.format(*stuff) + '\n' + f.format('', d, d, d, d, d, d, d, d, d, d, d)
|
||||
return f.format(*stuff).rstrip()
|
||||
|
||||
def lenient_cmp(s1, s2):
|
||||
"""Compare two font name (parts) but be a bit lenient ;->"""
|
||||
# We do not care about:
|
||||
# - Case
|
||||
# - "Display" vs "Disp" (in Noto)
|
||||
# Allow for "IBM 3278" name
|
||||
s = [ s1, s2 ]
|
||||
for i in range(2):
|
||||
# Usually given transform from 'their' to 'our' style
|
||||
s[i] = s[i].lower()
|
||||
s[i] = re.sub(r'\bdisp\b', 'display', s[i]) # Noto
|
||||
s[i] = s[i].replace('ibm 3270', '3270') # 3270
|
||||
s[i] = s[i].replace('3270-', '3270 ') # 3270
|
||||
s[i] = s[i].replace('lekton-', 'lekton ') # Lekton
|
||||
s[i] = s[i].replace('semi-narrow', 'seminarrow') # 3270
|
||||
s[i] = s[i].replace('bolditalic', 'bold italic')
|
||||
s[i] = re.sub(r'\bfor\b', '', s[i]) # Meslo, Monofur
|
||||
s[i] = re.sub(r'\bpowerline\b', '', s[i]) # Meslo, Monofur
|
||||
s[i] = s[i].replace('fira mono', 'fura mono') # Obviously someone forgot to rename the fonts in Fira/
|
||||
s[i] = s[i].replace('aurulentsansmono-', 'aurulent sans mono ') # Aurulent fullname oddity
|
||||
s[i] = s[i].replace('mononoki-', 'mononoki ') # Mononoki has somtimes a dash
|
||||
s[i] = re.sub(r'\br\b', 'regular', s[i]) # Nonstandard style in Agave
|
||||
s[i] = re.sub(r'(bitstream vera sans mono.*) oblique', r'\1 italic', s[i]) # They call it Oblique but the filename says Italic
|
||||
s[i] = re.sub(r'gohufont (uni-)?(11|14)', 'gohufont', s[i]) # They put the 'name' into the subfamily/weight
|
||||
s[i] = s[i].replace('xltobl', 'extralight oblique') # Iosevka goes inventing names
|
||||
s[i] = re.sub(r'proggyclean(?!TT)( ?)', 'proggycleantt\1', s[i]) # ProggyClean has no TT in filename
|
||||
|
||||
s[i] = re.sub(r' +', ' ', s[i]).strip()
|
||||
return s[0] == s[1]
|
||||
|
||||
TEST_TABLE = [
|
||||
( '(m)plus', '\\1+'),
|
||||
( 'IAWriter', 'iA Writer'),
|
||||
( 'IBMPlex', 'IBM Plex'),
|
||||
( 'Vera', 'Bitstream Vera Sans'),
|
||||
]
|
||||
|
||||
###### Let's go!
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage: {} font_name [font_name ...]\n'.format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
with open(sys.argv[0] + '.known_issues', 'r') as f:
|
||||
known_issues = f.read().splitlines()
|
||||
# known_issues = [line.rstrip() for line in known_issues]
|
||||
print('Found {:.0f} known issues'.format(len(known_issues) / 3)) # approx ;)
|
||||
except OSError:
|
||||
print('Can not open known_issues file')
|
||||
known_issues = []
|
||||
new_issues = open(sys.argv[0] + '.known_issues.new', 'w')
|
||||
|
||||
print('Examining {} font files'.format(len(sys.argv) - 1))
|
||||
all_files = 0
|
||||
issue_files = 0
|
||||
known_files = 0
|
||||
|
||||
print(format_names(True, '', 'Filename', '', 'Fullname', '', 'Family', '', 'Subfamily', '', 'Typogr. Family', '', 'Typogr. Subfamily'))
|
||||
|
||||
for filename in sys.argv[1:]:
|
||||
fullfile = os.path.basename(filename)
|
||||
fname = os.path.splitext(fullfile)[0]
|
||||
if fname == 'NotoColorEmoji':
|
||||
continue # font is not patchable
|
||||
n = FontnameParser(fname).enable_short_families(False, 'Noto').add_name_substitution_table(TEST_TABLE)
|
||||
# Example for name injection:
|
||||
# n.inject_suffix("Nerd Font Complete Mono", "Nerd Font Complete M", "Nerd Font Mono")
|
||||
|
||||
font = fontforge.open(filename, 1)
|
||||
sfnt = get_sfnt_dict(font)
|
||||
font.close()
|
||||
all_files += 1
|
||||
|
||||
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 ''
|
||||
|
||||
t1 = not lenient_cmp(sfnt_full, n.fullname())
|
||||
t2 = not lenient_cmp(sfnt_fam, n.family())
|
||||
t3 = not lenient_cmp(sfnt_subfam, n.subfamily())
|
||||
t4 = not lenient_cmp(sfnt_pfam, n.preferred_family())
|
||||
t5 = not lenient_cmp(sfnt_psubfam, n.preferred_styles())
|
||||
|
||||
# Lenience: Allow for dropping unneeded prefered stuff:
|
||||
# New (sub)family is same as old preferred sub(family)
|
||||
if t4 and n.preferred_family() == '' and sfnt_pfam.lower() == n.family().lower():
|
||||
t4 = False
|
||||
if t5 and n.preferred_styles() == '' and sfnt_psubfam.lower() == n.subfamily().lower():
|
||||
t5 = False
|
||||
|
||||
if t1 or t2 or t3 or t4 or t5:
|
||||
m1 = '+'; m2 = '-'
|
||||
else:
|
||||
m1 = ''; m2 = ''
|
||||
if not n.parse_ok:
|
||||
m1 = '!'
|
||||
t1_ = 'X' if t1 else ''
|
||||
t2_ = 'X' if t2 else ''
|
||||
t3_ = 'X' if t3 else ''
|
||||
t4_ = 'X' if t4 else ''
|
||||
t5_ = 'X' if t5 else ''
|
||||
|
||||
o1 = format_names(False, m1, fullfile, t1_, n.fullname(), t2_, n.family(), t3_, n.subfamily(), t4_, n.preferred_family(), t5_, n.preferred_styles())
|
||||
o2 = format_names(False, m2, fullfile, '', sfnt_full, '', sfnt_fam, '', sfnt_subfam, '', sfnt_pfam, '', sfnt_psubfam)
|
||||
|
||||
if len(m1):
|
||||
issue_files += 1
|
||||
if not o1 in known_issues or not o2 in known_issues:
|
||||
new_issues.writelines(['#### AUTOGENERATED\n', o1 + '\n', o2 + '\n'])
|
||||
else:
|
||||
known_files += 1
|
||||
idx = known_issues.index(o1) - 1 # should be the index of the explanation line
|
||||
if known_issues[idx][0] != '#':
|
||||
sys.exit('Problem with known issues file, line', known_issues.index(o1))
|
||||
new_issues.writelines([known_issues[idx] + '\n', o1 + '\n', o2 + '\n'])
|
||||
# remove known_issue triplet
|
||||
known_issues.pop(idx)
|
||||
known_issues.pop(idx)
|
||||
known_issues.pop(idx)
|
||||
|
||||
print(o1, o2, sep='\n')
|
||||
|
||||
print('Fonts with different name rendering: {}/{} ({}/{} are in known_issues)'.format(issue_files, all_files, known_files, issue_files))
|
||||
|
||||
if len(known_issues) > 0:
|
||||
print('There are {} lines not needed in known_issues, appending commented out in new known_issues'.format(len(known_issues)))
|
||||
new_issues.write('\n#### The following lines are not needed anymore\n\n')
|
||||
for l in known_issues:
|
||||
new_issues.writelines([' ', l, '\n'])
|
||||
|
||||
new_issues.close()
|
210
bin/scripts/name_parser/name_parser_test1.known_issues
Normal file
210
bin/scripts/name_parser/name_parser_test1.known_issues
Normal file
|
@ -0,0 +1,210 @@
|
|||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|3270Medium.otf | | 3270 Medium |X| 3270 Medium |X| Regular |X| 3270 |X| Medium
|
||||
-|3270Medium.otf | | 3270-Medium | | IBM 3270 | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|3270Medium.ttf | | 3270 Medium |X| 3270 Medium |X| Regular |X| 3270 |X| Medium
|
||||
-|3270Medium.ttf | | 3270-Medium | | IBM 3270 | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, obviously for them Medium is Regular
|
||||
+|3270Narrow.otf | | 3270 Narrow | | 3270 Narrow |X| Regular |X| 3270 |X| Narrow
|
||||
-|3270Narrow.otf | | 3270 Narrow | | IBM 3270 Narrow | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, obviously for them Medium is Regular
|
||||
+|3270Narrow.ttf | | 3270 Narrow | | 3270 Narrow |X| Regular |X| 3270 |X| Narrow
|
||||
-|3270Narrow.ttf | | 3270 Narrow | | IBM 3270 Narrow | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, obviously for them Medium is Regular
|
||||
+|3270SemiNarrow.otf | | 3270 SemiNarrow | | 3270 SemiNarrow |X| Regular |X| 3270 |X| SemiNarrow
|
||||
-|3270SemiNarrow.otf | | 3270 Semi-Narrow | | IBM 3270 Semi-Narrow | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, obviously for them Medium is Regular
|
||||
+|3270SemiNarrow.ttf | | 3270 SemiNarrow | | 3270 SemiNarrow |X| Regular |X| 3270 |X| SemiNarrow
|
||||
-|3270SemiNarrow.ttf | | 3270 Semi-Narrow | | IBM 3270 Semi-Narrow | | Medium | | | |
|
||||
#### Drop special/unexpected name in Typographic Family
|
||||
+|Anonymice Powerline.ttf | | Anonymice Powerline | | Anonymice Powerline | | Regular |X| | |
|
||||
-|Anonymice Powerline.ttf | | Anonymice Powerline | | Anonymice Powerline | | Regular | | Anonymous Pro for Powerline | | Regular
|
||||
#### Font file says it is italic and not oblique
|
||||
+|VeraMono-Bold-Italic.ttf | | Bitstream Vera Sans Mono Bold Italic | | Bitstream Vera Sans Mono |X| Bold Italic | | | |
|
||||
-|VeraMono-Bold-Italic.ttf | | Bitstream Vera Sans Mono Bold Oblique | | Bitstream Vera Sans Mono | | Bold Oblique | | | |
|
||||
#### Font file says it is italic and not oblique
|
||||
+|VeraMono-Italic.ttf | | Bitstream Vera Sans Mono Italic | | Bitstream Vera Sans Mono |X| Italic | | | |
|
||||
-|VeraMono-Italic.ttf | | Bitstream Vera Sans Mono Oblique | | Bitstream Vera Sans Mono | | Oblique | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, Roman is usually equivalent to Regular
|
||||
+|VeraMono.ttf | | Bitstream Vera Sans Mono | | Bitstream Vera Sans Mono |X| Regular | | | |
|
||||
-|VeraMono.ttf | | Bitstream Vera Sans Mono | | Bitstream Vera Sans Mono | | Roman | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, Book is usually equivalent to Regular
|
||||
!|DaddyTimeMono.ttf | | DaddyTimeMono | | DaddyTimeMono |X| Regular | | | |
|
||||
-|DaddyTimeMono.ttf | | DaddyTimeMono | | DaddyTimeMono | | Book | | DaddyTimeMono | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|DejaVuSansMono-BoldOblique.ttf | | DejaVu Sans Mono Bold Oblique |X| DejaVu Sans Mono Oblique |X| Bold Italic |X| DejaVu Sans Mono |X| Bold Oblique
|
||||
-|DejaVuSansMono-BoldOblique.ttf | | DejaVu Sans Mono Bold Oblique | | DejaVu Sans Mono | | Bold Oblique | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|DejaVuSansMono-Oblique.ttf | | DejaVu Sans Mono Oblique |X| DejaVu Sans Mono Oblique |X| Italic |X| DejaVu Sans Mono |X| Oblique
|
||||
-|DejaVuSansMono-Oblique.ttf | | DejaVu Sans Mono Oblique | | DejaVu Sans Mono | | Oblique | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, Book is usually equivalent to Regular
|
||||
+|DejaVuSansMono.ttf | | DejaVu Sans Mono | | DejaVu Sans Mono |X| Regular | | | |
|
||||
-|DejaVuSansMono.ttf | | DejaVu Sans Mono | | DejaVu Sans Mono | | Book | | | |
|
||||
#### No need to have Typographic Family/Subfamily if it is identical to normal Family/Subfamily
|
||||
+|FuraMono-Bold Powerline.otf | | Fura Mono Powerline Bold | | Fura Mono Powerline | | Bold |X| | |
|
||||
-|FuraMono-Bold Powerline.otf | | Fira Mono Bold for Powerline | | Fira Mono for Powerline | | Bold | | Fira Mono for Powerline | |
|
||||
#### False positive, move Powerline from end to middle, Powerline will be dropped when patching anyhow
|
||||
+|FuraMono-Medium Powerline.otf | | Fura Mono Powerline Medium | | Fura Mono Powerline Medium | | Regular |X| Fura Mono Powerline | | Medium
|
||||
-|FuraMono-Medium Powerline.otf | | Fira Mono Medium for Powerline | | Fira Mono Medium for Powerline | | Regular | | Fira Mono Medium for Powerline | | Medium
|
||||
#### No need to have Typographic Family/Subfamily if it is identical to normal Family/Subfamily
|
||||
+|FuraMono-Regular Powerline.otf | | Fura Mono Powerline | | Fura Mono Powerline | | Regular |X| | |
|
||||
-|FuraMono-Regular Powerline.otf | | Fira Mono | | Fira Mono for Powerline | | Regular | | Fira Mono for Powerline | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|gohufont-11.ttf | | Gohufont 11 | | Gohufont 11 |X| Regular | | | |
|
||||
-|gohufont-11.ttf | | GohuFont | | GohuFont | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|gohufont-14.ttf | | Gohufont 14 | | Gohufont 14 |X| Regular | | | |
|
||||
-|gohufont-14.ttf | | GohuFont | | GohuFont | | 14 | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|gohufont-uni-11.ttf | | Gohufont uni-11 | | Gohufont uni-11 |X| Regular | | | |
|
||||
-|gohufont-uni-11.ttf | | GohuFont | | GohuFont | | Medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|gohufont-uni-14.ttf | | Gohufont uni-14 | | Gohufont uni-14 |X| Regular | | | |
|
||||
-|gohufont-uni-14.ttf | | GohuFont | | GohuFont | | uni-14 | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, Normal is usually equivalent to Regular
|
||||
+|heavy_data.ttf | | Heavy Data | | Heavy Data |X| Regular | | | |
|
||||
-|heavy_data.ttf | | Heavy Data | | Heavy Data | | Normal | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|Hermit-light.otf | | Hermit Light |X| Hermit Light |X| Regular |X| Hermit |X| Light
|
||||
-|Hermit-light.otf | | Hermit Light | | Hermit | | light | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Subfamily name into Family instead
|
||||
+|Hermit-medium.otf | | Hermit Medium |X| Hermit Medium |X| Regular |X| Hermit |X| Medium
|
||||
-|Hermit-medium.otf | | Hermit Medium | | Hermit | | medium | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Bold into Subfamily name
|
||||
+|iAWriterDuospace-Bold.otf | | iA Writer Duospace Bold |X| iA Writer Duospace |X| Bold | | | |
|
||||
-|iAWriterDuospace-Bold.otf | | iA Writer Duospace Bold | | iA Writer Duospace Bold | | Regular | | iA Writer Duospace | | Bold
|
||||
#### Limit Subfamiliy to 4 standard styles, put Bold into Subfamily name
|
||||
+|iAWriterDuospace-Bold.ttf | | iA Writer Duospace Bold |X| iA Writer Duospace |X| Bold | | | |
|
||||
-|iAWriterDuospace-Bold.ttf | | iA Writer Duospace Bold | | iA Writer Duospace Bold | | Regular | | iA Writer Duospace | | Bold
|
||||
#### Limit Subfamiliy to 4 standard styles, put Bold into Subfamily name
|
||||
+|iAWriterDuospace-BoldItalic.otf | | iA Writer Duospace Bold Italic |X| iA Writer Duospace |X| Bold Italic | | |X|
|
||||
-|iAWriterDuospace-BoldItalic.otf | | iA Writer Duospace BoldItalic | | iA Writer Duospace Bold | | Italic | | iA Writer Duospace | | BoldItalic
|
||||
#### Limit Subfamiliy to 4 standard styles, put Bold into Subfamily name
|
||||
+|iAWriterDuospace-BoldItalic.ttf | | iA Writer Duospace Bold Italic |X| iA Writer Duospace |X| Bold Italic | | |X|
|
||||
-|iAWriterDuospace-BoldItalic.ttf | | iA Writer Duospace BoldItalic | | iA Writer Duospace Bold | | Italic | | iA Writer Duospace | | BoldItalic
|
||||
#### Ignore naming part Text
|
||||
+|IBMPlexMono-TextItalic.ttf | | IBM Plex Mono Text Italic | | IBM Plex Mono Text | | Italic |X| |X|
|
||||
-|IBMPlexMono-TextItalic.ttf | | IBM Plex Mono Text Italic | | IBM Plex Mono Text | | Italic | | IBM Plex Mono | | Text Italic
|
||||
#### Ignore naming part Text
|
||||
+|IBMPlexMono-Text.ttf | | IBM Plex Mono Text | | IBM Plex Mono Text | | Regular |X| |X|
|
||||
-|IBMPlexMono-Text.ttf | | IBM Plex Mono Text | | IBM Plex Mono Text | | Regular | | IBM Plex Mono | | Text
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-boldoblique.ttf | | Iosevka Bold Oblique | | Iosevka Oblique |X| Bold Italic | | Iosevka | | Bold Oblique
|
||||
-|iosevka-boldoblique.ttf | | Iosevka Bold Oblique | | Iosevka Oblique | | Bold | | Iosevka | | Bold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-boldoblique.ttf | | Iosevka Term Bold Oblique | | Iosevka Term Oblique |X| Bold Italic | | Iosevka Term | | Bold Oblique
|
||||
-|iosevka-term-boldoblique.ttf | | Iosevka Term Bold Oblique | | Iosevka Term Oblique | | Bold | | Iosevka Term | | Bold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-extraboldoblique.ttf | | Iosevka ExtraBold Oblique | | Iosevka ExtraBold Oblique |X| Italic | | Iosevka | | ExtraBold Oblique
|
||||
-|iosevka-extraboldoblique.ttf | | Iosevka Extrabold Oblique | | Iosevka Extrabold Oblique | | Regular | | Iosevka | | Extrabold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-extraboldoblique.ttf | | Iosevka Term ExtraBold Oblique | | Iosevka Term ExtraBold Oblique |X| Italic | | Iosevka Term | | ExtraBold Oblique
|
||||
-|iosevka-term-extraboldoblique.ttf | | Iosevka Term Extrabold Oblique | | Iosevka Term Extrabold Oblique | | Regular | | Iosevka Term | | Extrabold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-extralightoblique.ttf | | Iosevka ExtraLight Oblique | | Iosevka ExtraLight Oblique |X| Italic | | Iosevka | | ExtraLight Oblique
|
||||
-|iosevka-extralightoblique.ttf | | Iosevka Extralight Oblique | | Iosevka Extralight Oblique | | Regular | | Iosevka | | Extralight Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-extralightoblique.ttf | | Iosevka Term ExtraLight Oblique | | Iosevka Term ExtraLight Obliqu |X| Italic | | Iosevka Term | | ExtraLight Oblique
|
||||
-|iosevka-term-extralightoblique.ttf | | Iosevka Term Extralight Oblique | | Iosevka Term XLtObl | | Regular | | Iosevka Term | | Extralight Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-heavyoblique.ttf | | Iosevka Heavy Oblique | | Iosevka Heavy Oblique |X| Italic | | Iosevka | | Heavy Oblique
|
||||
-|iosevka-heavyoblique.ttf | | Iosevka Heavy Oblique | | Iosevka Heavy Oblique | | Regular | | Iosevka | | Heavy Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-heavyoblique.ttf | | Iosevka Term Heavy Oblique | | Iosevka Term Heavy Oblique |X| Italic | | Iosevka Term | | Heavy Oblique
|
||||
-|iosevka-term-heavyoblique.ttf | | Iosevka Term Heavy Oblique | | Iosevka Term Heavy Oblique | | Regular | | Iosevka Term | | Heavy Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-lightoblique.ttf | | Iosevka Light Oblique | | Iosevka Light Oblique |X| Italic | | Iosevka | | Light Oblique
|
||||
-|iosevka-lightoblique.ttf | | Iosevka Light Oblique | | Iosevka Light Oblique | | Regular | | Iosevka | | Light Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-lightoblique.ttf | | Iosevka Term Light Oblique | | Iosevka Term Light Oblique |X| Italic | | Iosevka Term | | Light Oblique
|
||||
-|iosevka-term-lightoblique.ttf | | Iosevka Term Light Oblique | | Iosevka Term Light Oblique | | Regular | | Iosevka Term | | Light Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-mediumoblique.ttf | | Iosevka Medium Oblique | | Iosevka Medium Oblique |X| Italic | | Iosevka | | Medium Oblique
|
||||
-|iosevka-mediumoblique.ttf | | Iosevka Medium Oblique | | Iosevka Medium Oblique | | Regular | | Iosevka | | Medium Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-mediumoblique.ttf | | Iosevka Term Medium Oblique | | Iosevka Term Medium Oblique |X| Italic | | Iosevka Term | | Medium Oblique
|
||||
-|iosevka-term-mediumoblique.ttf | | Iosevka Term Medium Oblique | | Iosevka Term Medium Oblique | | Regular | | Iosevka Term | | Medium Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-oblique.ttf | | Iosevka Oblique | | Iosevka Oblique |X| Italic | | Iosevka | | Oblique
|
||||
-|iosevka-oblique.ttf | | Iosevka Oblique | | Iosevka Oblique | | Regular | | Iosevka | | Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-oblique.ttf | | Iosevka Term Oblique | | Iosevka Term Oblique |X| Italic | | Iosevka Term | | Oblique
|
||||
-|iosevka-term-oblique.ttf | | Iosevka Term Oblique | | Iosevka Term Oblique | | Regular | | Iosevka Term | | Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-semiboldoblique.ttf | | Iosevka SemiBold Oblique | | Iosevka SemiBold Oblique |X| Italic | | Iosevka | | SemiBold Oblique
|
||||
-|iosevka-semiboldoblique.ttf | | Iosevka Semibold Oblique | | Iosevka Semibold Oblique | | Regular | | Iosevka | | Semibold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-semiboldoblique.ttf | | Iosevka Term SemiBold Oblique | | Iosevka Term SemiBold Oblique |X| Italic | | Iosevka Term | | SemiBold Oblique
|
||||
-|iosevka-term-semiboldoblique.ttf | | Iosevka Term Semibold Oblique | | Iosevka Term Semibold Oblique | | Regular | | Iosevka Term | | Semibold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-term-thinoblique.ttf | | Iosevka Term Thin Oblique | | Iosevka Term Thin Oblique |X| Italic | | Iosevka Term | | Thin Oblique
|
||||
-|iosevka-term-thinoblique.ttf | | Iosevka Term Thin Oblique | | Iosevka Term Thin Oblique | | Regular | | Iosevka Term | | Thin Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead (and mark it Italic so applications know it's slanted) & Keep the grouping in Typographic Family
|
||||
+|iosevka-thinoblique.ttf | | Iosevka Thin Oblique | | Iosevka Thin Oblique |X| Italic | | Iosevka | | Thin Oblique
|
||||
-|iosevka-thinoblique.ttf | | Iosevka Thin Oblique | | Iosevka Thin Oblique | | Regular | | Iosevka | | Thin Oblique
|
||||
#### Do we really base on the VF (variable font) We can not create VF. This makes no sense.
|
||||
+|Lilex-VF.ttf |X| Lilex VF |X| Lilex VF | | Regular | | | |
|
||||
-|Lilex-VF.ttf | | Lilex Regular | | Lilex | | Regular | | | |
|
||||
#### Limit Subfamiliy to 4 standard styles, put Retina into Family instead
|
||||
+|Monoid-Retina.ttf | | Monoid Retina |X| Monoid Retina |X| Regular |X| Monoid |X| Retina
|
||||
-|Monoid-Retina.ttf | | Monoid Retina | | Monoid | | Retina | | | |
|
||||
#### Remove nonstandard BoldItalic typographic style
|
||||
+|mononoki-BoldItalic.ttf | | Mononoki Bold Italic | | Mononoki | | Bold Italic | | |X|
|
||||
-|mononoki-BoldItalic.ttf | | mononoki Bold Italic | | mononoki | | Bold Italic | | | | BoldItalic
|
||||
#### They say SemiBold is the same as Light Bold, we can not generalize this and make SemiBold self standing
|
||||
+|overpass-mono-semibold.otf | | Overpass Mono SemiBold |X| Overpass Mono SemiBold |X| Regular | | Overpass Mono | | SemiBold
|
||||
-|overpass-mono-semibold.otf | | Overpass Mono SemiBold | | Overpass Mono Light | | Bold | | Overpass Mono | | SemiBold
|
||||
#### They say SemiBold is the same as Light Bold, we can not generalize this and make SemiBold self standing
|
||||
+|overpass-semibold.otf | | Overpass SemiBold |X| Overpass SemiBold |X| Regular | | Overpass | | SemiBold
|
||||
-|overpass-semibold.otf | | Overpass SemiBold | | Overpass Light | | Bold | | Overpass | | SemiBold
|
||||
#### Nonstandard font naming: fullname shall be same as familyname plus more
|
||||
+|ProFontIIx.ttf | | ProFont IIx |X| ProFont IIx | | Regular | | | |
|
||||
-|ProFontIIx.ttf | | ProFont IIx | | ProFontIIx | | Regular | | | |
|
||||
#### We are fine here (just list with exclamation mark because it is a potentially problematic case)
|
||||
!|ProFontWindows.ttf | | ProFontWindows | | ProFontWindows | | Regular | | | |
|
||||
|ProFontWindows.ttf | | ProFontWindows | | ProFontWindows | | Regular | | | |
|
||||
#### No mention of TT in file name
|
||||
+|ProggyCleanCE.ttf |X| ProggyClean CE |X| ProggyClean CE | | Regular | | | |
|
||||
-|ProggyCleanCE.ttf | | ProggyCleanTT CE | | ProggyCleanTT CE | | Regular | | | |
|
||||
#### No mention of TT in file name
|
||||
!|ProggyClean.ttf |X| ProggyClean |X| ProggyClean | | Regular | | | |
|
||||
-|ProggyClean.ttf | | ProggyCleanTT | | ProggyCleanTT | | Regular | | | |
|
||||
#### No mention of TT in file name
|
||||
+|ProggyCleanSZ.ttf |X| ProggyClean SZ |X| ProggyClean SZ | | Regular | | | |
|
||||
-|ProggyCleanSZ.ttf | | ProggyCleanTTSZ | | ProggyCleanTTSZ | | Regular | | | |
|
||||
#### They put one name part in parens
|
||||
+|TerminusTTF-Bold Italic-4.40.1.ttf |X| Terminus TTF Bold Italic |X| Terminus TTF | | Bold Italic | | | |
|
||||
-|TerminusTTF-Bold Italic-4.40.1.ttf | | Terminus (TTF) Bold Italic | | Terminus (TTF) | | Bold Italic | | | |
|
||||
#### They put one name part in parens
|
||||
+|TerminusTTF-Bold-4.40.1.ttf |X| Terminus TTF Bold |X| Terminus TTF | | Bold | | | |
|
||||
-|TerminusTTF-Bold-4.40.1.ttf | | Terminus (TTF) Bold | | Terminus (TTF) | | Bold | | | |
|
||||
#### They put one name part in parens
|
||||
+|TerminusTTF-Italic-4.40.1.ttf |X| Terminus TTF Italic |X| Terminus TTF | | Italic | | | |
|
||||
-|TerminusTTF-Italic-4.40.1.ttf | | Terminus (TTF) Italic | | Terminus (TTF) | | Italic | | | |
|
||||
#### They put one name part in parens
|
||||
+|TerminusTTF-4.40.1.ttf |X| Terminus TTF |X| Terminus TTF |X| Regular | | | |
|
||||
-|TerminusTTF-4.40.1.ttf | | Terminus (TTF) | | Terminus (TTF) | | Medium | | | |
|
||||
#### Ubuntu Condensed should be grouped with Ubuntu, that they didn't is an error?
|
||||
+|Ubuntu-C.ttf | | Ubuntu Condensed | | Ubuntu Condensed | | Regular |X| Ubuntu |X| Condensed
|
||||
-|Ubuntu-C.ttf | | Ubuntu Condensed | | Ubuntu Condensed | | Regular | | Ubuntu Condensed | | Regular
|
||||
#### They say Medium is the same as Light Bold, we can not generalize this and make Medium self standing
|
||||
+|Ubuntu-MI.ttf | | Ubuntu Medium Italic |X| Ubuntu Medium |X| Italic | | Ubuntu | | Medium Italic
|
||||
-|Ubuntu-MI.ttf | | Ubuntu Medium Italic | | Ubuntu Light | | Bold Italic | | Ubuntu | | Medium Italic
|
||||
#### They say Medium is the same as Light Bold, we can not generalize this and make Medium self standing
|
||||
+|Ubuntu-M.ttf | | Ubuntu Medium |X| Ubuntu Medium |X| Regular | | Ubuntu | | Medium
|
||||
-|Ubuntu-M.ttf | | Ubuntu Medium | | Ubuntu Light | | Bold | | Ubuntu | | Medium
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead
|
||||
+|VictorMono-ExtraLightOblique.ttf | | Victor Mono ExtraLight Oblique |X| Victor Mono ExtraLight Oblique | | Italic | | Victor Mono | | ExtraLight Oblique
|
||||
-|VictorMono-ExtraLightOblique.ttf | | Victor Mono ExtraLight Oblique | | Victor Mono ExtraLight | | Italic | | Victor Mono | | ExtraLight Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead
|
||||
+|VictorMono-LightOblique.ttf | | Victor Mono Light Oblique |X| Victor Mono Light Oblique | | Italic | | Victor Mono | | Light Oblique
|
||||
-|VictorMono-LightOblique.ttf | | Victor Mono Light Oblique | | Victor Mono Light | | Italic | | Victor Mono | | Light Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead
|
||||
+|VictorMono-MediumOblique.ttf | | Victor Mono Medium Oblique |X| Victor Mono Medium Oblique | | Italic | | Victor Mono | | Medium Oblique
|
||||
-|VictorMono-MediumOblique.ttf | | Victor Mono Medium Oblique | | Victor Mono Medium | | Italic | | Victor Mono | | Medium Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead
|
||||
+|VictorMono-SemiBoldOblique.ttf | | Victor Mono SemiBold Oblique |X| Victor Mono SemiBold Oblique | | Italic | | Victor Mono | | SemiBold Oblique
|
||||
-|VictorMono-SemiBoldOblique.ttf | | Victor Mono SemiBold Oblique | | Victor Mono SemiBold | | Italic | | Victor Mono | | SemiBold Oblique
|
||||
#### Limit Subfamiliy to 4 standard styles, put Oblique into Family instead
|
||||
+|VictorMono-ThinOblique.ttf | | Victor Mono Thin Oblique |X| Victor Mono Thin Oblique | | Italic | | Victor Mono | | Thin Oblique
|
||||
-|VictorMono-ThinOblique.ttf | | Victor Mono Thin Oblique | | Victor Mono Thin | | Italic | | Victor Mono | | Thin Oblique
|
195
bin/scripts/name_parser/name_parser_test2
Normal file
195
bin/scripts/name_parser/name_parser_test2
Normal file
|
@ -0,0 +1,195 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os.path
|
||||
import glob
|
||||
import subprocess
|
||||
import fontforge
|
||||
|
||||
###### Some helpers
|
||||
|
||||
def get_sfnt_dict(font):
|
||||
"""Extract SFNT table as nice dict"""
|
||||
d = []
|
||||
for i, el in enumerate(font.sfnt_names):
|
||||
d += [(el[1], el[2])]
|
||||
return dict(d)
|
||||
|
||||
def extract_sfnt_data(sfnt):
|
||||
"""Get the usual names out of the SFNT table"""
|
||||
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 ''
|
||||
return (sfnt_full, sfnt_fam, sfnt_subfam, sfnt_pfam, sfnt_psubfam)
|
||||
|
||||
def format_names(header, *stuff):
|
||||
"""Unify outputs (with header)"""
|
||||
f = '{:1.1}|{:50.50} |{:1.1}| {:65.65} |{:1.1}| {:55.55} |{:1.1}| {:30.30} |{:1.1}| {:40.40} |{:1.1}| {:.40}'
|
||||
if header:
|
||||
d = '------------------------------------------------------------'
|
||||
return f.format(*stuff) + '\n' + f.format('', d, d, d, d, d, d, d, d, d, d, d)
|
||||
return f.format(*stuff).rstrip()
|
||||
|
||||
def lenient_cmp(s1, s2, allow_shuffle_all):
|
||||
"""Compare two font name (parts) but be a bit lenient ;->"""
|
||||
# We do not care about:
|
||||
# - Case
|
||||
# - "Display" vs "Disp" (in Noto)
|
||||
# Allow for "IBM 3278" name
|
||||
s = [ s1, s2 ]
|
||||
for i in range(2):
|
||||
# Usually given transform from 'their' to 'our' style
|
||||
s[i] = s[i].lower()
|
||||
s[i] = re.sub(r'\bdisp\b', 'display', s[i]) # Noto
|
||||
s[i] = s[i].replace('ibm 3270', '3270') # 3270
|
||||
s[i] = s[i].replace('3270-', '3270 ') # 3270
|
||||
s[i] = s[i].replace('lekton-', 'lekton ') # Lekton
|
||||
s[i] = s[i].replace('semi-narrow', 'seminarrow') # 3270
|
||||
s[i] = s[i].replace('bolditalic', 'bold italic')
|
||||
s[i] = re.sub(r'\bfor\b', '', s[i]) # Meslo, Monofur
|
||||
s[i] = re.sub(r'\bpowerline\b', '', s[i]) # Meslo, Monofur
|
||||
s[i] = s[i].replace('fira mono', 'fura mono') # Obviously someone forgot to rename the fonts in Fira/
|
||||
s[i] = s[i].replace('aurulentsansmono-', 'aurulent sans mono ') # Aurulent fullname oddity
|
||||
s[i] = s[i].replace('mononoki-', 'mononoki ') # Mononoki has somtimes a dash
|
||||
s[i] = re.sub(r'\br\b', 'regular', s[i]) # Nonstandard style in Agave
|
||||
s[i] = re.sub(r'(bitstream vera sans mono.*) oblique', r'\1 italic', s[i]) # They call it Oblique but the filename says Italic
|
||||
s[i] = re.sub(r'gohufont (uni-)?(11|14)', 'gohufont', s[i]) # They put the 'name' into the subfamily/weight
|
||||
s[i] = s[i].replace('xltobl', 'extralight oblique') # Iosevka goes inventing names
|
||||
s[i] = re.sub(r'proggyclean(?!TT)( ?)', 'proggycleantt\1', s[i]) # ProggyClean has no TT in filename
|
||||
|
||||
s[i] = re.sub(r' +', ' ', s[i]).strip()
|
||||
|
||||
p = []
|
||||
for e in s:
|
||||
parts = e.split(' ')
|
||||
if not allow_shuffle_all and len(parts) > 2:
|
||||
tail = parts[1:]
|
||||
tail.sort()
|
||||
parts = [parts[0]] + tail
|
||||
elif len(parts) > 1:
|
||||
parts.sort()
|
||||
p.append(' '.join(parts))
|
||||
return p[0] == p[1]
|
||||
|
||||
###### Let's go!
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage: {} font_name [font_name ...]\n'.format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
font_patcher = os.path.realpath(os.path.dirname(os.path.realpath(sys.argv[0]))+'/../../../font-patcher')
|
||||
|
||||
existing_font = glob.glob('*.[ot]tf')
|
||||
if len(existing_font):
|
||||
sys.exit('Would overwrite any existing *.ttf and *.otf, bailing out (remove them first)')
|
||||
|
||||
try:
|
||||
with open(sys.argv[0] + '.known_issues', 'r') as f:
|
||||
known_issues = f.read().splitlines()
|
||||
# known_issues = [line.rstrip() for line in known_issues]
|
||||
print('Found {:.0f} known issues'.format(len(known_issues) / 4)) # approx ;)
|
||||
except OSError:
|
||||
print('Can not open known_issues file')
|
||||
known_issues = []
|
||||
new_issues = open(sys.argv[0] + '.known_issues.new', 'w')
|
||||
|
||||
print('Examining {} font files'.format(len(sys.argv) - 1))
|
||||
all_files = 0
|
||||
issue_files = 0
|
||||
known_files = 0
|
||||
|
||||
print(format_names(True, '', 'Filename', '', 'Fullname', '', 'Family', '', 'Subfamily', '', 'Typogr. Family', '', 'Typogr. Subfamily'))
|
||||
|
||||
for filename in sys.argv[1:]:
|
||||
data = []
|
||||
fullfile = os.path.basename(filename)
|
||||
fname = os.path.splitext(fullfile)[0]
|
||||
if fname == 'NotoColorEmoji':
|
||||
continue # font is not patchable
|
||||
if fname in [ 'iosevka-heavyoblique', 'iosevka-term-heavyoblique', 'iosevka-mediumoblique', 'Lilex-VF' ]:
|
||||
continue # Patch resultant font not openable
|
||||
log = open("log", "w")
|
||||
log.write(filename)
|
||||
log.close()
|
||||
|
||||
for option in ['--parser', '']:
|
||||
cmd = ['fontforge', '--script', font_patcher, '--powerline', option, filename ]
|
||||
cmd = [ c for c in cmd if len(c) ]
|
||||
ff = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, encoding='utf8')
|
||||
#ff = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
|
||||
if ff.returncode:
|
||||
print("\nERROR running command:\n {}\n\n{}".format(' '.join(cmd), ''))#ff.stdout))
|
||||
sys.exit(1)
|
||||
new_font = glob.glob('*.[ot]tf')
|
||||
font = fontforge.open(new_font[0], 1)
|
||||
sfnt = get_sfnt_dict(font)
|
||||
font.close()
|
||||
os.system('rm -f *.[ot]tf')
|
||||
(sfnt_full, sfnt_fam, sfnt_subfam, sfnt_pfam, sfnt_psubfam) = extract_sfnt_data(sfnt)
|
||||
|
||||
data.append(( os.path.basename(new_font[0]), sfnt_full, sfnt_fam, sfnt_subfam, sfnt_pfam, sfnt_psubfam ))
|
||||
|
||||
all_files += 1
|
||||
|
||||
t1 = not lenient_cmp(data[0][1], data[1][1], False)
|
||||
t2 = not lenient_cmp(data[0][2], data[1][2], False)
|
||||
t3 = not lenient_cmp(data[0][3], data[1][3], True)
|
||||
t4 = not lenient_cmp(data[0][4], data[1][4], False)
|
||||
t5 = not lenient_cmp(data[0][5], data[1][5], True)
|
||||
|
||||
# Lenience: Allow for dropping unneeded prefered stuff:
|
||||
# New (sub)family is same as old preferred sub(family)
|
||||
if t4 and data[0][4] == '' and data[1][4].lower() == data[0][2].lower():
|
||||
t4 = False
|
||||
if t5 and data[0][5] == '' and data[1][5].lower() == data[0][3].lower():
|
||||
t5 = False
|
||||
|
||||
if t1 or t2 or t3 or t4 or t5:
|
||||
m1 = '+'; m2 = '-'
|
||||
else:
|
||||
m1 = ''; m2 = ''
|
||||
t1_ = 'X' if t1 else ''
|
||||
t2_ = 'X' if t2 else ''
|
||||
t3_ = 'X' if t3 else ''
|
||||
t4_ = 'X' if t4 else ''
|
||||
t5_ = 'X' if t5 else ''
|
||||
|
||||
o1 = format_names(False, m1, data[0][0], t1_, data[0][1], t2_, data[0][2], t3_, data[0][3], t4_, data[0][4], t5_, data[0][5])
|
||||
o2 = format_names(False, m2, data[1][0], '', data[1][1], '', data[1][2], '', data[1][3], '', data[1][4], '', data[1][5])
|
||||
|
||||
if len(m1):
|
||||
issue_files += 1
|
||||
font = fontforge.open(filename, 1)
|
||||
sfnt = get_sfnt_dict(font)
|
||||
font.close()
|
||||
(sfnt_full, sfnt_fam, sfnt_subfam, sfnt_pfam, sfnt_psubfam) = extract_sfnt_data(sfnt)
|
||||
o3 = format_names(False, '>', os.path.basename(filename), '', sfnt_full, '', sfnt_fam, '', sfnt_subfam, '', sfnt_pfam, '', sfnt_psubfam)
|
||||
if not o1 in known_issues or not o2 in known_issues:
|
||||
new_issues.writelines(['#### AUTOGENERATED\n', o3 + '\n', o1 + '\n', o2 + '\n'])
|
||||
else:
|
||||
known_files += 1
|
||||
idx = known_issues.index(o1) - 2 # should be the index of the explanation line
|
||||
if known_issues[idx][0] != '#':
|
||||
sys.exit('Problem with known issues file, line', known_issues.index(o1))
|
||||
new_issues.writelines([known_issues[idx] + '\n', o3 + '\n', o1 + '\n', o2 + '\n'])
|
||||
# remove known_issue triplet
|
||||
known_issues.pop(idx)
|
||||
known_issues.pop(idx)
|
||||
known_issues.pop(idx)
|
||||
known_issues.pop(idx)
|
||||
|
||||
print(o1, o2, sep='\n')
|
||||
|
||||
print('Fonts with different name rendering: {}/{} ({}/{} are in known_issues)'.format(issue_files, all_files, known_files, issue_files))
|
||||
|
||||
if len(known_issues) > 0:
|
||||
print('There are {} lines not needed in known_issues, appending commented out in new known_issues'.format(len(known_issues)))
|
||||
new_issues.write('\n#### The following lines are not needed anymore\n\n')
|
||||
for l in known_issues:
|
||||
new_issues.writelines([' ', l, '\n'])
|
||||
|
||||
new_issues.close()
|
2272
bin/scripts/name_parser/name_parser_test2.known_issues
Normal file
2272
bin/scripts/name_parser/name_parser_test2.known_issues
Normal file
File diff suppressed because it is too large
Load diff
48
bin/scripts/name_parser/query_names
Normal file
48
bin/scripts/name_parser/query_names
Normal file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
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 }
|
||||
|
||||
def format_names(header, *stuff):
|
||||
"""Unify outputs (with header)"""
|
||||
f = '{:1.1}|{:50.50} |{:1.1}| {:50.50} |{:1.1}| {:30.30} |{:1.1}| {:30.30} |{:1.1}| {:30.30} |{:1.1}| {:.30}'
|
||||
if header:
|
||||
d = '------------------------------------------------------------'
|
||||
return f.format(*stuff) + '\n' + f.format('', d, d, d, d, d, d, d, d, d, d, d)
|
||||
return f.format(*stuff).rstrip()
|
||||
|
||||
###### 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))
|
||||
|
||||
print(format_names(True, '', 'Filename', '', 'Fullname', '', 'Family', '', 'Subfamily', '', 'Typogr. Family', '', 'Typogr. Subfamily'))
|
||||
|
||||
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)
|
||||
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 ''
|
||||
|
||||
o2 = format_names(False, '', fullfile, '', sfnt_full, '', sfnt_fam, '', sfnt_subfam, '', sfnt_pfam, '', sfnt_psubfam)
|
||||
|
||||
print(o2)
|
16
bin/scripts/name_parser/query_panose
Normal file
16
bin/scripts/name_parser/query_panose
Normal file
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import fontforge
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: {} font_name\n".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
font = fontforge.open(sys.argv[1])
|
||||
|
||||
panose = list(font.os2_panose)
|
||||
print("Panose 4 = {} in {}".format(panose[3], font.fullname))
|
||||
|
||||
font.close()
|
35
bin/scripts/name_parser/query_sftn
Normal file
35
bin/scripts/name_parser/query_sftn
Normal file
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import fontforge
|
||||
import sys
|
||||
|
||||
def get_sfnt_dict(font):
|
||||
"""Extract SFNT table as nice dict"""
|
||||
return { k: v for l, k, v in font.sfnt_names }
|
||||
|
||||
if len(sys.argv) < 2 or len(sys.argv) > 3:
|
||||
print("Usage: {} [<sfnt-name>] font_name\n".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
fname = sys.argv[1]
|
||||
sname = None
|
||||
else:
|
||||
fname = sys.argv[2]
|
||||
sname = sys.argv[1]
|
||||
|
||||
font = fontforge.open(fname)
|
||||
sfnt = get_sfnt_dict(font)
|
||||
font.close()
|
||||
|
||||
if sname:
|
||||
for key in sname.split(','):
|
||||
if key in sfnt:
|
||||
print("SFNT {:20.20} is {:80.80}".format(key, '\'' + sfnt[key] + '\''));
|
||||
else:
|
||||
print("SFNT {:20.20} is not set".format(key));
|
||||
else:
|
||||
for k in sfnt:
|
||||
print("{:20.20} {:80.80}".format(k, sfnt[k]))
|
||||
|
26
bin/scripts/name_parser/query_version
Normal file
26
bin/scripts/name_parser/query_version
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf8
|
||||
|
||||
import fontforge
|
||||
import sys
|
||||
|
||||
def get_sfnt_dict(font):
|
||||
"""Extract SFNT table as nice dict"""
|
||||
return { k: v for l, k, v in font.sfnt_names }
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: {} font_name\n".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
font = fontforge.open(sys.argv[1])
|
||||
sfnt = get_sfnt_dict(font)
|
||||
|
||||
print("Version is '{}'".format(font.version));
|
||||
print("CID Version is '{}'".format(font.cidversion));
|
||||
print("SFNT Revision is '{}'".format(font.sfntRevision));
|
||||
if "Version" in sfnt:
|
||||
print("SFNT ['Version'] is '{}'".format(sfnt["Version"]));
|
||||
else:
|
||||
print("SFNT ['Version'] is not set".format(sys.argv[1]));
|
||||
|
||||
font.close()
|
14
bin/scripts/name_parser/tags
Normal file
14
bin/scripts/name_parser/tags
Normal file
|
@ -0,0 +1,14 @@
|
|||
Add weight/style to family [1]
|
||||
Use only Regular/Bold/Italic in SubFamily [2]
|
||||
Classify Medium as own weight and not Bold [3]
|
||||
Change regular-equivalent name to Regular [4]
|
||||
Drop unneeded Typogr.Family/Typogr.Style [5]
|
||||
Do not call Semibold Light-Bold [6]
|
||||
Fullname has been missing 'Nerd Font' [7]
|
||||
The fonts name is M+ not Mplus [8]
|
||||
Handle Retina as Weight and not Style [9]
|
||||
Bold and Italic are styles of a basefont [10]
|
||||
Put Oblique into own SubFamily (and mark it as italic) [11]
|
||||
'Term' is missing from Family [12]
|
||||
Bold / Bold-Italic are just a styles of Regular [13]
|
||||
Drop Regular from Style [14]
|
48
font-patcher
48
font-patcher
|
@ -36,6 +36,10 @@ except ImportError:
|
|||
)
|
||||
)
|
||||
|
||||
# This is (for experimenting) far far away...
|
||||
sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])) + '/bin/scripts/name_parser/')
|
||||
from FontnameParser import FontnameParser
|
||||
from FontnameTools import FontnameTools
|
||||
|
||||
class TableHEADWriter:
|
||||
""" Access to the HEAD table without external dependencies """
|
||||
|
@ -306,6 +310,7 @@ class font_patcher:
|
|||
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('--parser', dest='parser', default=False, action='store_true', help='Use alternative method to name patched fonts (experimental)')
|
||||
|
||||
# progress bar arguments - https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse
|
||||
progressbars_group_parser = parser.add_mutually_exclusive_group(required=False)
|
||||
|
@ -425,6 +430,26 @@ class font_patcher:
|
|||
additionalFontNameSuffix += " M"
|
||||
verboseAdditionalFontNameSuffix += " Mono"
|
||||
|
||||
if self.args.parser:
|
||||
use_fullname = True # Usually the fullname is better to parse
|
||||
# Use fullname if it is 'equal' to the fontname
|
||||
use_fullname |= self.sourceFont.fontname.lower() == FontnameTools.postscript_char_filter(self.sourceFont.fullname).lower()
|
||||
# Use fullname for any of these source fonts (that are impossible to disentangle from the fontname, we need the blanks)
|
||||
for hit in [ 'Meslo' ]:
|
||||
use_fullname |= self.sourceFont.fontname.lower().startswith(hit.lower())
|
||||
parser_name = self.sourceFont.fullname if use_fullname else self.sourceFont.fontname
|
||||
# Gohu fontnames hide the weight, but the file names are ok...
|
||||
if parser_name.startswith('Gohu'):
|
||||
parser_name = os.path.splitext(os.path.basename(self.args.font))[0]
|
||||
n = FontnameParser(parser_name)
|
||||
if not n.parse_ok:
|
||||
print("Have only minimal naming information, check resulting name. Maybe omit --parser option")
|
||||
n.drop_for_powerline()
|
||||
n.enable_short_families(True, "Noto")
|
||||
n.set_for_windows(self.args.windows)
|
||||
|
||||
# All the following stuff is ignored in parser-mode
|
||||
|
||||
# basically split the font name around the dash "-" to get the fontname and the style (e.g. Bold)
|
||||
# this does not seem very reliable so only use the style here as a fallback if the font does not
|
||||
# have an internal style defined (in sfnt_names)
|
||||
|
@ -558,15 +583,22 @@ class font_patcher:
|
|||
fullname = replace_font_name(fullname, additionalFontNameReplacements2)
|
||||
fontname = replace_font_name(fontname, additionalFontNameReplacements2)
|
||||
|
||||
# replace any extra whitespace characters:
|
||||
self.sourceFont.familyname = " ".join(familyname.split())
|
||||
self.sourceFont.fullname = " ".join(fullname.split())
|
||||
self.sourceFont.fontname = " ".join(fontname.split())
|
||||
if not self.args.parser:
|
||||
# replace any extra whitespace characters:
|
||||
self.sourceFont.familyname = " ".join(familyname.split())
|
||||
self.sourceFont.fullname = " ".join(fullname.split())
|
||||
self.sourceFont.fontname = " ".join(fontname.split())
|
||||
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Preferred Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Compatible Full'), self.sourceFont.fullname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||
else:
|
||||
fam_suffix = projectNameSingular if not self.args.windows else projectNameAbbreviation
|
||||
fam_suffix += ' Mono' if self.args.single else ''
|
||||
n.inject_suffix(verboseAdditionalFontNameSuffix, additionalFontNameSuffix, fam_suffix)
|
||||
n.rename_font(self.sourceFont)
|
||||
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Preferred Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Family'), self.sourceFont.familyname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('Compatible Full'), self.sourceFont.fullname)
|
||||
self.sourceFont.appendSFNTName(str('English (US)'), str('SubFamily'), subFamily)
|
||||
self.sourceFont.comment = projectInfo
|
||||
self.sourceFont.fontlog = projectInfo
|
||||
|
||||
|
|
Loading…
Reference in a new issue