Skip to content

Commit

Permalink
Merge pull request #1733 from ryanoasis/feature/progress-indicators
Browse files Browse the repository at this point in the history
font-patcher: Add progress indicators
  • Loading branch information
Finii authored Nov 17, 2024
2 parents d43e4a2 + 33d4af7 commit d5d0f2d
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 31 deletions.
100 changes: 69 additions & 31 deletions font-patcher
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from __future__ import absolute_import, print_function, unicode_literals

# Change the script version when you edit this script:
script_version = "4.15.0"
script_version = "4.16.0"

version = "3.2.1"
projectName = "Nerd Fonts"
Expand Down Expand Up @@ -964,6 +964,16 @@ class font_patcher:
# 0x2592: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'dont_copy': box_keep}},
# 0x2593: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'dont_copy': box_keep}},
}
SYM_ATTR_PROGRESS = {
'default': {'align': 'c', 'valign': 'c', 'stretch': '^pa1!', 'params': {'overlap': -0.03, 'careful': True}}, # Cirles
# All the squares:
0xee00: {'align': 'r', 'valign': 'c', 'stretch': '^xy', 'params': {'overlap': 0.05, 'careful': True}},
0xee01: {'align': 'c', 'valign': 'c', 'stretch': '^xy', 'params': {'overlap': 0.10, 'careful': True}},
0xee02: {'align': 'l', 'valign': 'c', 'stretch': '^xy', 'params': {'overlap': 0.05, 'careful': True}},
0xee03: {'align': 'r', 'valign': 'c', 'stretch': '^xy', 'params': {'overlap': 0.05, 'careful': True}},
0xee04: {'align': 'c', 'valign': 'c', 'stretch': '^xy', 'params': {'overlap': 0.10, 'careful': True}},
0xee05: {'align': 'l', 'valign': 'c', 'stretch': '^xy', 'params': {'overlap': 0.05, 'careful': True}},
}
CUSTOM_ATTR = {
# previous custom scaling => do not touch the icons
# 'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
Expand All @@ -983,9 +993,9 @@ class font_patcher:
# Shifting in addition to scaling can be selected too (see below).
# - ScaleGroups:
# Here you specify a group of glyphs that should be handled together
# with the same scaling and shifting. The basis for it is a 'combined
# bounding box' of all glyphs in that group. All glyphs are handled as
# if they fill that combined bounding box.
# with the same scaling and shifting (see bottom). The basis for it is
# a 'combined bounding box' of all glyphs in that group. All glyphs are
# handled as if they fill that combined bounding box.
# (- ScaleGroupsVert: Removed with this commit)
#
# The ScaleGlyph method: You set 'ScaleGlyph' to the unicode of the reference glyph.
Expand All @@ -1002,7 +1012,7 @@ class font_patcher:
#
# For the ScaleGroup method you define any number groups of glyphs and each group is
# handled separately. The combined bounding box of all glyphs in the group is determined
# and based on that the scale and shift for all the glyphs in the group.
# and based on that the scale and shift (see bottom) for all the glyphs in the group.
# You define the groups as value of 'ScaleGroups'.
# It is a List of: ((lists of glyph codes) or (ranges of glyph codes))
# 'ScaleGroups': [
Expand All @@ -1013,15 +1023,21 @@ class font_patcher:
# See prepareScaleRules() for some more details.
# For historic reasons ScaleGroups is sometimes called 'new method' and ScaleGlyph 'old'.
# The codepoints mentioned here are symbol-font-codepoints.

BOX_SCALE_LIST = {'ScaleGroups': [
#
# Shifting:
# If we have a combined bounding box stored in a range, that
# box is used to align all symbols in the range identically.
# - If the symbol font is proportinal only the v alignment is synced.
# - If the symbol font is monospaced v and h alignemnts are synced.
# To make sure the behavior is as expected you are required to set a ShiftMode property
# accordingly. It just checks, you can not (!) select what is done with that property.

BOX_SCALE_LIST = {'ShiftMode': 'xy', 'ScaleGroups': [
[*range(0x2500, 0x2570 + 1), *range(0x2574, 0x257f + 1)], # box drawing
range(0x2571, 0x2573 + 1), # diagonals
[*range(0x2580, 0x2590 + 1), 0x2594, 0x2595], # blocks
range(0x2591, 0x2593 + 1), # greys
range(0x2594, 0x259f + 1), # quards (Note: quard 2597 in Hack is wrong, scales like block!)
range(0x2580, 0x259f + 1), # blocks and greys (greys are less tall originally, so overlap will be less)
]}
CODI_SCALE_LIST = {'ScaleGroups': [
CODI_SCALE_LIST = {'ShiftMode': 'xy', 'ScaleGroups': [
[0xea61, 0xeb13], # lightbulb
range(0xeab4, 0xeab7 + 1), # chevrons
[0xea7d, *range(0xea99, 0xeaa1 + 1), 0xebcb], # arrows
Expand All @@ -1033,7 +1049,7 @@ class font_patcher:
range(0xebd5, 0xebd7 + 1), # compasses
]}
DEVI_SCALE_LIST = None
FONTA_SCALE_LIST = {'ScaleGroups': [
FONTA_SCALE_LIST = {'ShiftMode': '', 'ScaleGroups': [
[0xf005, 0xf006, 0xf089], # star, star empty, half star
range(0xf026, 0xf028 + 1), # volume off, down, up
range(0xf02b, 0xf02c + 1), # tag, tags
Expand All @@ -1054,11 +1070,11 @@ class font_patcher:
range(0xf221, 0xf22d + 1), # gender or so
range(0xf255, 0xf25b + 1), # hand symbols
]}
HEAVY_SCALE_LIST = {'ScaleGlyph': 0x2771, # widest bracket, horizontally
HEAVY_SCALE_LIST = {'ShiftMode': 'y', 'ScaleGlyph': 0x2771, # widest bracket, horizontally
'GlyphsToScale': [
(0x276c, 0x2771) # all
]}
OCTI_SCALE_LIST = {'ScaleGroups': [
OCTI_SCALE_LIST = {'ShiftMode': '', 'ScaleGroups': [
[*range(0xf03d, 0xf040 + 1), 0xf019, 0xf030, 0xf04a, 0xf051, 0xf071, 0xf08c ], # arrows
[0xF0E7, # Smily and ...
0xf044, 0xf05a, 0xf05b, 0xf0aa, # triangles
Expand All @@ -1070,7 +1086,11 @@ class font_patcher:
range(0xf2c2, 0xf2c5 + 1), # move to
[0xf07b, 0xf0a1, 0xf0d6, 0xf306], # bookmarks
]}
WEATH_SCALE_LIST = {'ScaleGroups': [
PROGR_SCALE_LIST = {'ShiftMode': 'xy', 'ScaleGroups': [
range(0xedff, 0xee05 + 1), # boxes... with helper glyph for Y padding
range(0xee06, 0xee0b + 1), # circles
]}
WEATH_SCALE_LIST = {'ShiftMode': '', 'ScaleGroups': [
[0xf03c, 0xf042, 0xf045 ], # degree signs
[0xf043, 0xf044, 0xf048, 0xf04b, 0xf04c, 0xf04d, 0xf057, 0xf058, 0xf087, 0xf088], # arrows
range(0xf053, 0xf055 + 1), # thermometers
Expand Down Expand Up @@ -1104,6 +1124,7 @@ class font_patcher:
{'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': 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': "Progress Indicators", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0xEE00, 'SymEnd': 0xEE0B, 'SrcStart': None, 'ScaleRules': PROGR_SCALE_LIST, 'Attributes': SYM_ATTR_PROGRESS},
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons/devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE7E3, '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},
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0B0, 'SymEnd': 0xE0B3, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE},
Expand Down Expand Up @@ -1344,25 +1365,29 @@ class font_patcher:
return 1
return 2

def get_scale_factors(self, sym_dim, stretch):
def get_scale_factors(self, sym_dim, stretch, overlap=None):
""" Get scale in x and y as tuple """
# It is possible to have empty glyphs, so we need to skip those.
if not sym_dim['width'] or not sym_dim['height']:
return (1.0, 1.0)

target_width = self.font_dim['width'] * self.get_target_width(stretch)
if overlap:
target_width += self.font_dim['width'] * overlap
scale_ratio_x = target_width / sym_dim['width']

# 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'] if '^' in stretch else self.font_dim['iconheight']
target_height *= 1.0 - self.font_dim['ypadding']
if overlap:
target_height *= 1.0 + min(0.01, overlap) # never aggressive vertical overlap
scale_ratio_y = target_height / sym_dim['height']

if 'pa' in stretch:
# We want to preserve x/y aspect ratio, so find biggest scale factor that allows symbol to fit
scale_ratio_x = min(scale_ratio_x, scale_ratio_y)
if not self.args.single and not '!' in stretch:
if not self.args.single and not '!' in stretch and not overlap:
# non monospaced fonts just scale down on 'pa', not up
scale_ratio_x = min(scale_ratio_x, 1.0)
scale_ratio_y = scale_ratio_x
Expand Down Expand Up @@ -1502,25 +1527,26 @@ class font_patcher:

# Prepare symbol glyph dimensions
sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph])
overlap = sym_attr['params'].get('overlap')
if overlap and ypadding:
logger.critical("Conflicting params: overlap and ypadding")
sys.exit(1)

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, stretch)
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, stretch, overlap)
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])
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
scale_ratio_y *= 1.0 + (self.font_dim['height'] / (sym_dim['height'] * scale_ratio_y)) * y_overlap
else:
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, stretch)
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, stretch, overlap)

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
scale_ratio_y *= 1.0 + (self.font_dim['height'] / (sym_dim['height'] * scale_ratio_y)) * y_overlap

# Size in x to size in y ratio limit (to prevent over-wide glyphs)
xy_ratio_max = sym_attr['params'].get('xy-ratio')
Expand Down Expand Up @@ -1567,15 +1593,17 @@ class font_patcher:
elif sym_attr['align'] == 'r':
# Right align
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)
if not overlap:
# 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)

if overlap:
overlap_width = self.font_dim['width'] * overlap
if sym_attr['align'] == 'l':
x_align_distance -= overlap_width
elif sym_attr['align'] == 'c':
if overlap_width > 0:
# center aligned keeps being center aligned even with overlap
if overlap_width < 0 and self.args.nonmono and sym_dim['advance'] is None: # Keep positive bearing due to negative overlap (propo)
x_align_distance -= overlap_width / 2
elif sym_attr['align'] == 'r' and not self.args.nonmono:
# Check and correct overlap; it can go wrong if we have a xy-ratio limit
Expand Down Expand Up @@ -1609,7 +1637,7 @@ class font_patcher:
else:
width = sym_dim['width']
# If we have overlap we need to subtract that to keep/get negative bearings
if overlap and (sym_attr['align'] == 'l' or sym_attr['align'] == 'r'):
if overlap:
width -= overlap_width
# Fontforge handles the width change like this:
# - Keep existing left_side_bearing
Expand Down Expand Up @@ -1707,11 +1735,21 @@ class font_patcher:
scaleRules['bbdims'] = []
if 'ScaleGroups' not in scaleRules:
scaleRules['ScaleGroups'] = []

mode = scaleRules['ShiftMode'] # Mode is only documentary
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, stretch)[0]
scaleRules['scales'].append(scale)
scaleRules['bbdims'].append(sym_dim)
if (mode):
if ('x' in mode) != (sym_dim['advance'] is not None):
d = '0x{:X} - 0x{:X}'.format(group[0], group[-1])
if ('x' in mode) :
logger.critical("Scaling in group %s is expected to do horizonal shifts but can not", d)
else:
logger.critical("Scaling in group %s is expected to not do horizonal shifts but will", d)
sys.exit(1)

if 'ScaleGlyph' in scaleRules:
# Rewrite to equivalent ScaleGroup
Expand Down
Binary file modified src/glyphs/extraglyphs.sfd
Binary file not shown.

0 comments on commit d5d0f2d

Please sign in to comment.