diff --git a/font-patcher b/font-patcher index 7cd08de700..d7fb83bd66 100755 --- a/font-patcher +++ b/font-patcher @@ -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" @@ -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': {}} @@ -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. @@ -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': [ @@ -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 @@ -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 @@ -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 @@ -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 @@ -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}, @@ -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 @@ -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') @@ -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 @@ -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 @@ -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 diff --git a/src/glyphs/extraglyphs.sfd b/src/glyphs/extraglyphs.sfd index dc5d252d74..15d08c7c9e 100644 Binary files a/src/glyphs/extraglyphs.sfd and b/src/glyphs/extraglyphs.sfd differ