Update files (nerd-fonts 3.0.1)
This commit is contained in:
parent
43b872a6ce
commit
2904d3e8c3
|
@ -7,8 +7,8 @@ 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"""
|
||||
def __init__(self, fontname, logger):
|
||||
"""Parse a fontname 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
|
||||
|
@ -17,7 +17,7 @@ class FontnameParser:
|
|||
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(fontname)
|
||||
self.basename = self._basename
|
||||
self.rest = self._rest
|
||||
self.add_name_substitution_table(FontnameTools.SIL_TABLE)
|
||||
|
@ -252,10 +252,13 @@ class FontnameParser:
|
|||
# Ignore Italic if we have Oblique
|
||||
if 'Oblique' in self.weight_token:
|
||||
b |= OBLIQUE
|
||||
if not self.rename_oblique:
|
||||
# If we have no dedicated italic, than oblique = italic
|
||||
b |= ITALIC
|
||||
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
|
||||
return b
|
||||
|
|
|
@ -5,7 +5,7 @@ import re
|
|||
import sys
|
||||
|
||||
class FontnameTools:
|
||||
"""Deconstruct a font filename to get standardized name parts"""
|
||||
"""Deconstruct a fontname to get standardized name parts"""
|
||||
|
||||
@staticmethod
|
||||
def front_upper(word):
|
||||
|
@ -64,24 +64,11 @@ class FontnameTools:
|
|||
known_names = {
|
||||
# Source of the table is the current sourcefonts
|
||||
# Left side needs to be lower case
|
||||
'-': '',
|
||||
'book': '',
|
||||
'text': '',
|
||||
'ce': 'CE',
|
||||
#'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
|
||||
return known_names.get(style_name.lower(), style_name)
|
||||
|
||||
@staticmethod
|
||||
def find_in_dicts(key, dicts):
|
||||
|
@ -146,7 +133,7 @@ class FontnameTools:
|
|||
return (weights, styles)
|
||||
|
||||
@staticmethod
|
||||
def get_name_token(name, tokens, allow_regex_token = False):
|
||||
def get_name_token(name, tokens):
|
||||
"""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
|
||||
|
@ -160,7 +147,11 @@ class FontnameTools:
|
|||
not_matched = ""
|
||||
all_tokens = []
|
||||
j = 1
|
||||
regex = re.compile('(.*?)(' + '|'.join(tokens) + ')(.*)', re.IGNORECASE)
|
||||
token_regex = '|'.join(tokens)
|
||||
# Allow a dash between CamelCase token word parts, i.e. Camel-Case
|
||||
# This allows for styles like Extra-Bold
|
||||
token_regex = re.sub(r'(?<=[a-z])(?=[A-Z])', '-?', token_regex)
|
||||
regex = re.compile('(.*?)(' + token_regex + ')(.*)', re.IGNORECASE)
|
||||
while j:
|
||||
j = regex.match(name)
|
||||
if not j:
|
||||
|
@ -169,6 +160,7 @@ class FontnameTools:
|
|||
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()
|
||||
tok = tok.replace('-', '') # Remove dashes between CamelCase token words
|
||||
if tok in lower_tokens:
|
||||
tok = tokens[lower_tokens.index(tok)]
|
||||
tok = FontnameTools.unify_style_names(tok)
|
||||
|
@ -238,6 +230,7 @@ class FontnameTools:
|
|||
'Medium': ('Md', 'Med'),
|
||||
'Nord': ('Nd', 'Nord'),
|
||||
'Book': ('Bk', 'Book'),
|
||||
'Text': ('Txt', 'Text'),
|
||||
'Poster': ('Po', 'Poster'),
|
||||
'Demi': ('Dm', 'Demi'), # Demi is sometimes used as a weight, sometimes as a modifier
|
||||
'Regular': ('Rg', 'Reg'),
|
||||
|
@ -306,8 +299,9 @@ class FontnameTools:
|
|||
|
||||
@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?
|
||||
"""Parse a fontname that does not follow the 'FontFamilyName-FontStyle' pattern"""
|
||||
# This is the usual case, because the font-patcher usually uses the fullname and
|
||||
# not the PS name
|
||||
if ' ' in name:
|
||||
return FontnameTools.parse_font_name(name.replace(' ', '-'))
|
||||
# Do we have a number-name boundary?
|
||||
|
@ -322,8 +316,15 @@ class FontnameTools:
|
|||
|
||||
@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" :-/
|
||||
"""Expects a fontname following the 'FontFamilyName-FontStyle' pattern and returns ... parts"""
|
||||
# This could parse filenames in the beginning but that was never used in production; code removed with this commit
|
||||
for special in [
|
||||
('ExtLt', 'ExtraLight'), # IBM-Plex
|
||||
('Medm', 'Medium'), # IBM-Plex
|
||||
('Semi-Condensed', 'SemiCondensed'), # 3270
|
||||
('SmBld', 'SemiBold'), # IBM-Plex
|
||||
]:
|
||||
name = re.sub(r'\b' + special[0] + r'\b', special[1], name, 1, re.IGNORECASE)
|
||||
name = re.sub('[_\s]+', ' ', name)
|
||||
matches = re.match(r'([^-]+)(?:-(.*))?', name)
|
||||
familyname = FontnameTools.camel_casify(matches.group(1))
|
||||
|
@ -345,7 +346,6 @@ class FontnameTools:
|
|||
# Some font specialities:
|
||||
other = [
|
||||
'-', 'Book', 'For', 'Powerline',
|
||||
'Text', # Plex
|
||||
'IIx', # Profont IIx
|
||||
'LGC', # Inconsolata LGC
|
||||
r'\bCE\b', # ProggycleanTT CE
|
||||
|
@ -353,19 +353,9 @@ class FontnameTools:
|
|||
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
|
||||
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
|
||||
( style, other_token ) = FontnameTools.get_name_token(style, other)
|
||||
while 'Regular' in style_token and len(style_token) > 1:
|
||||
# Correct situation where "Regular" and something else is given
|
||||
style_token.remove('Regular')
|
||||
|
|
167
font-patcher
167
font-patcher
|
@ -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.1"
|
||||
script_version = "4.3.3"
|
||||
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
projectName = "Nerd Fonts"
|
||||
projectNameAbbreviation = "NF"
|
||||
projectNameSingular = projectName[:-1]
|
||||
|
@ -250,11 +250,11 @@ def force_panose_monospaced(font):
|
|||
def get_advance_width(font, extended, minimum):
|
||||
""" Get the maximum/minimum advance width in the extended(?) range """
|
||||
width = 0
|
||||
if extended:
|
||||
end = 0x17f
|
||||
if not extended:
|
||||
r = range(0x021, 0x07e)
|
||||
else:
|
||||
end = 0x07e
|
||||
for glyph in range(0x21, end):
|
||||
r = range(0x07f, 0x17f)
|
||||
for glyph in r:
|
||||
if not glyph in font:
|
||||
continue
|
||||
if glyph in range(0x7F, 0xBF):
|
||||
|
@ -270,8 +270,8 @@ def get_advance_width(font, extended, minimum):
|
|||
|
||||
def report_advance_widths(font):
|
||||
return "Advance widths (base/extended): {} - {} / {} - {}".format(
|
||||
get_advance_width(font, True, True), get_advance_width(font, False, True),
|
||||
get_advance_width(font, False, False), get_advance_width(font, True, False))
|
||||
get_advance_width(font, False, True), get_advance_width(font, False, False),
|
||||
get_advance_width(font, True, True), get_advance_width(font, True, False))
|
||||
|
||||
def get_btb_metrics(font):
|
||||
""" Get the baseline to baseline distance for all three metrics """
|
||||
|
@ -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)
|
||||
|
@ -741,7 +742,7 @@ class font_patcher:
|
|||
|
||||
def remove_ligatures(self):
|
||||
# let's deal with ligatures (mostly for monospaced fonts)
|
||||
# the tables have been removed from the repo with >this< commit
|
||||
# Usually removes 'fi' ligs that end up being only one cell wide, and 'ldot'
|
||||
if self.args.configfile and self.config.read(self.args.configfile):
|
||||
if self.args.removeligatures:
|
||||
logger.info("Removing ligatures from configfile `Subtables` section")
|
||||
|
@ -764,16 +765,18 @@ class font_patcher:
|
|||
if self.args.nonmono:
|
||||
return
|
||||
panose_mono = check_panose_monospaced(self.sourceFont)
|
||||
logger.debug("Monospace check: %s; glyph-width-mono %s",
|
||||
panose_check_to_text(panose_mono, self.sourceFont.os2_panose), repr(width_mono))
|
||||
# The following is in fact "width_mono != panose_mono", but only if panose_mono is not 'unknown'
|
||||
if (width_mono and panose_mono == 0) or (not width_mono and panose_mono == 1):
|
||||
logger.warning("Monospaced check: Panose assumed to be wrong")
|
||||
logger.warning(" %s and %s",
|
||||
logger.warning("Monospaced check: %s and %s",
|
||||
report_advance_widths(self.sourceFont),
|
||||
panose_check_to_text(panose_mono, self.sourceFont.os2_panose))
|
||||
if self.args.single and not width_mono:
|
||||
logger.warning("Sourcefont is not monospaced - forcing to monospace not advisable, results might be useless")
|
||||
if offending_char is not None:
|
||||
logger.warning(" Offending char: %X", offending_char)
|
||||
logger.warning("Sourcefont is not monospaced - forcing to monospace not advisable, "
|
||||
"results might be useless%s",
|
||||
" - offending char: {:X}".format(offending_char) if offending_char is not None else "")
|
||||
if self.args.single <= 1:
|
||||
logger.critical("Font will not be patched! Give --mono (or -s, or --use-single-width-glyphs) twice to force patching")
|
||||
sys.exit(1)
|
||||
|
@ -784,7 +787,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 +805,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 +813,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 +891,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 +901,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 +962,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 +991,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 +1032,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 +1054,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 +1097,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 +1137,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 +1149,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)
|
||||
|
@ -1190,6 +1207,7 @@ class font_patcher:
|
|||
0x132, 0x133, # IJ, ij (in Overpass Mono)
|
||||
0x022, 0x027, 0x060, # Single and double quotes in Inconsolata LGC
|
||||
0x0D0, 0x10F, 0x110, 0x111, 0x127, 0x13E, 0x140, 0x165, # Eth and others with stroke or caron in RobotoMono
|
||||
0x149, # napostrophe in DaddyTimeMono
|
||||
0x02D, # hyphen for Monofur
|
||||
]:
|
||||
continue # ignore special characters like '1/4' etc and some specifics
|
||||
|
@ -1201,8 +1219,8 @@ class font_patcher:
|
|||
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
||||
self.font_dim['width'] = self.sourceFont[glyph].width
|
||||
if not warned1 and glyph > 0x7a: # NOT 'basic' glyph, which includes a-zA-Z
|
||||
logger.debug("Extended glyphs wider than basic glyphs, results might be useless\n %s",
|
||||
report_advance_widths(self.sourceFont))
|
||||
logger.debug("Extended glyphs wider than basic glyphs, results might be useless")
|
||||
logger.debug("%s", report_advance_widths(self.sourceFont))
|
||||
warned1 = True
|
||||
# print("New MAXWIDTH-A {:X} {} -> {} {}".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||
if xmax > self.font_dim['xmax']:
|
||||
|
@ -1214,7 +1232,7 @@ class font_patcher:
|
|||
if self.font_dim['width'] < self.font_dim['xmax']:
|
||||
logger.debug("Font has negative right side bearing in extended glyphs")
|
||||
self.font_dim['xmax'] = self.font_dim['width'] # In fact 'xmax' is never used
|
||||
# print("FINAL", self.font_dim)
|
||||
logger.debug("Final font cell dimensions %d w x %d h", self.font_dim['width'], self.font_dim['height'])
|
||||
|
||||
self.xavgwidth.append(self.args.xavgwidth)
|
||||
if isinstance(self.xavgwidth[-1], int) and self.xavgwidth[-1] == 0:
|
||||
|
@ -1240,7 +1258,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 +1342,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 +1354,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 +1367,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 +1394,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 +1455,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 +1468,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 +1510,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 +1564,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 +1598,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 +1617,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 +1625,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 +1831,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 +1855,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 +1978,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)
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue