chore: change batteries

This commit is contained in:
github-actions 2023-05-15 00:29:20 +00:00
parent 2d153f57c0
commit a4d4640f01
14 changed files with 431 additions and 560 deletions

View file

@ -4,32 +4,20 @@
import re
from FontnameTools import FontnameTools
class FontnameParser:
"""Parse a font name and generate all kinds of names"""
def __init__(self, filename, logger):
"""Parse a font filename and store the results"""
self.parse_ok = False
self.use_short_families = (
False,
False,
False,
) # ( camelcase name, short styles, aggressive )
self.keep_regular_in_family = None # None = auto, True, False
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 = ""
self.ps_fontname_suff = ""
self.short_family_suff = ""
self.family_suff = ''
self.ps_fontname_suff = ''
self.short_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.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)
@ -38,25 +26,21 @@ class FontnameParser:
def _make_ps_name(self, n, is_family):
"""Helper to limit font name length in PS names"""
fam = "family " if is_family else ""
fam = 'family ' if is_family else ''
limit = 31 if is_family else 63
if len(n) <= limit:
return n
r = re.search("(.*)(-.*)", n)
r = re.search('(.*)(-.*)', n)
if not r:
new_n = n[:limit]
else:
q = limit - len(r.groups()[1])
if q < 1:
q = 1
self.logger.error(
"====-< Shortening too long PS {}name: Garbage warning".format(fam)
)
self.logger.error('====-< Shortening too long PS {}name: Garbage warning'. format(fam))
new_n = r.groups()[0][:q] + r.groups()[1]
if new_n != n:
self.logger.error(
"====-< Shortening too long PS {}name: {} -> {}".format(fam, n, new_n)
)
self.logger.error('====-< Shortening too long PS {}name: {} -> {}'.format(fam, n, new_n))
return new_n
def _shortened_name(self):
@ -64,7 +48,7 @@ class FontnameParser:
if not self.use_short_families[0]:
return (self.basename, self.rest)
else:
return (FontnameTools.concat(self.basename, self.rest).replace(" ", ""), "")
return (FontnameTools.concat(self.basename, self.rest).replace(' ', ''), '')
def set_keep_regular_in_family(self, keep):
"""Familyname may contain 'Regular' where it should normally be suppressed"""
@ -79,7 +63,7 @@ class FontnameParser:
# is not needed, or rather contraproductive. This can not be detected on a
# font file level but needs to be specified per family from the outside.
# Returns true if setting was successful.
if "Italic" in self.style_token:
if 'Italic' in self.style_token:
self.rename_oblique = True
return not noitalic
self.rename_oblique = not noitalic
@ -92,7 +76,7 @@ class FontnameParser:
def inject_suffix(self, family, ps_fontname, short_family):
"""Add a custom additonal string that shows up in the resulting names"""
self.family_suff = family.strip()
self.ps_fontname_suff = ps_fontname.replace(" ", "")
self.ps_fontname_suff = ps_fontname.replace(' ', '')
self.short_family_suff = short_family.strip()
return self
@ -102,7 +86,7 @@ class FontnameParser:
# prefix is either a string or False/True
if isinstance(prefix, str):
prefix = self._basename.startswith(prefix)
self.use_short_families = (camelcase_name, prefix, aggressive)
self.use_short_families = ( camelcase_name, prefix, aggressive )
return self
def add_name_substitution_table(self, table):
@ -113,29 +97,27 @@ class FontnameParser:
self.basename = self._basename
self.rest = self._rest
for regex, replacement in self.name_subst:
base_and_rest = self.basename + (" " + self.rest if len(self.rest) else "")
base_and_rest = self.basename + (' ' + self.rest if len(self.rest) else '')
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).rstrip()
self.rest = self.rest[-(i + 1) :].lstrip()
self.rest = self.rest[-(i+1):].lstrip()
else:
self.basename = m.expand(replacement) + self.basename[len(m.group(0)) :]
self.basename = m.expand(replacement) + self.basename[len(m.group(0)):]
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")
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":
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
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:
@ -166,26 +148,20 @@ class FontnameParser:
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
)
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
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")
styles.remove('Regular')
# For naming purposes we want Oblique to be part of the styles
(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], self.use_short_families[2]
)
return FontnameTools.concat(
name, rest, self.other_token, self.short_family_suff, 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)"""
@ -196,12 +172,10 @@ class FontnameParser:
if self.use_short_families[1]:
styles = FontnameTools.short_styles(styles, self.use_short_families[2])
weights = FontnameTools.short_styles(weights, self.use_short_families[2])
fam = FontnameTools.camel_casify(
FontnameTools.concat(name, rest, self.other_token, self.ps_fontname_suff)
)
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:
sub = "-" + sub
sub = '-' + sub
fam = FontnameTools.postscript_char_filter(fam)
sub = FontnameTools.postscript_char_filter(sub)
return self._make_ps_name(fam + sub, False)
@ -212,7 +186,7 @@ class FontnameParser:
pfn = FontnameTools.concat(name, rest, self.other_token, self.family_suff)
if self.suppress_preferred_if_identical and pfn == self.family():
# Do not set if identical to ID 1
return ""
return ''
return pfn
def preferred_styles(self):
@ -224,7 +198,7 @@ class FontnameParser:
pfs = FontnameTools.concat(weights, styles)
if self.suppress_preferred_if_identical and pfs == self.subfamily():
# Do not set if identical to ID 2
return ""
return ''
return pfs
def family(self):
@ -237,8 +211,8 @@ class FontnameParser:
if not self.rename_oblique:
(weights, styles) = FontnameTools.make_oblique_style(weights, [])
if self.use_short_families[1]:
[other, weights] = FontnameTools.short_styles([other, weights], aggressive)
weights = [w if w != "Oblique" else "Obl" for w in weights]
[ other, weights ] = FontnameTools.short_styles([ other, weights ], aggressive)
weights = [ w if w != 'Oblique' else 'Obl' for w in weights ]
return FontnameTools.concat(name, rest, other, self.short_family_suff, weights)
def subfamily(self):
@ -248,11 +222,11 @@ class FontnameParser:
if not self.rename_oblique:
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
if len(styles) == 0:
if "Oblique" in weights:
return FontnameTools.concat(styles, "Italic")
return "Regular"
if "Oblique" in weights and not "Italic" in styles:
return FontnameTools.concat(styles, "Italic")
if 'Oblique' in weights:
return FontnameTools.concat(styles, 'Italic')
return 'Regular'
if 'Oblique' in weights and not 'Italic' in styles:
return FontnameTools.concat(styles, 'Italic')
return FontnameTools.concat(styles)
def ps_familyname(self):
@ -265,45 +239,33 @@ class FontnameParser:
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
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
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:
if 'Bold' in self.style_token:
b |= BOLD
# Ignore Italic if we have Oblique
if "Oblique" in self.weight_token:
if 'Oblique' in self.weight_token:
b |= OBLIQUE
elif "Italic" in self.style_token:
elif 'Italic' in self.style_token:
b |= ITALIC
# Regular is just the basic weight
if len(self.weight_token) == 0:
if len(self.weight_token) == 0 and not b & (ITALIC | BOLD | OBLIQUE):
b |= REGULAR
b |= WWS # We assert this by our naming process
b |= WWS # We assert this by our naming process
return b
def checklen(self, max_len, entry_id, name):
"""Check the length of a name string and report violations"""
if len(name) <= max_len:
self.logger.debug(
"=====> {:18} ok ({:2} <={:2}): {}".format(
entry_id, len(name), max_len, name
)
)
self.logger.debug('=====> {:18} ok ({:2} <={:2}): {}'.format(entry_id, len(name), max_len, name))
else:
self.logger.error(
"====-< {:18} too long ({:2} > {:2}): {}".format(
entry_id, len(name), max_len, name
)
)
self.logger.error('====-< {:18} too long ({:2} > {:2}): {}'.format(entry_id, len(name), max_len, name))
return name
def rename_font(self, font):
@ -339,78 +301,32 @@ class FontnameParser:
# and it is actually embedded as empty string, but empty strings are not
# shown if you query the sfnt_names *rolleyes*
version_tag = ""
version_tag = ''
sfnt_list = []
TO_DEL = [
"Family",
"SubFamily",
"Fullname",
"PostScriptName",
"Preferred Family",
"Preferred Styles",
"Compatible Full",
"WWS Family",
"WWS Subfamily",
"UniqueID",
"CID findfont Name",
]
TO_DEL = ['Family', 'SubFamily', 'Fullname', 'PostScriptName', 'Preferred Family',
'Preferred Styles', 'Compatible Full', 'WWS Family', 'WWS Subfamily',
'UniqueID', 'CID findfont Name']
# Remove these entries in all languages and add (at least the vital ones) some
# back, but only as 'English (US)'. This makes sure we do not leave contradicting
# names over different languages.
for l, k, v in list(font.sfnt_names):
if not k in TO_DEL:
sfnt_list += [(l, k, v)]
if k == "Version" and l == "English (US)":
version_tag = " " + v.split()[-1]
sfnt_list += [( l, k, v )]
if k == 'Version' and l == 'English (US)':
version_tag = ' ' + v.split()[-1]
sfnt_list += [
(
"English (US)",
"Family",
self.checklen(31, "Family (ID 1)", self.family()),
)
] # 1
sfnt_list += [
(
"English (US)",
"SubFamily",
self.checklen(31, "SubFamily (ID 2)", self.subfamily()),
)
] # 2
sfnt_list += [("English (US)", "UniqueID", self.fullname() + version_tag)] # 3
sfnt_list += [
(
"English (US)",
"Fullname",
self.checklen(63, "Fullname (ID 4)", self.fullname()),
)
] # 4
sfnt_list += [
(
"English (US)",
"PostScriptName",
self.checklen(63, "PSN (ID 6)", self.psname()),
)
] # 6
sfnt_list += [( 'English (US)', 'Family', self.checklen(31, 'Family (ID 1)', self.family()) )] # 1
sfnt_list += [( 'English (US)', 'SubFamily', self.checklen(31, 'SubFamily (ID 2)', self.subfamily()) )] # 2
sfnt_list += [( 'English (US)', 'UniqueID', self.fullname() + version_tag )] # 3
sfnt_list += [( 'English (US)', 'Fullname', self.checklen(63, 'Fullname (ID 4)', self.fullname()) )] # 4
sfnt_list += [( 'English (US)', 'PostScriptName', self.checklen(63, 'PSN (ID 6)', self.psname()) )] # 6
p_fam = self.preferred_family()
if len(p_fam):
sfnt_list += [
(
"English (US)",
"Preferred Family",
self.checklen(31, "PrefFamily (ID 16)", p_fam),
)
] # 16
sfnt_list += [( 'English (US)', 'Preferred Family', self.checklen(31, 'PrefFamily (ID 16)', p_fam) )] # 16
p_sty = self.preferred_styles()
if len(p_sty):
sfnt_list += [
(
"English (US)",
"Preferred Styles",
self.checklen(31, "PrefStyles (ID 17)", p_sty),
)
] # 17
sfnt_list += [( 'English (US)', 'Preferred Styles', self.checklen(31, 'PrefStyles (ID 17)', p_sty) )] # 17
font.sfnt_names = tuple(sfnt_list)

View file

@ -4,7 +4,6 @@
import re
import sys
class FontnameTools:
"""Deconstruct a font filename to get standardized name parts"""
@ -16,32 +15,32 @@ class FontnameTools:
@staticmethod
def camel_casify(word):
"""Remove blanks and use CamelCase for the new word"""
return "".join(map(FontnameTools.front_upper, word.split(" ")))
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)
'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)
parts = re.split('(?<=[a-z0-9])(?=[A-Z])', word)
if len(prefix):
parts.insert(0, prefix)
return " ".join(parts)
return ' '.join(parts)
@staticmethod
def drop_empty(l):
@ -57,7 +56,7 @@ class FontnameTools:
all.append(thing)
else:
all += thing
return " ".join(FontnameTools.drop_empty(all))
return ' '.join(FontnameTools.drop_empty(all))
@staticmethod
def unify_style_names(style_name):
@ -65,20 +64,20 @@ class FontnameTools:
known_names = {
# Source of the table is the current sourcefonts
# Left side needs to be lower case
"-": "",
"book": "",
"text": "",
"ce": "CE",
'-': '',
'book': '',
'text': '',
'ce': 'CE',
#'semibold': 'Demi',
"ob": "Oblique",
"it": "Italic",
"i": "Italic",
"b": "Bold",
"normal": "Regular",
"c": "Condensed",
"r": "Regular",
"m": "Medium",
"l": "Light",
'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()]
@ -89,7 +88,7 @@ class FontnameTools:
"""Find an entry in a list of dicts, return entry and in which list it was"""
for i, d in enumerate(dicts):
if key in d:
return (d[key], i)
return ( d[key], i )
return (None, 0)
@staticmethod
@ -111,26 +110,20 @@ class FontnameTools:
# - has modifier: use second form of mod plus first form of weights2
# - has modifier: use second form of mod plus second form of widths
name_rest = name
name_pre = ""
form = FontnameTools.get_shorten_form_idx(aggressive, "", 0)
name_pre = ''
form = FontnameTools.get_shorten_form_idx(aggressive, '', 0)
for mod in FontnameTools.known_modifiers:
if name.startswith(mod) and len(name) > len(
mod
): # Second condition specifically for 'Demi'
if name.startswith(mod) and len(name) > len(mod): # Second condition specifically for 'Demi'
name_pre = FontnameTools.known_modifiers[mod][form]
name_rest = name[len(mod) :]
name_rest = name[len(mod):]
break
subst, i = FontnameTools.find_in_dicts(
name_rest, [FontnameTools.known_weights2, FontnameTools.known_widths]
)
subst, i = FontnameTools.find_in_dicts(name_rest, [ FontnameTools.known_weights2, FontnameTools.known_widths ])
form = FontnameTools.get_shorten_form_idx(aggressive, name_pre, i)
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]
)
subst, _ = FontnameTools.find_in_dicts(name_rest, [ FontnameTools.known_weights1, FontnameTools.known_slopes ])
if isinstance(subst, tuple):
return subst[form]
return name
@ -139,26 +132,21 @@ class FontnameTools:
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(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
]
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):
"""Move "Oblique" from weights to styles for font naming purposes"""
if "Oblique" in weights:
if 'Oblique' in weights:
weights = list(weights)
weights.remove("Oblique")
weights.remove('Oblique')
styles = list(styles)
styles.append("Oblique")
styles.append('Oblique')
return (weights, styles)
@staticmethod
def get_name_token(name, tokens, allow_regex_token=False):
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
@ -168,29 +156,27 @@ class FontnameTools:
#
# 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]
lower_tokens = [ t.lower() for t in tokens ]
not_matched = ""
all_tokens = []
j = 1
regex = re.compile("(.*?)(" + "|".join(tokens) + ")(.*)", re.IGNORECASE)
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
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)
name = j.groups()[2] # Recurse rest
not_matched += ' ' + name
return ( not_matched.strip(), all_tokens )
@staticmethod
def postscript_char_filter(name):
@ -199,48 +185,42 @@ class FontnameTools:
# except for the 10 characters '[', ']', '(', ')', '{', '}', '<', '>', '/', '%'
out = ""
for c in name:
if c in "[](){}<>/%" or ord(c) < 33 or ord(c) > 126:
if c in '[](){}<>/%' or ord(c) < 33 or ord(c) > 126:
continue
out += c
return out
SIL_TABLE = [
("(a)nonymous", r"\1nonymice"),
("(b)itstream( ?)(v)era( ?sans ?mono)?", r"\1itstrom\2Wera"),
("(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
( '(a)nonymous', r'\1nonymice' ),
( '(b)itstream( ?)(v)era( ?sans ?mono)?', r'\1itstrom\2Wera' ),
( '(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:
("IBM 3270", r"3270"), # for historical reasons and 'IBM' is a TM or something
( 'IBM 3270', r'3270'), # for historical reasons and 'IBM' is a TM or something
# Some name parts that are too long for us
("(.*sans ?m)ono", r"\1"), # Various SomenameSansMono fonts
("(.*code ?lat)in Expanded", r"\1X"), # for 'M PLUS Code Latin Expanded'
("(.*code ?lat)in", r"\1"), # for 'M PLUS Code Latin'
("(b)ig( ?)(b)lue( ?)(t)erminal", r"\1ig\3lue\5erm"), # Shorten BigBlueTerminal
("(.*)437TT", r"\g<1>437"), # Shorten BigBlueTerminal 437 TT even further
("(.*dyslexic ?alt)a", r"\1"), # Open Dyslexic Alta -> Open Dyslexic Alt
("(.*dyslexic ?m)ono", r"\1"), # Open Dyslexic Mono -> Open Dyslexic M
("(overpass ?m)ono", r"\1"), # Overpass Mono -> Overpass M
("(proggyclean) ?tt", r"\1"), # Remove TT from ProggyClean
(
"(terminess) ?\(ttf\)",
r"\1",
), # Remove TTF from Terminus (after renamed to Terminess)
("(im ?writing ?q)uattro", r"\1uat"), # Rename iM Writing Quattro to Quat
(
"(im ?writing ?(mono|duo|quat)) ?s",
r"\1",
), # Remove S from all iM Writing styles
( '(.*sans ?m)ono', r'\1'), # Various SomenameSansMono fonts
( '(.*code ?lat)in Expanded', r'\1X'), # for 'M PLUS Code Latin Expanded'
( '(.*code ?lat)in', r'\1'), # for 'M PLUS Code Latin'
( '(b)ig( ?)(b)lue( ?)(t)erminal', r'\1ig\3lue\5erm'), # Shorten BigBlueTerminal
( '(.*)437TT', r'\g<1>437'), # Shorten BigBlueTerminal 437 TT even further
( '(.*dyslexic ?alt)a', r'\1'), # Open Dyslexic Alta -> Open Dyslexic Alt
( '(.*dyslexic ?m)ono', r'\1'), # Open Dyslexic Mono -> Open Dyslexic M
( '(overpass ?m)ono', r'\1'), # Overpass Mono -> Overpass M
( '(proggyclean) ?tt', r'\1'), # Remove TT from ProggyClean
( '(terminess) ?\(ttf\)', r'\1'), # Remove TTF from Terminus (after renamed to Terminess)
( '(im ?writing ?q)uattro', r'\1uat'), # Rename iM Writing Quattro to Quat
( '(im ?writing ?(mono|duo|quat)) ?s', r'\1'), # Remove S from all iM Writing styles
]
# From https://adobe-type-tools.github.io/font-tech-notes/pdfs/5088.FontNames.pdf
@ -254,103 +234,98 @@ class FontnameTools:
# - has modifier: use second form of mod plus first form of weights2
# - has modifier: use second form of mod plus second form of widths
# This is encoded in get_shorten_form_idx()
known_weights1 = { # can not take modifiers
"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_weights1 = { # can not take modifiers
'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", "Black"),
"Bold": ("Bd", "Bold"),
"Heavy": ("Hv", "Heavy"),
"Thin": ("Th", "Thin"),
"Light": ("Lt", "Light"),
" ": (), # Just for CodeClimate :-/
known_weights2 = { # can take modifiers
'Black': ('Blk', 'Black'),
'Bold': ('Bd', 'Bold'),
'Heavy': ('Hv', 'Heavy'),
'Thin': ('Th', 'Thin'),
'Light': ('Lt', 'Light'),
' ': (), # Just for CodeClimate :-/
}
known_widths = { # can take modifiers
"Compressed": ("Cm", "Comp"),
"Extended": ("Ex", "Extd"),
"Condensed": ("Cn", "Cond"),
"Narrow": ("Nr", "Narrow"),
"Compact": ("Ct", "Compact"),
known_widths = { # can take modifiers
'Compressed': ('Cm', 'Comp'),
'Extended': ('Ex', 'Extd'),
'Condensed': ('Cn', 'Cond'),
'Narrow': ('Nr', 'Narrow'),
'Compact': ('Ct', 'Compact'),
}
known_slopes = { # can not take modifiers
"Inclined": ("Ic", "Incl"),
"Oblique": ("Obl", "Obl"),
"Italic": ("It", "Italic"),
"Upright": ("Up", "Uprght"),
"Kursiv": ("Ks", "Kurs"),
"Sloped": ("Sl", "Slop"),
known_slopes = { # can not take modifiers
'Inclined': ('Ic', 'Incl'),
'Oblique': ('Obl', 'Obl'),
'Italic': ('It', 'Italic'),
'Upright': ('Up', 'Uprght'),
'Kursiv': ('Ks', 'Kurs'),
'Sloped': ('Sl', 'Slop'),
}
known_modifiers = {
"Demi": ("Dm", "Dem"),
"Ultra": ("Ult", "Ult"),
"Semi": ("Sm", "Sem"),
"Extra": ("X", "Ext"),
'Demi': ('Dm', 'Dem'),
'Ultra': ('Ult', 'Ult'),
'Semi': ('Sm', 'Sem'),
'Extra': ('X', 'Ext'),
}
@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",
'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
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(" ", "-"))
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)
p = re.split('(?<=[0-9])(?=[a-zA-Z])', name)
if len(p) > 1:
return FontnameTools.parse_font_name("-".join(p))
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), [], [], [], "")
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-condensed\b", "SemiCondensed", name, 1, re.IGNORECASE
) # Just for "3270 Semi-Condensed" :-/
name = re.sub("[_\s]+", " ", name)
matches = re.match(r"([^-]+)(?:-(.*))?", name)
name = re.sub(r'\bsemi-condensed\b', 'SemiCondensed', name, 1, re.IGNORECASE) # Just for "3270 Semi-Condensed" :-/
name = re.sub('[_\s]+', ' ', name)
matches = re.match(r'([^-]+)(?:-(.*))?', name)
familyname = FontnameTools.camel_casify(matches.group(1))
style = matches.group(2)
@ -361,81 +336,47 @@ class FontnameTools:
# 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 = (
[
m + s
for s in list(FontnameTools.known_weights2)
+ list(FontnameTools.known_widths)
for m in list(FontnameTools.known_modifiers) + [""]
if m != s
]
+ list(FontnameTools.known_weights1)
+ list(FontnameTools.known_slopes)
)
styles = [
"Bold",
"Italic",
"Regular",
"Normal",
]
weights = [w for w in weights if w not in styles]
weights = [ m + s
for s in list(FontnameTools.known_weights2) + list(FontnameTools.known_widths)
for m in list(FontnameTools.known_modifiers) + [''] if m != s
] + list(FontnameTools.known_weights1) + list(FontnameTools.known_slopes)
styles = [ 'Bold', 'Italic', 'Regular', 'Normal', ]
weights = [ w for w in weights if w not in styles ]
# Some font specialities:
other = [
"-",
"Book",
"For",
"Powerline",
"Text", # Plex
"IIx", # Profont IIx
"LGC", # Inconsolata LGC
r"\bCE\b", # ProggycleanTT CE
r"[12][cmp]n?", # MPlus
r"(?:uni-)?1[14]", # GohuFont uni
'-', 'Book', 'For', 'Powerline',
'Text', # Plex
'IIx', # Profont IIx
'LGC', # Inconsolata LGC
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",
]
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 and style.lower() != "pro"
): # Prevent 'r' of Pro to be detected as style_abbrev
(style, weight_token_abbrevs) = FontnameTools.get_name_token(
style, weight_abbrevs
)
(style, style_token_abbrevs) = FontnameTools.get_name_token(
style, style_abbrevs
)
( 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
and style.lower() != 'pro'): # Prevent 'r' of Pro to be detected as style_abbrev
( 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:
while 'Regular' in style_token and len(style_token) > 1:
# Correct situation where "Regular" and something else is given
style_token.remove("Regular")
style_token.remove('Regular')
# Recurse to see if unmatched stuff between dashes can belong to familyname
matches2 = re.match(r"(\w+)-(.*)", style)
matches2 = re.match(r'(\w+)-(.*)', style)
if matches2:
return FontnameTools.parse_font_name(
familyname + matches2.group(1) + "-" + matches2.group(2)
)
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))
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)

View file

@ -1,14 +1,14 @@
#!/usr/bin/env python
# coding=utf8
# Nerd Fonts Version: 3.0.0
# Nerd Fonts Version: 3.0.1
# Script version is further down
from __future__ import absolute_import, print_function, unicode_literals
# Change the script version when you edit this script:
script_version = "4.1.2"
script_version = "4.3.1"
version = "3.0.0"
version = "3.0.1"
projectName = "Nerd Fonts"
projectNameAbbreviation = "NF"
projectNameSingular = projectName[:-1]
@ -324,7 +324,7 @@ class font_patcher:
self.font_dim = None # class 'dict'
self.font_extrawide = False
self.source_monospaced = None # Later True or False
self.symbolsonly = False
self.symbolsonly = False # Are we generating the SymbolsOnly font?
self.onlybitmaps = 0
self.essential = set()
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
@ -337,8 +337,8 @@ class font_patcher:
self.setup_name_backup(font)
self.assert_monospace()
self.remove_ligatures()
self.setup_patch_set()
self.get_sourcefont_dimensions()
self.setup_patch_set()
self.improve_line_dimensions()
self.sourceFont.encoding = 'UnicodeFull' # Update the font encoding to ensure that the Unicode glyphs are available
self.onlybitmaps = self.sourceFont.onlybitmaps # Fetch this property before adding outlines. NOTE self.onlybitmaps initialized and never used
@ -373,15 +373,16 @@ class font_patcher:
if symfont:
symfont.close()
symfont = None
if not os.path.isfile(self.args.glyphdir + patch['Filename']):
symfont_file = os.path.join(self.args.glyphdir, patch['Filename'])
if not os.path.isfile(symfont_file):
logger.critical("Can not find symbol source for '%s' (i.e. %s)",
patch['Name'], self.args.glyphdir + patch['Filename'])
patch['Name'], symfont_file)
sys.exit(1)
if not os.access(self.args.glyphdir + patch['Filename'], os.R_OK):
if not os.access(symfont_file, os.R_OK):
logger.critical("Can not open symbol source for '%s' (i.e. %s)",
patch['Name'], self.args.glyphdir + patch['Filename'])
patch['Name'], symfont_file)
sys.exit(1)
symfont = fontforge.open(os.path.join(self.args.glyphdir, patch['Filename']))
symfont = fontforge.open(symfont_file)
symfont.encoding = 'UnicodeFull'
# Match the symbol font size to the source font size
@ -434,10 +435,10 @@ class font_patcher:
sanitize_filename(fontname) + self.args.extension))
bitmaps = str()
if len(self.sourceFont.bitmapSizes):
logger.debug("Preserving bitmaps {}".format(self.sourceFont.bitmapSizes))
logger.debug("Preserving bitmaps %s", repr(self.sourceFont.bitmapSizes))
bitmaps = str('otf') # otf/ttf, both is bf_ttf
if self.args.dry_run:
logger.debug("=====> Filename '{}'".format(outfile))
logger.debug("=====> Filename '%s'", outfile)
return
sourceFont.generate(outfile, bitmap_type=bitmaps, flags=gen_flags)
message = " {}\n \===> '{}'".format(self.sourceFont.fullname, outfile)
@ -784,7 +785,7 @@ class font_patcher:
def setup_patch_set(self):
""" Creates list of dicts to with instructions on copying glyphs from each symbol font into self.sourceFont """
box_enabled = self.source_monospaced # Box glyph only for monospaced
box_enabled = self.source_monospaced and not self.symbolsonly # Box glyph only for monospaced and not for Symbols Only
box_keep = False
if box_enabled:
self.sourceFont.selection.select(("ranges",), 0x2500, 0x259f)
@ -802,7 +803,7 @@ class font_patcher:
box_enabled = False # Cowardly not scaling existing glyphs, although the code would allow this
# Stretch 'xz' or 'pa' (preserve aspect ratio)
# Supported params: overlap | careful | xy-ratio | dont_copy
# Supported params: overlap | careful | xy-ratio | dont_copy | ypadding
# Overlap value is used horizontally but vertically limited to 0.01
# Careful does not overwrite/modify existing glyphs
# The xy-ratio limits the x-scale for a given y-scale to make the ratio <= this value (to prevent over-wide glyphs)
@ -810,6 +811,8 @@ class font_patcher:
# '2' means occupy 2 cells (default for 'pa')
# '!' means do the 'pa' scaling even with non mono fonts (else it just scales down, never up)
# Dont_copy does not overwrite existing glyphs but rescales the preexisting ones
#
# Be careful, stretch may not change within a ScaleRule!
SYM_ATTR_DEFAULT = {
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}}
@ -886,7 +889,7 @@ class font_patcher:
0xf0de: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}}
}
SYM_ATTR_HEAVYBRACKETS = {
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {'careful': True}}
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa1!', 'params': {'ypadding': 0.3, 'careful': True}}
}
SYM_ATTR_BOX = {
'default': {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'dont_copy': box_keep}},
@ -896,8 +899,9 @@ class font_patcher:
# 0x2593: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'dont_copy': box_keep}},
}
CUSTOM_ATTR = {
# 'pa' == preserve aspect ratio
'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
# previous custom scaling => do not touch the icons
# 'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {'careful': self.args.careful}}
}
# Most glyphs we want to maximize (individually) during the scale
@ -956,6 +960,9 @@ class font_patcher:
range(0xeab4, 0xeab7 + 1), # chevrons
[0xea71, *range(0xeaa6, 0xeaab + 1), 0xeabc, 0xeb18, 0xeb87, 0xeb88, 0xeb8a, 0xeb8c, 0xebb4], # cicles
[0xeacc, 0xeaba], # dash
[0xea75, 0xebe7], # lock pair
[0xeacf, 0xebe0], # debug-continue pair
[0xeb91, 0xeba8], # debug-alt pair
]}
DEVI_SCALE_LIST = {'ScaleGlyph': 0xE60E, # Android logo
'GlyphsToScale': [
@ -982,14 +989,21 @@ class font_patcher:
range(0xf221, 0xf22d + 1), # gender or so
range(0xf255, 0xf25b + 1), # hand symbols
]}
OCTI_SCALE_LIST = {'ScaleGlyph': 0xF02E, # looking glass (probably biggest glyph?)
HEAVY_SCALE_LIST = {'ScaleGlyph': 0x2771, # widest bracket, horizontally
'GlyphsToScale': [
(0xf03d, 0xf040), # arrows
0xf044, 0xf05a, 0xf05b, 0xf0aa, # triangles
(0xf051, 0xf053), # small stuff
0xf071, 0xf09f, 0xf0a0, 0xf0a1, # small arrows
0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons
0xf0ca, # dash
(0x276c, 0x2771) # all
]}
OCTI_SCALE_LIST = {'ScaleGroups': [
[*range(0xf03d, 0xf040 + 1), 0xf019, 0xf030, 0xf04a, 0xf050, 0xf071, 0xf08c ], # arrows
[0xF0E7, # Smily and ...
0xf044, 0xf05a, 0xf05b, 0xf0aa, # triangles
0xf052, 0xf053, 0x296, 0xf2f0, # small stuff
0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons
0xf0ca, 0xf081, 0xf092, # dash, X, github-text
],
[0xf09c, 0xf09f, 0xf0de], # bells
range(0xf2c2, 0xf2c5 + 1), # move to
[0xf07b, 0xf0a1, 0xf0d6, 0xf306], # bookmarks
]}
WEATH_SCALE_LIST = {'ScaleGroups': [
[0xf03c, 0xf042, 0xf045 ], # degree signs
@ -1016,7 +1030,7 @@ class font_patcher:
# Symbol font ranges
self.patch_set = [
{'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE5FF, 'SrcStart': 0xE5FA, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': True, 'Name': "Heavy Angle Brackets", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0x276C, 'SymEnd': 0x2771, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_HEAVYBRACKETS},
{'Enabled': True, 'Name': "Heavy Angle Brackets", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0x276C, 'SymEnd': 0x2771, 'SrcStart': None, 'ScaleRules': HEAVY_SCALE_LIST, 'Attributes': SYM_ATTR_HEAVYBRACKETS},
{'Enabled': box_enabled, 'Name': "Box Drawing", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0x2500, 'SymEnd': 0x259F, 'SrcStart': None, 'ScaleRules': BOX_SCALE_LIST, 'Attributes': SYM_ATTR_BOX},
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'ScaleRules': DEVI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0A0, 'SymEnd': 0xE0A2, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE},
@ -1038,7 +1052,7 @@ class font_patcher:
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': True, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': True, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF305, 'SrcStart': 0xF4A9, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons/octicons.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF306, 'SrcStart': 0xF4A9, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleRules': CODI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
{'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleRules': None, 'Attributes': CUSTOM_ATTR}
]
@ -1081,10 +1095,11 @@ class font_patcher:
# Sometimes basic glyphs are constructed from multiple other glyphs.
# Find out which other glyphs are also needed to keep the basic
# glyphs intact.
# 0x00-0x17f is the Latin Extended-A range
# 0x0000-0x017f is the Latin Extended-A range
# 0xfb00-0xfb06 are 'fi' and other ligatures
basic_glyphs = set()
# Collect substitution destinations
for glyph in range(0x21, 0x17f + 1):
for glyph in [*range(0x21, 0x17f + 1), *range(0xfb00, 0xfb06 + 1)]:
if not glyph in self.sourceFont:
continue
basic_glyphs.add(glyph)
@ -1120,8 +1135,8 @@ class font_patcher:
# Try the other metric
our_btb = typo_btb if not use_typo else win_btb
if our_btb == hhea_btb:
logger.warning("Font vertical metrics probably wrong USE TYPO METRICS, assume opposite (i.e. %s)", 'True' if not use_typo else 'False')
use_typo = not use_typo
logger.warning("Font vertical metrics probably wrong USE TYPO METRICS, assume opposite (i.e. %s)", repr(use_typo))
self.sourceFont.os2_use_typo_metrics = 1 if use_typo else 0
metrics = Metric.TYPO if use_typo else Metric.WIN
else:
@ -1132,7 +1147,7 @@ class font_patcher:
# print("FINI hhea {} typo {} win {} use {} {} {}".format(hhea_btb, typo_btb, win_btb, use_typo, our_btb != hhea_btb, self.sourceFont.fontname))
self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0}
self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0, 'ypadding': 0}
if metrics == Metric.HHEA:
self.font_dim['ymin'] = self.sourceFont.hhea_descent - half_gap(self.sourceFont.hhea_linegap, False)
@ -1240,7 +1255,7 @@ class font_patcher:
# font_dim['height'] represents total line height, keep our symbols sized based upon font's em
# Use the font_dim['height'] only for explicit 'y' scaling (not 'pa')
target_height = self.font_dim['height']
target_height = self.font_dim['height'] * (1.0 - self.font_dim['ypadding'])
scale_ratio_y = target_height / sym_dim['height']
if 'pa' in stretch:
@ -1324,6 +1339,9 @@ class font_patcher:
# if currentSourceFontGlyph != 0xe7bd:
# continue
ypadding = sym_attr['params'].get('ypadding')
self.font_dim['ypadding'] = ypadding or 0.0
if not self.args.quiet:
if self.args.progressbars:
update_progress(round(float(index + 1) / glyphSetLength, 2))
@ -1333,7 +1351,8 @@ class font_patcher:
sys.stdout.flush()
# check if a glyph already exists in this location
if careful or 'careful' in sym_attr['params'] or currentSourceFontGlyph in self.essential:
do_careful = sym_attr['params'].get('careful', careful) # params take precedence
if do_careful or currentSourceFontGlyph in self.essential:
if currentSourceFontGlyph in self.sourceFont:
careful_type = 'essential' if currentSourceFontGlyph in self.essential else 'existing'
logger.debug("Found %s Glyph at %X. Skipping...", careful_type, currentSourceFontGlyph)
@ -1345,14 +1364,15 @@ class font_patcher:
if currentSourceFontGlyph in self.sourceFont:
self.sourceFont[currentSourceFontGlyph].removePosSub("*")
stretch = sym_attr['stretch']
dont_copy = sym_attr['params'].get('dont_copy')
if dont_copy:
# Just prepare scaling of existing glyphs
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, self.sourceFont, currentSourceFontGlyph) if scaleRules is not None else None
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, stretch, self.sourceFont, currentSourceFontGlyph) if scaleRules is not None else None
else:
# This will destroy any content currently in currentSourceFontGlyph, so do it first
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, symbolFont, currentSourceFontGlyph) if scaleRules is not None else None
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, stretch, symbolFont, currentSourceFontGlyph) if scaleRules is not None else None
# Select and copy symbol from its encoding point
# We need to do this select after the careful check, this way we don't
@ -1371,15 +1391,18 @@ class font_patcher:
if glyph_scale_data is not None:
if glyph_scale_data[1] is not None:
sym_dim = glyph_scale_data[1] # Use combined bounding box
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, sym_attr['stretch'])
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, stretch)
else:
# This is roughly alike get_scale_factors(glyph_scale_data[1], 'pa')
# Except we do not have glyph_scale_data[1] always...
(scale_ratio_x, scale_ratio_y) = (glyph_scale_data[0], glyph_scale_data[0])
else:
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, sym_attr['stretch'])
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, stretch)
overlap = sym_attr['params'].get('overlap')
if overlap and ypadding:
logger.critical("Conflicting params: overlap and ypadding")
sys.exit(1)
if overlap:
scale_ratio_x *= 1.0 + (self.font_dim['width'] / (sym_dim['width'] * scale_ratio_x)) * overlap
y_overlap = min(0.01, overlap) # never aggressive vertical overlap
@ -1429,7 +1452,7 @@ class font_patcher:
x_align_distance += (self.font_dim['width'] / 2) - (sym_dim['width'] / 2)
elif sym_attr['align'] == 'r':
# Right align
x_align_distance += self.font_dim['width'] * self.get_target_width(sym_attr['stretch']) - sym_dim['width']
x_align_distance += self.font_dim['width'] * self.get_target_width(stretch) - sym_dim['width']
# If symbol glyph is wider than target font cell, just left-align
x_align_distance = max(self.font_dim['xmin'] - sym_dim['xmin'], x_align_distance)
@ -1442,7 +1465,7 @@ class font_patcher:
x_align_distance -= overlap_width / 2
elif sym_attr['align'] == 'r':
# Check and correct overlap; it can go wrong if we have a xy-ratio limit
target_xmax = (self.font_dim['xmin'] + self.font_dim['width']) * self.get_target_width(sym_attr['stretch'])
target_xmax = (self.font_dim['xmin'] + self.font_dim['width']) * self.get_target_width(stretch)
target_xmax += overlap_width
glyph_xmax = sym_dim['xmax'] + x_align_distance
correction = target_xmax - glyph_xmax
@ -1484,8 +1507,8 @@ class font_patcher:
if self.args.single:
(xmin, _, xmax, _) = self.sourceFont[currentSourceFontGlyph].boundingBox()
if int(xmax - xmin) > self.font_dim['width'] * (1 + (overlap or 0)):
logger.warning("Scaled glyph %X wider than one monospace width (%d / %d (overlap %f))",
currentSourceFontGlyph, int(xmax - xmin), self.font_dim['width'], overlap)
logger.warning("Scaled glyph %X wider than one monospace width (%d / %d (overlap %s))",
currentSourceFontGlyph, int(xmax - xmin), self.font_dim['width'], repr(overlap))
# end for
@ -1538,7 +1561,7 @@ class font_patcher:
except:
pass
def prepareScaleRules(self, scaleRules, symbolFont, destGlyph):
def prepareScaleRules(self, scaleRules, stretch, symbolFont, destGlyph):
""" Prepare raw ScaleRules data for use """
# The scaleRules is/will be a dict with these (possible) entries:
# 'ScaleGroups': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled
@ -1572,7 +1595,7 @@ class font_patcher:
scaleRules['ScaleGroups'] = []
for group in scaleRules['ScaleGroups']:
sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ], destGlyph)
scale = self.get_scale_factors(sym_dim, 'pa')[0]
scale = self.get_scale_factors(sym_dim, stretch)[0]
scaleRules['scales'].append(scale)
scaleRules['bbdims'].append(sym_dim)
@ -1591,7 +1614,7 @@ class font_patcher:
else:
group_list.append(i)
sym_dim = get_glyph_dimensions(symbolFont[scaleRules['ScaleGlyph']])
scale = self.get_scale_factors(sym_dim, 'pa')[0]
scale = self.get_scale_factors(sym_dim, stretch)[0]
scaleRules['ScaleGroups'].append(group_list)
scaleRules['scales'].append(scale)
if plus:
@ -1599,13 +1622,13 @@ class font_patcher:
else:
scaleRules['bbdims'].append(None) # The 'old' style keeps just the scale, not the positioning
def get_glyph_scale(self, symbol_unicode, scaleRules, symbolFont, dest_unicode):
def get_glyph_scale(self, symbol_unicode, scaleRules, stretch, symbolFont, dest_unicode):
""" Determines whether or not to use scaled glyphs for glyph in passed symbol_unicode """
# Potentially destorys the contents of self.sourceFont[dest_unicode]
if not 'scales' in scaleRules:
if not dest_unicode in self.sourceFont:
self.sourceFont.createChar(dest_unicode)
self.prepareScaleRules(scaleRules, symbolFont, self.sourceFont[dest_unicode])
self.prepareScaleRules(scaleRules, stretch, symbolFont, self.sourceFont[dest_unicode])
for glyph_list, scale, box in zip(scaleRules['ScaleGroups'], scaleRules['scales'], scaleRules['bbdims']):
for e in glyph_list:
if isinstance(e, range):
@ -1805,7 +1828,7 @@ def setup_arguments():
parser.add_argument('--removeligs', '--removeligatures', dest='removeligatures', default=False, action='store_true', help='Removes ligatures specificed in JSON configuration file')
parser.add_argument('--postprocess', dest='postprocess', default=False, type=str, nargs='?', help='Specify a Script for Post Processing')
parser.add_argument('--configfile', dest='configfile', default=False, type=str, nargs='?', help='Specify a file path for JSON configuration file (see sample: src/config.sample.json)')
parser.add_argument('--custom', dest='custom', default=False, type=str, nargs='?', help='Specify a custom symbol font. All new glyphs will be copied, with no scaling applied.')
parser.add_argument('--custom', dest='custom', default=False, type=str, nargs='?', help='Specify a custom symbol font, all glyphs will be copied; absolute path suggested')
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')
@ -1829,7 +1852,7 @@ def setup_arguments():
progressbars_group_parser.add_argument('--progressbars', dest='progressbars', action='store_true', help='Show percentage completion progress bars per Glyph Set (default)')
progressbars_group_parser.add_argument('--no-progressbars', dest='progressbars', action='store_false', help='Don\'t show percentage completion progress bars per Glyph Set')
parser.set_defaults(progressbars=True)
parser.add_argument('--debug', dest='debugmode', default=False, action='store_true', help='Verbose mode')
parser.add_argument('--debug', dest='debugmode', default=0, type=int, nargs='?', help='Verbose mode (optional: 1=just to file; 2*=just to terminal; 3=display and file)', const=2, choices=range(0, 3 + 1))
parser.add_argument('--dry', dest='dry_run', default=False, action='store_true', help='Do neither patch nor store the font, to check naming')
parser.add_argument('--xavgcharwidth', dest='xavgwidth', default=None, type=int, nargs='?', help='Adjust xAvgCharWidth (optional: concrete value)', const=True)
# --xavgcharwidth for compatibility with old applications like notepad and non-latin fonts
@ -1952,16 +1975,23 @@ def main():
global logger
logger = logging.getLogger(os.path.basename(args.font))
logger.setLevel(logging.DEBUG)
f_handler = logging.FileHandler('font-patcher-log.txt')
f_handler.setFormatter(logging.Formatter('%(levelname)s: %(name)s %(message)s'))
logger.addHandler(f_handler)
logger.debug(allversions)
logger.debug("Options %s", repr(sys.argv[1:]))
log_to_file = (args.debugmode & 1 == 1)
if log_to_file:
try:
f_handler = logging.FileHandler('font-patcher-log.txt')
f_handler.setFormatter(logging.Formatter('%(levelname)s: %(name)s %(message)s'))
logger.addHandler(f_handler)
except:
log_to_file = False
logger.debug(allversions)
logger.debug("Options %s", repr(sys.argv[1:]))
c_handler = logging.StreamHandler(stream=sys.stdout)
c_handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
if not args.debugmode:
if not (args.debugmode & 2 == 2):
c_handler.setLevel(logging.INFO)
logger.addHandler(c_handler)
if (args.debugmode & 1 == 1) and not log_to_file:
logger.info("Can not write logfile, disabling")
logger.debug("Naming mode %d", args.makegroups)
patcher = font_patcher(args)

View file

@ -14,10 +14,10 @@ import fontforge
octi_orig = "octicons.ttf"
current_cp = 0xF400
print("# Examining {}".format(octi_orig))
print('# Examining {}'.format(octi_orig))
font = fontforge.open(octi_orig)
for glyph in font.glyphs("encoding"):
for glyph in font.glyphs('encoding'):
point = glyph.unicode
if point < 0:
continue

View file

@ -0,0 +1 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m1.75 1c-0.966 0-1.75 0.784-1.75 1.75v1.75c0 1 1.5 1 1.5 0v-1.75c0-0.138 0.112-0.25 0.25-0.25h3.25c0.0783 1.53e-4 0.152 0.037 0.199 0.0996l0.9 1.2c0.33 0.44 0.85 0.699 1.4 0.699h6.75c0.138 0 0.25 0.112 0.25 0.25v8.5c0 0.138-0.112 0.25-0.25 0.25h-9c-1 0-1 1.5 0 1.5h9c0.966 0 1.75-0.784 1.75-1.75v-8.5c0-0.966-0.784-1.75-1.75-1.75h-6.75c-0.0783-1.53e-4 -0.152-0.037-0.199-0.0996l-0.9-1.2c-0.33-0.44-0.85-0.699-1.4-0.699zm2.49 5.32c-0.135 0.00422-0.242 0.115-0.242 0.25v1.92h-0.23c-2.08-0.0119-3.77 1.67-3.77 3.75v1.01c1.63e-4 1 1.5 1 1.5 0v-1.01c-1.69e-5 -1.25 1.01-2.26 2.26-2.25l0.238 0.00195v1.94c0.0015 0.217 0.261 0.329 0.42 0.182l2.88-2.68c0.107-0.099 0.107-0.268 0-0.367l-2.88-2.68c-0.0482-0.0446-0.112-0.0685-0.178-0.0664z"/></svg>

After

Width:  |  Height:  |  Size: 845 B

View file

@ -0,0 +1 @@
<svg width="24" height="24" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m3.75 3c-0.966 0-1.75 0.784-1.75 1.75v4.74c0 1 1.5 1 1.5 0v-4.74c0-0.138 0.112-0.25 0.25-0.25h4.97c0.0828 2.38e-4 0.16 0.0411 0.207 0.109l1.4 2.06c0.326 0.479 0.868 0.766 1.45 0.766h8.47c0.138 0 0.25 0.112 0.25 0.25v11.6c0 0.138-0.112 0.25-0.25 0.25h-13.5c-1 0-1 1.5 0 1.5h13.5c0.966 0 1.75-0.784 1.75-1.75v-11.6c0-0.967-0.784-1.75-1.75-1.75h-8.47c-0.083 0-0.161-0.0404-0.207-0.109l-1.4-2.06c-0.327-0.479-0.867-0.766-1.45-0.766zm5.95 5.99c-0.0441 0.00229-0.0877 0.00751-0.131 0.0176-0.569 0.132-0.775 0.835-0.369 1.25l2.11 2.24h-5.22c-2.26 8.6e-5 -4.09 1.83-4.09 4.09v3.16c0 1 1.5 1 1.5 0v-3.16c8.6e-5 -1.43 1.16-2.59 2.59-2.59h5.22l-2.11 2.24c-0.188 0.194-0.258 0.475-0.182 0.734 0.164 0.564 0.882 0.729 1.28 0.293l3.29-3.5c0.271-0.289 0.271-0.739 0-1.03l-3.29-3.5c-0.12-0.133-0.284-0.218-0.461-0.24-0.044-0.00555-0.0887-0.00619-0.133-0.00391z"/></svg>

After

Width:  |  Height:  |  Size: 960 B

View file

@ -8,95 +8,81 @@ import subprocess
import fontforge
# Double-quotes required here, for version-bump.sh:
version = "2.3.3"
# version-bump.sh is not working here, need to adjust manually!
version = "3.0.0"
archive = "v18.3.0.tar.gz"
archive = 'v18.3.0.tar.gz'
vectorsdir = "icons"
fontdir = "."
fontfile = "octicons.ttf"
glyphsetfile = "i_oct.sh"
glyphsetsdir = "../../../bin/scripts/lib"
subset = "-16" # use 16 px subset if possible
subset_other = "-24" # use 24 px subset otherwise
vectorsdir = 'icons'
fontdir = '.'
fontfile = 'octicons.ttf'
glyphsetfile = 'i_oct.sh'
glyphsetsdir = '../../../bin/scripts/lib'
subset = '-16' # use 16 px subset if possible
subset_other = '-24' # use 24 px subset otherwise
def renamer(old_name):
"""Return new equivalent icon name"""
""" Return new equivalent icon name """
return {
"trashcan": "trash",
"cloud-download": "download",
"cloud-upload": "upload",
"clippy": "paste",
"mail-read": "read",
"primitive-dot": "dot-fill",
"primitive-square": "square-fill",
"settings": "sliders",
"dashboard": "meter",
"paintcan": "paintbrush",
}.get(old_name, old_name)
'trashcan' : 'trash',
'cloud-download' : 'download',
'cloud-upload' : 'upload',
'clippy' : 'paste',
'mail-read' : 'read',
'primitive-dot' : 'dot-fill',
'primitive-square' : 'square-fill',
'settings' : 'sliders',
'dashboard' : 'meter',
'paintcan' : 'paintbrush',
}.get(old_name, old_name)
def addIcon(codepoint, name, filename):
"""Add one outline file and rescale/move"""
dBB = [120, 0, 1000 - 120, 900] # just some nice sizes
""" Add one outline file and rescale/move """
dBB = [120, 0, 1000-120, 900] # just some nice sizes
filename = os.path.join(vectorsdir, filename)
glyph = font.createChar(codepoint, name)
glyph.importOutlines(filename)
glyph.manualHints = True
def createGlyphInfo(icon_datasets, filepathname, into):
"""Write the glyphinfo file"""
with open(filepathname, "w", encoding="utf8") as f:
f.write("#!/usr/bin/env bash\n")
""" Write the glyphinfo file """
with open(filepathname, 'w', encoding = 'utf8') as f:
f.write(u'#!/usr/bin/env bash\n')
f.write(intro)
f.write("# Script Version: (autogenerated)\n")
f.write('test -n "$__i_oct_loaded" && return || __i_oct_loaded=1\n')
f.write(u'# Script Version: (autogenerated)\n')
f.write(u'test -n "$__i_oct_loaded" && return || __i_oct_loaded=1\n')
for _, codepoint, name in icon_datasets:
codepoint = int(codepoint, 16)
f.write(
"i='{}' i_oct_{}=$i\n".format(chr(codepoint), name.replace("-", "_"))
)
f.write("unset i\n")
f.write(u"i='{}' i_oct_{}=$i\n".format(chr(codepoint), name.replace('-', '_')))
f.write(u'unset i\n')
print("\nReading mapping file")
print('\nReading mapping file')
old_mapping = []
with open("mapping", "r") as f:
with open('mapping', 'r') as f:
for line in f.readlines():
if line.startswith("#"):
if line.startswith('#'):
continue
old_mapping.append(tuple(re.split(" +", line.strip())))
print("Found {} entries".format(len(old_mapping)))
old_mapping.append(tuple(re.split(' +', line.strip())))
print('Found {} entries'.format(len(old_mapping)))
old_mapping.sort(key=(lambda x: x[0]))
print('Fetching octicons archive "{}"\n'.format(archive))
if subprocess.call(
"curl -OL https://github.com/primer/octicons/archive/" + archive, shell=True
):
sys.exit("Error fetching octicons archive")
print("\nUnpacking octicons archive")
if subprocess.call(
"rm -rf icons octicons-* && tar zxf *.gz && mv octicons-*/icons . && rm -rf octicons-*",
shell=True,
):
sys.exit("Error unpacking archive")
if subprocess.call('curl -OL https://github.com/primer/octicons/archive/' + archive, shell=True):
sys.exit('Error fetching octicons archive')
print('\nUnpacking octicons archive')
if subprocess.call('rm -rf icons octicons-* && tar zxf *.gz && mv octicons-*/icons . && rm -rf octicons-* && cp file-symlink-directory-*.svg icons', shell=True):
sys.exit('Error unpacking archive')
svgs = os.listdir(vectorsdir)
print("Found {} svgs".format(len(svgs)))
names = {
s[0 : -len("-xx.svg")]
for s in svgs
if s.endswith(subset + ".svg") or s.endswith(subset_other + ".svg")
}
print("Found {} icons after de-duplicating\n".format(len(names)))
print('Found {} svgs'.format(len(svgs)))
names = { s[0:-len('-xx.svg')] for s in svgs if s.endswith(subset + '.svg') or s.endswith(subset_other + '.svg') }
print('Found {} icons after de-duplicating\n'.format(len(names)))
num_found = 0
num_missing = 0
misslist = ""
renamelist = ""
misslist = ''
renamelist = ''
freeslots = []
new_mapping = []
@ -108,22 +94,18 @@ for i, j, old_n in old_mapping:
continue
new_n = renamer(old_n)
if new_n in names:
renamelist += "Renamed {} -> {}\n".format(old_n, new_n)
renamelist += 'Renamed {} -> {}\n'.format(old_n, new_n)
names.remove(new_n)
new_mapping.append((i, j, new_n))
num_found += 1
continue
misslist += "Missing {}\n".format(old_n)
misslist += 'Missing {}\n'.format(old_n)
freeslots.append((i, j))
num_missing += 1
print(renamelist)
print(misslist)
print(
"Found {} (of {}, missing {}) and new {}".format(
num_found, len(old_mapping), num_missing, len(names)
)
)
print('Found {} (of {}, missing {}) and new {}'.format(num_found, len(old_mapping), num_missing, len(names)))
names = list(names)
names.sort()
@ -135,7 +117,7 @@ for n in list(names):
names.remove(n)
freeslots = freeslots[1:]
print("Filled in missing, remaining new {}".format(len(names)))
print('Filled in missing, remaining new {}'.format(len(names)))
i_max = 0
j_max = 0
@ -150,52 +132,50 @@ for i, j, _ in new_mapping:
for n in names:
i_max += 1
j_max += 1
new_mapping.append(("{:X}".format(i_max), "{:X}".format(j_max), n))
new_mapping.append(('{:X}'.format(i_max), '{:X}'.format(j_max), n))
print("Appended remaining new, total new mapping {}".format(len(new_mapping)))
print('Appended remaining new, total new mapping {}'.format(len(new_mapping)))
new_mapping.sort(key=(lambda x: x[0]))
with open("mapping", "w") as f:
with open('mapping', 'w') as f:
for i, j, n in new_mapping:
f.write("{} {} {}\n".format(i, j, n))
f.write('{} {} {}\n'.format(i, j, n))
font = fontforge.font()
font.fontname = "OcticonsNerdFont-Regular"
font.fullname = "Octicons Nerd Font Regular"
font.familyname = "Octicons Nerd Font"
font.fontname = 'OcticonsNerdFont-Regular'
font.fullname = 'Octicons Nerd Font Regular'
font.familyname = 'Octicons Nerd Font'
font.em = 2048
font.encoding = "UnicodeFull"
font.encoding = 'UnicodeFull'
# Add valid space glyph to avoid "unknown character" box on IE11
glyph = font.createChar(32)
glyph.width = 200
font.sfntRevision = None # Auto-set (refreshed) by fontforge
font.sfntRevision = None # Auto-set (refreshed) by fontforge
font.version = version
font.copyright = "GitHub Inc."
font.appendSFNTName("English (US)", "Version", archive + "; " + version)
font.appendSFNTName(
"English (US)", "Vendor URL", "https://github.com/ryanoasis/nerd-fonts"
)
font.appendSFNTName("English (US)", "Copyright", "GitHub Inc.")
font.copyright = 'GitHub Inc.'
font.appendSFNTName('English (US)', 'Version', archive + '; ' + version)
font.appendSFNTName('English (US)', 'Vendor URL', 'https://github.com/ryanoasis/nerd-fonts')
font.appendSFNTName('English (US)', 'Copyright', 'GitHub Inc.')
for codepoint, _, name in new_mapping:
codepoint = int(codepoint, 16)
filename = name + subset + ".svg"
filename = name + subset + '.svg'
if filename not in svgs:
filename = name + subset_other + ".svg"
filename = name + subset_other + '.svg'
addIcon(codepoint, name, filename)
num_icons = len(new_mapping)
print("Generating {} with {} glyphs".format(fontfile, num_icons))
print('Generating {} with {} glyphs'.format(fontfile, num_icons))
font.generate(os.path.join(fontdir, fontfile), flags=("no-FFTM-table",))
codepoints = [int(p, 16) for _, p, _ in new_mapping]
intro = "# Octicons ({} icons)\n".format(num_icons)
intro += "# Codepoints: {:X}-{:X} with gaps\n".format(min(codepoints), max(codepoints))
intro += "# Nerd Fonts Version: {}\n".format(version)
codepoints = [ int(p, 16) for _, p, _ in new_mapping ]
intro = u'# Octicons ({} icons)\n'.format(num_icons)
intro += u'# Codepoints: {:X}-{:X} with gaps\n'.format(min(codepoints), max(codepoints))
intro += u'# Nerd Fonts Version: {}\n'.format(version)
print("Generating GlyphInfo {}".format(glyphsetfile))
print('Generating GlyphInfo {}'.format(glyphsetfile))
createGlyphInfo(new_mapping, os.path.join(glyphsetsdir, glyphsetfile), intro)
print("Finished")
print('Finished')

View file

@ -130,7 +130,7 @@ F0AA F47E triangle-up
F0AC F47F git-compare
F0AD F480 logo-gist
F0B0 F481 file-symlink-file
F0B1 F482 bookmark-slash
F0B1 F482 file-symlink-directory
F0B2 F483 squirrel
F0B6 F484 globe
F0BA F485 unmute
@ -307,3 +307,4 @@ F302 F52F x-circle
F303 F530 x-circle-fill
F304 F531 zoom-in
F305 F532 zoom-out
F306 F533 bookmark-slash

Binary file not shown.

Binary file not shown.

View file

@ -433,3 +433,4 @@ Foobar.org is a distributed community of developers...
Company.com is a small business who likes to support community designers...
University.edu is a renowned educational institution with a strong design department...
-----