From c3fd8991710633bc7bd682d919812527e9e0691a Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 12 Oct 2022 17:43:26 +0200 Subject: [PATCH 01/25] font-patcher: Fix new ScaleGlyph (for codepoint F000) [why] First the new ScaleGlyph has been introduced with commit e5768e925 font-patcher: Redesign ScaleGlyph parameter and afterwards it has been enhanced to avoid rounding errors with commit 983226a70 font-patcher: Fix scaleGlyph related rounding error The later commit uses a function that explicitely says it will destroy the glyph at a specific location, AFTER we already patched in one glyph (namely F000). It does not look too bad, bad that glyph is not correctly rescaled or translated. Only that glyph is affected because only Font Awesome uses the new ScaleGlyph capabilities, and only the first glyph of a set is affected. [how] The ScaleGlyph calculations need to be done before the final glyph is patched in. It is shifted some lines up. Usually that glyph is not existing in the to-be-patched font, so we create a dummy first. Also need to correct distinction between 'unicode in symbol font' and 'unicode in to-be-patched font', as this would bite us in cases where we move the symbol's codepoint. Signed-off-by: Fini Jastrow --- font-patcher | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/font-patcher b/font-patcher index 8fe31d458c..36a8af530f 100755 --- a/font-patcher +++ b/font-patcher @@ -988,6 +988,9 @@ class font_patcher: if currentSourceFontGlyph in self.sourceFont: self.sourceFont[currentSourceFontGlyph].removePosSub("*") + # This will destroy any content currently in currentSourceFontGlyph, so do it first + scale_glyph_data = self.get_glyph_scale(sym_glyph.encoding, scaleGlyph, symbolFont, currentSourceFontGlyph) if scaleGlyph 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 # reset our selection before starting the next loop @@ -1015,7 +1018,7 @@ class font_patcher: scale_ratio_x = False if scaleGlyph: # We want to preserve the relative size of each glyph in a glyph group - scale_ratio_x = self.get_glyph_scale(sym_glyph.unicode, scaleGlyph, symbolFont) + scale_ratio_x = scale_glyph_data if scale_ratio_x is False: # In the remaining cases, each glyph is sized independently to each other scale_ratio_x = self.get_scale_factor(sym_dim) @@ -1187,13 +1190,15 @@ class font_patcher: sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ], destGlyph) scaleGlyph['scales'].append(self.get_scale_factor(sym_dim)) - def get_glyph_scale(self, unicode_value, scaleGlyph, symbolFont): + def get_glyph_scale(self, symbol_unicode, scaleGlyph, symbolFont, dest_unicode): """ Determines whether or not to use scaled glyphs for glyphs in passed glyph_list """ - # Potentially destorys the contents of self.sourceFont[unicode_value] + # Potentially destorys the contents of self.sourceFont[dest_unicode] if not 'scales' in scaleGlyph: - self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[unicode_value]) + if not dest_unicode in self.sourceFont: + self.sourceFont.createChar(dest_unicode) + self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[dest_unicode]) for glyph_list, scale in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales']): - if unicode_value in glyph_list: + if symbol_unicode in glyph_list: return scale return False From a7cefb145ccc1f35fdbc0d33a39669d8e1d53bb1 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 12 Oct 2022 14:07:12 +0200 Subject: [PATCH 02/25] font-patcher: Add message when redistributing linegap Signed-off-by: Fini Jastrow --- font-patcher | 1 + 1 file changed, 1 insertion(+) diff --git a/font-patcher b/font-patcher index 36a8af530f..40e0898ffa 100755 --- a/font-patcher +++ b/font-patcher @@ -870,6 +870,7 @@ class font_patcher: if gap > 0: gap_top = int(gap / 2) gap_bottom = gap - gap_top + print("Redistributing line gap of {} ({} top and {} bottom)".format(gap, gap_top, gap_bottom)) self.font_dim['ymin'] -= gap_bottom self.font_dim['ymax'] += gap_top self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax'] From 6ffd8497bd0f186004770b59323e6fd4b11df8bf Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Thu, 2 Jun 2022 13:23:54 +0200 Subject: [PATCH 03/25] font-patcher: Fix empty glyph handling in get_multiglyph_boundingBox() [why] When the combinded bounding box of a range of glyphs is to be determined and the range contains an empty glyph the resulting bounding box will always include the [0, 0] point (that is the point-ish bounding box of the empty glyph). [how] If more than one codepoint is to be considered empty glyphs are ignored. But if only one (concrete) codepoint is queried do return the empty (i.e. [0, 0, 0, 0]) bounding box. Signed-off-by: Fini Jastrow --- font-patcher | 3 +++ 1 file changed, 3 insertions(+) diff --git a/font-patcher b/font-patcher index 40e0898ffa..37cf4267b7 100755 --- a/font-patcher +++ b/font-patcher @@ -1250,6 +1250,9 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): destGlyph.font.paste() glyph = destGlyph gbb = glyph.boundingBox() + if len(glyphs) > 1 and gbb[0] == gbb[2] and gbb[1] == gbb[3]: + # Ignore empty glyphs if we examine more than one glyph + continue bbox[0] = gbb[0] if bbox[0] is None or bbox[0] > gbb[0] else bbox[0] bbox[1] = gbb[1] if bbox[1] is None or bbox[1] > gbb[1] else bbox[1] bbox[2] = gbb[2] if bbox[2] is None or bbox[2] < gbb[2] else bbox[2] From 387448e68727543c5c06d1dec07db9b363241e29 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Thu, 2 Jun 2022 13:29:56 +0200 Subject: [PATCH 04/25] font-patcher: Store combined BB in ScaleGlyph [why] If we use a ScaleGlyph range (i.e. use the same scaling on a range of glyphs; the scaling is determined by the combined bounding box of all that glyphs), we probably want to shift the glyphs identically as well to preserve relative positions not only sizes of the glyphs within the range. But the data is stored nowhere. [how] Store the 'virtual' bounding box along with the scale. [note] With combined (virtual) bounding box we mean the bounding box that a glyph would have where all the individual glyphs would be stamped over each other without shifting/scaling beforehand. Signed-off-by: Fini Jastrow --- font-patcher | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/font-patcher b/font-patcher index 37cf4267b7..752fdf4c59 100755 --- a/font-patcher +++ b/font-patcher @@ -1017,9 +1017,9 @@ class font_patcher: # to fit in both the x and y directions if sym_attr['stretch'] == 'pa': scale_ratio_x = False - if scaleGlyph: + if scale_glyph_data: # We want to preserve the relative size of each glyph in a glyph group - scale_ratio_x = scale_glyph_data + scale_ratio_x = scale_glyph_data[0] if scale_ratio_x is False: # In the remaining cases, each glyph is sized independently to each other scale_ratio_x = self.get_scale_factor(sym_dim) @@ -1158,12 +1158,16 @@ class font_patcher: # The GlyphData is a dict with these (possible) entries: # 'GlyphsToScale': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled # 'scales': List of associated scale factors, one for each entry in 'GlyphsToScale' (generated by this function) + # 'bbdims': List of associated sym_dim dicts, one for each entry in 'GlyphsToScale' (generated by this function) + # Each sym_dim dict describes the combined bounding box of all glyphs in GlyphsToScale # Example: # { 'GlyphsToScale': [ range(1, 3), [ 7, 10 ], ], - # 'scales': [ 1.23, 1.33, ] } + # 'scales': [ 1.23, 1.33, ], + # 'bbdims': [ dim_dict1, dim_dict2, ] } # # Each item in 'GlyphsToScale' (a range or an explicit list) forms a group of glyphs that shall be # as rescaled all with the same and maximum possible (for the included glyphs) factor. + # If the 'bbdims' is present they all shall be shifted in the same way. # # Previously this structure has been used: # 'ScaleGlyph' Lead glyph, which scaling factor is taken @@ -1184,12 +1188,18 @@ class font_patcher: flat_list.append(i) scaleGlyph['GlyphsToScale'] = [ flat_list ] sym_dim = get_glyph_dimensions(symbolFont[scaleGlyph['ScaleGlyph']]) - scaleGlyph['scales'] = [ self.get_scale_factor(sym_dim) ] + scale = self.get_scale_factor(sym_dim) + scaleGlyph['scales'] = [ scale ] + # The 'old' style keeps just the scale, not the positioning + scaleGlyph['bbdims'] = [ None ] else: scaleGlyph['scales'] = [] + scaleGlyph['bbdims'] = [] for group in scaleGlyph['GlyphsToScale']: sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ], destGlyph) - scaleGlyph['scales'].append(self.get_scale_factor(sym_dim)) + scale = self.get_scale_factor(sym_dim) + scaleGlyph['scales'].append(scale) + scaleGlyph['bbdims'].append(sym_dim) def get_glyph_scale(self, symbol_unicode, scaleGlyph, symbolFont, dest_unicode): """ Determines whether or not to use scaled glyphs for glyphs in passed glyph_list """ @@ -1198,9 +1208,9 @@ class font_patcher: if not dest_unicode in self.sourceFont: self.sourceFont.createChar(dest_unicode) self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[dest_unicode]) - for glyph_list, scale in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales']): + for glyph_list, scale, box in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales'], scaleGlyph['bbdims']): if symbol_unicode in glyph_list: - return scale + return (scale, box) return False From aa86e407bf476ce6260497b8bedf496deaf62a6c Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Thu, 13 Oct 2022 12:18:24 +0200 Subject: [PATCH 05/25] font-patcher: Store if symbol font is monospaced in ScaleGlyph [why] We might want to handle monospaced symbol fonts differently then proportional symbol fonts. Proportional symbol fonts usually have no uniform symbol scaling, while monospaced symbol fonts usually have a well designed placement of the symbols within the cell. [how] Add new member to the dimensions dict. Signed-off-by: Fini Jastrow --- font-patcher | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/font-patcher b/font-patcher index 752fdf4c59..e854fd3ff8 100755 --- a/font-patcher +++ b/font-patcher @@ -1248,7 +1248,7 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): # If destGlyph is given the glyph(s) are first copied over into that # glyph and measured in that font (to avoid rounding errors) # Leaves the destGlyph in unknown state! - bbox = [ None, None, None, None ] + bbox = [ None, None, None, None, None ] for glyph in glyphs: if glyph is None: # Glyph has been in defining range but is not in the actual font @@ -1260,6 +1260,7 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): destGlyph.font.paste() glyph = destGlyph gbb = glyph.boundingBox() + gadvance = glyph.width if len(glyphs) > 1 and gbb[0] == gbb[2] and gbb[1] == gbb[3]: # Ignore empty glyphs if we examine more than one glyph continue @@ -1267,6 +1268,13 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): bbox[1] = gbb[1] if bbox[1] is None or bbox[1] > gbb[1] else bbox[1] bbox[2] = gbb[2] if bbox[2] is None or bbox[2] < gbb[2] else bbox[2] bbox[3] = gbb[3] if bbox[3] is None or bbox[3] < gbb[3] else bbox[3] + if not bbox[4]: + bbox[4] = gadvance + else: + if bbox[4] != gadvance: + bbox[4] = -1 # Marker for not-monospaced + if bbox[4] and bbox[4] < 0: + bbox[4] = None return { 'xmin' : bbox[0], 'ymin' : bbox[1], @@ -1274,6 +1282,7 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): 'ymax' : bbox[3], 'width' : bbox[2] + (-bbox[0]), 'height': bbox[3] + (-bbox[1]), + 'advance': bbox[4], # advance width if monospaced } def get_glyph_dimensions(glyph): From effa1cf464e7a4ba15bfe58830a54d16fb3f3c02 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Thu, 13 Oct 2022 12:28:37 +0200 Subject: [PATCH 06/25] font-patcher: Use ScaleGlyph BB to align glyph [why] If we define glyph groups in ScaleGlyph we want them to be scaled alike, but they are aligned individually, which makes previously matching pairs looking odd. [how] If we have a combined bounding box stored in a ScaleGlyph 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. Signed-off-by: Fini Jastrow --- font-patcher | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/font-patcher b/font-patcher index e854fd3ff8..53845a099f 100755 --- a/font-patcher +++ b/font-patcher @@ -1048,8 +1048,20 @@ class font_patcher: scale_ratio_y *= 1 + overlap self.sourceFont[currentSourceFontGlyph].transform(psMat.scale(scale_ratio_x, scale_ratio_y)) - # Use the dimensions from the newly pasted and stretched glyph + # We pasted and scaled now we want to align/move + # Use the dimensions from the newly pasted and stretched glyph to avoid any rounding errors sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]) + # Use combined bounding box? + if scale_glyph_data and scale_glyph_data[1] and (scale_ratio_x != 1 or scale_ratio_y != 1): + # Simulate scaling on combined bounding box + scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) + if not scaleglyph_dim['advance']: + # On monospaced symbol collections use their advance with, otherwise align horizontally individually + scaleglyph_dim['xmin'] = sym_dim['xmin'] + scaleglyph_dim['xmax'] = sym_dim['xmax'] + scaleglyph_dim['width'] = sym_dim['width'] + sym_dim = scaleglyph_dim + y_align_distance = 0 if sym_attr['valign'] == 'c': # Center the symbol vertically by matching the center of the line height and center of symbol @@ -1289,6 +1301,19 @@ def get_glyph_dimensions(glyph): """ Returns dict of the dimesions of the glyph passed to it. """ return get_multiglyph_boundingBox([ glyph ]) +def scale_bounding_box(bbox, scale_x, scale_y): + """ Return a scaled version of a glyph dimensions dict """ + new_dim = { + 'xmin' : int(bbox['xmin'] * scale_x), + 'ymin' : int(bbox['ymin'] * scale_y), + 'xmax' : int(bbox['xmax'] * scale_x), + 'ymax' : int(bbox['ymax'] * scale_y), + 'advance': int(bbox['advance'] * scale_y) if bbox['advance'] else None, + } + new_dim['width'] = new_dim['xmax'] + (-new_dim['xmin']) + new_dim['height'] = new_dim['ymax'] + (-new_dim['ymin']) + return new_dim + def update_progress(progress): """ Updates progress bar length. From 8c66e38bfffb9e0c6068dc474694e62781978802 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Thu, 13 Oct 2022 12:34:00 +0200 Subject: [PATCH 07/25] font-patcher: Whitespace cleanup [why] Man do I hate these indented tables in code. Specifically because they break conciseness of commits if one needs to re-indent unrelated entries. [how] Do it in this separate commit that does not change functionality. [note] Smuggled in unrelated code shift by some lines. Signed-off-by: Fini Jastrow --- font-patcher | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/font-patcher b/font-patcher index 53845a099f..a59327e355 100755 --- a/font-patcher +++ b/font-patcher @@ -1002,11 +1002,11 @@ class font_patcher: self.sourceFont.selection.select(currentSourceFontGlyph) self.sourceFont.paste() self.sourceFont[currentSourceFontGlyph].glyphname = sym_glyph.glyphname - scale_ratio_x = 1 - scale_ratio_y = 1 # Prepare symbol glyph dimensions sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]) + scale_ratio_x = 1 + scale_ratio_y = 1 # Now that we have copy/pasted the glyph, if we are creating a monospace # font we need to scale and move the glyphs. It is possible to have @@ -1288,12 +1288,12 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): if bbox[4] and bbox[4] < 0: bbox[4] = None return { - 'xmin' : bbox[0], - 'ymin' : bbox[1], - 'xmax' : bbox[2], - 'ymax' : bbox[3], - 'width' : bbox[2] + (-bbox[0]), - 'height': bbox[3] + (-bbox[1]), + 'xmin' : bbox[0], + 'ymin' : bbox[1], + 'xmax' : bbox[2], + 'ymax' : bbox[3], + 'width' : bbox[2] + (-bbox[0]), + 'height' : bbox[3] + (-bbox[1]), 'advance': bbox[4], # advance width if monospaced } From 40190c9362a5d65a3f5cf9f6f86204c350dada4f Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 12 Oct 2022 18:44:16 +0200 Subject: [PATCH 08/25] font-patcher: Fix wrong ScaleGlyph in Font Awesome [why] This is obviously a complete mess. Firstly it seems the author (me) used the array elements as ranges, which is of course not possible. And them the end has a typo. Sigh. [how] Not consecutive codes must all be given one by one (unfortunately). Signed-off-by: Fini Jastrow --- font-patcher | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/font-patcher b/font-patcher index a59327e355..0c0990fb85 100755 --- a/font-patcher +++ b/font-patcher @@ -756,7 +756,7 @@ class font_patcher: range(0xf060, 0xf063 + 1), # arrows [0xf053, 0xf054, 0xf077, 0xf078], # chevron all directions range(0xf07d, 0xf07e + 1), # resize - [0xf0d7, 0xf0da, 0xf0dc, 0xf0fe], # caret all directions and same looking sort + [0xf0d7, 0xf0d8, 0xf0d9, 0xf0da, 0xf0dc, 0xf0dd, 0xf0de], # caret all directions and same looking sort range(0xf100, 0xf107 + 1), # angle range(0xf141, 0xf142 + 1), # ellipsis range(0xf153, 0xf15a + 1), # currencies From 2e49eb221ea807c5baf47a07a409f0d9fa7874c4 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Thu, 13 Oct 2022 12:39:47 +0200 Subject: [PATCH 09/25] font-patcher: Add more entries to ScaleGlyph of Font Awesome Signed-off-by: Fini Jastrow --- font-patcher | 2 ++ 1 file changed, 2 insertions(+) diff --git a/font-patcher b/font-patcher index 0c0990fb85..4554ed227b 100755 --- a/font-patcher +++ b/font-patcher @@ -756,8 +756,10 @@ class font_patcher: range(0xf060, 0xf063 + 1), # arrows [0xf053, 0xf054, 0xf077, 0xf078], # chevron all directions range(0xf07d, 0xf07e + 1), # resize + range(0xf0a4, 0xf0a7 + 1), # pointing hands [0xf0d7, 0xf0d8, 0xf0d9, 0xf0da, 0xf0dc, 0xf0dd, 0xf0de], # caret all directions and same looking sort range(0xf100, 0xf107 + 1), # angle + range(0xf130, 0xf131 + 1), # mic range(0xf141, 0xf142 + 1), # ellipsis range(0xf153, 0xf15a + 1), # currencies range(0xf175, 0xf178 + 1), # long arrows From 247e30f659f95b6df1479c6c0882df59a50122f4 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 7 Sep 2022 15:56:14 +0200 Subject: [PATCH 10/25] font-patcher: Fix unequal weather icon scale [why] The weather icons have some symbols that have a different bounding box but should nevertheless be scaled alike, because for example one is the outer line of a thermometer and one is the matching stem. [how] Just add a scaleGlyph set. Fixes: #915 Signed-off-by: Fini Jastrow --- font-patcher | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/font-patcher b/font-patcher index 4554ed227b..ae3a848331 100755 --- a/font-patcher +++ b/font-patcher @@ -742,6 +742,8 @@ class font_patcher: # Most glyphs we want to maximize during the scale. However, there are some # that need to be small or stay relative in size to each other. # The following list are those glyphs. A tuple represents a range. + # See prepareScaleGlyph() for an explanation how this needs to look like. + # The codepoints mentioned here are symbol-font-codepoints. DEVI_SCALE_LIST = {'ScaleGlyph': 0xE60E, # Android logo 'GlyphsToScale': [ (0xe6bd, 0xe6c3) # very small things @@ -776,6 +778,12 @@ class font_patcher: 0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons 0xf0ca, # dash ]} + WEATH_SCALE_LIST = {'GlyphsToScale': [ + range(0xf095, 0xf0b0 + 1), # moon phases + range(0xf0b7, 0xf0c3 + 1), # wind strengths + range(0xf053, 0xf055 + 1), # thermometer + [0xf06e, 0xf070 ], # solar eclipse + ]} # Define the character ranges # Symbol font ranges @@ -794,7 +802,7 @@ class font_patcher: {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x23FB, 'SymEnd': 0x23FE, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Power, Power On/Off, Power On, Sleep {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off) {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, + {'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'ScaleGlyph': WEATH_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, {'Enabled': self.args.fontlogos, 'Name': "Font Logos", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart From 98e24392f77666f3dc70cddf816e5c3429b246d0 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 21 Dec 2022 17:10:04 +0100 Subject: [PATCH 11/25] font-patcher: Fix another weather icon scale [why] The weather icons have a glyph for degress, a small cirle for up. That gets completely destroyed on scaling to fit. [how] Just add a scaleGlyph set. Signed-off-by: Fini Jastrow --- font-patcher | 1 + 1 file changed, 1 insertion(+) diff --git a/font-patcher b/font-patcher index ae3a848331..a2d516634d 100755 --- a/font-patcher +++ b/font-patcher @@ -783,6 +783,7 @@ class font_patcher: range(0xf0b7, 0xf0c3 + 1), # wind strengths range(0xf053, 0xf055 + 1), # thermometer [0xf06e, 0xf070 ], # solar eclipse + [0xf042, 0xf045 ], # degree sign ]} # Define the character ranges From f8171b46800ffaeee6f68461e5575fc1e0e456f4 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 21 Dec 2022 18:31:53 +0100 Subject: [PATCH 12/25] font-patcher: Fix vertical alignment for non-Mono with ScaleGlyph [why] When creating a non-Mono font the vertical alignment does not observe a possible ScaleGlyph group. Icons that should be far up (like the degree-icon, which is ScaleGlyph-grouped together with a full height symbol) end up centered vertically. [how] When the glyph is not scaled we just do not use the ScaleGlyph. But that data is also needed for just shifting the glyph. Signed-off-by: Fini Jastrow --- font-patcher | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/font-patcher b/font-patcher index a2d516634d..d8ff337f12 100755 --- a/font-patcher +++ b/font-patcher @@ -1063,9 +1063,12 @@ class font_patcher: # Use the dimensions from the newly pasted and stretched glyph to avoid any rounding errors sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]) # Use combined bounding box? - if scale_glyph_data and scale_glyph_data[1] and (scale_ratio_x != 1 or scale_ratio_y != 1): - # Simulate scaling on combined bounding box - scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) + if scale_glyph_data and scale_glyph_data[1]: + if scale_ratio_x != 1 or scale_ratio_y != 1: + # Simulate scaling on combined bounding box + scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) + else: + scaleglyph_dim = scale_glyph_data[1] if not scaleglyph_dim['advance']: # On monospaced symbol collections use their advance with, otherwise align horizontally individually scaleglyph_dim['xmin'] = sym_dim['xmin'] From 251b4877dd092d9b8e40703a71cbe28d433de1e7 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 21 Dec 2022 18:35:27 +0100 Subject: [PATCH 13/25] font-patcher: Preserve symbol advance with variable width font [why] Very slender symbols added to a proportional patch end up being at least one mono-width wide, which mixes proportional and monospaces metrics. [how] When we create a proportional font we should a) not try to align them in a (non existing) monocpace cell b) insert the symbols with their own (advance) width Signed-off-by: Fini Jastrow --- font-patcher | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/font-patcher b/font-patcher index d8ff337f12..c89c14237b 100755 --- a/font-patcher +++ b/font-patcher @@ -1085,7 +1085,9 @@ class font_patcher: # Handle glyph l/r/c alignment x_align_distance = 0 - if sym_attr['align']: + if self.args.nonmono: + pass + elif sym_attr['align']: # First find the baseline x-alignment (left alignment amount) x_align_distance = self.font_dim['xmin'] - sym_dim['xmin'] if sym_attr['align'] == 'c': @@ -1117,9 +1119,13 @@ class font_patcher: # It should come after setting the glyph bearings self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph]) - # Re-remove negative bearings for target font with variable advance width + # Target font with variable advance width get the icons with their native advance + # or at least width if self.args.nonmono: - self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph]) + if sym_dim['advance']: + self.sourceFont[currentSourceFontGlyph].width = int(sym_dim['advance']) + else: + self.sourceFont[currentSourceFontGlyph].width = int(sym_dim['width']) # Check if the inserted glyph is scaled correctly for monospace if self.args.single: From f20457c3dcf196d7b91d6f174f9d128bd3d526bd Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 2 Jan 2023 16:17:22 +0100 Subject: [PATCH 14/25] font-patcher: Keep overlap in proportional font [why] The previous commit is somewhat incomplete in some cases and plain wrong in others (with proportional fonts). Examples: The Heard 0x2665 gets a positive right side bearing, which is unexpected. The commits prevents negative right side bearings but not positive ones. The glyphs with overlap (which are the Powerline ones) like 0xE0B0 and 0xE0B2 end up in wrong sizes. This can especially be seen with the Symbols-Only (non-Mono) font, because that is (secretly) a 'Nerd Font Propo' (--variable-width-glyphs) font. [how] This is kind of a design choice: As with the other patched font variants we ignore existing borders (positive bearings) around the glyph. The previous commit tried to keep them, which seems to be impossible and is inconsistent). Also negative bearings would be ignored (but there are none). The only place where bearings come into play is now when we have overlap. All non-overlap glyphs render without any bearing. If we have overlap we need to a) reduce the width by the overlap b) introduce a negative bearing on the appropriate side First we remove any left side bearing by transforming the glyph to the side, such that the bearing becomes zero. For left-side overlap we additionally transform the glyph by the overlap amount to the left (as usual). This creates the neg. left bearing. For right-side overlap we keep the left bearing to be zero. After correcting the left-side bearing (by transforming) we set the corrected width. That is the width subtracted by the overlap. In the left-aligned case this makes the right-side bearing zero. In the right-aligned case this results in a negative right-side bearing. Note how fontforge handles size and bearing changes: Fontforge handles the width change like this: - Keep existing left_side_bearing - Set width - Calculate and set new right_side_bearing Signed-off-by: Fini Jastrow --- font-patcher | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/font-patcher b/font-patcher index c89c14237b..fc89b5998d 100755 --- a/font-patcher +++ b/font-patcher @@ -1086,7 +1086,8 @@ class font_patcher: # Handle glyph l/r/c alignment x_align_distance = 0 if self.args.nonmono: - pass + # Remove left side bearing + x_align_distance = -self.sourceFont[currentSourceFontGlyph].left_side_bearing elif sym_attr['align']: # First find the baseline x-alignment (left alignment amount) x_align_distance = self.font_dim['xmin'] - sym_dim['xmin'] @@ -1101,7 +1102,8 @@ class font_patcher: overlap_width = self.font_dim['width'] * overlap if sym_attr['align'] == 'l': x_align_distance -= overlap_width - if sym_attr['align'] == 'r': + if sym_attr['align'] == 'r' and not self.args.nonmono: + # Nonmono is 'left aligned' per definition, translation does not help here x_align_distance += overlap_width align_matrix = psMat.translate(x_align_distance, y_align_distance) @@ -1117,15 +1119,20 @@ class font_patcher: # same width for all character glyphs. This needs to be done for all glyphs, # even the ones that are empty and didn't go through the scaling operations. # It should come after setting the glyph bearings - self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph]) - - # Target font with variable advance width get the icons with their native advance - # or at least width - if self.args.nonmono: - if sym_dim['advance']: - self.sourceFont[currentSourceFontGlyph].width = int(sym_dim['advance']) - else: - self.sourceFont[currentSourceFontGlyph].width = int(sym_dim['width']) + if not self.args.nonmono: + self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph]) + else: + # Target font with variable advance width get the icons with their native widths + # and keeping possible (negative) bearings in effect + 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'): + width -= overlap_width + # Fontforge handles the width change like this: + # - Keep existing left_side_bearing + # - Set width + # - Calculate and set new right_side_bearing + self.sourceFont[currentSourceFontGlyph].width = int(width) # Check if the inserted glyph is scaled correctly for monospace if self.args.single: From 5a64f068eefcb246f387b612bfc8f841ad333c8e Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Mon, 2 Jan 2023 18:00:13 +0100 Subject: [PATCH 15/25] font-patcher: Fix GlyphToScale for 'xy' scales [why] The scale-glyph-data is used only for 'pa' scales, but thereafter used for all shifts, even if the scaling has been 'x' or 'y' or both. As we do not use GlyphToScale for anything but 'pa' scaled glyphs that should not make any difference right now. But it will be an obscure bug if we ever want to handle the Powerline symbols with a scale group. I do not know if that will ever happen, but I tried it whilst experimenting spending hours on finding this bug. [how] Access the GlyphToScale data and use it even for 'x' and 'y' scaling, if we have it for the particular glyph. [note] Also change 'invalid' flag from False to None. Also use 'is None' or 'is not None' for comparisons with None. Signed-off-by: Fini Jastrow --- font-patcher | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/font-patcher b/font-patcher index fc89b5998d..0d49b9c44f 100755 --- a/font-patcher +++ b/font-patcher @@ -1001,7 +1001,7 @@ class font_patcher: self.sourceFont[currentSourceFontGlyph].removePosSub("*") # This will destroy any content currently in currentSourceFontGlyph, so do it first - scale_glyph_data = self.get_glyph_scale(sym_glyph.encoding, scaleGlyph, symbolFont, currentSourceFontGlyph) if scaleGlyph else None + scale_glyph_data = self.get_glyph_scale(sym_glyph.encoding, scaleGlyph, symbolFont, currentSourceFontGlyph) if scaleGlyph 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 @@ -1027,18 +1027,22 @@ class font_patcher: # find the largest possible scaling factor that will allow the glyph # to fit in both the x and y directions if sym_attr['stretch'] == 'pa': - scale_ratio_x = False + scale_ratio_x = None if scale_glyph_data: # We want to preserve the relative size of each glyph in a glyph group scale_ratio_x = scale_glyph_data[0] - if scale_ratio_x is False: + if scale_ratio_x is None: # In the remaining cases, each glyph is sized independently to each other scale_ratio_x = self.get_scale_factor(sym_dim) scale_ratio_y = scale_ratio_x else: if 'x' in sym_attr['stretch']: # Stretch the glyph horizontally to fit the entire available width - scale_ratio_x = self.font_dim['width'] / sym_dim['width'] + scale_ratio_x = None + if scale_glyph_data is not None and scale_glyph_data[1] is not None: + scale_ratio_x = self.font_dim['width'] / scale_glyph_data[1]['width'] + if scale_ratio_x is None: + scale_ratio_x = self.font_dim['width'] / sym_dim['width'] # end if single width # non-monospace (double width glyphs) @@ -1049,7 +1053,11 @@ class font_patcher: if 'y' in sym_attr['stretch']: # Stretch the glyph vertically to total line height (good for powerline separators) # Currently stretching vertically for both monospace and double-width - scale_ratio_y = self.font_dim['height'] / sym_dim['height'] + scale_ratio_y = None + if scale_glyph_data is not None and scale_glyph_data[1] is not None: + scale_ratio_y = self.font_dim['height'] / scale_glyph_data[1]['height'] + if scale_ratio_y is None: + scale_ratio_y = self.font_dim['height'] / sym_dim['height'] overlap = sym_attr['params'].get('overlap') @@ -1063,13 +1071,13 @@ class font_patcher: # Use the dimensions from the newly pasted and stretched glyph to avoid any rounding errors sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]) # Use combined bounding box? - if scale_glyph_data and scale_glyph_data[1]: + if scale_glyph_data is not None and scale_glyph_data[1] is not None: if scale_ratio_x != 1 or scale_ratio_y != 1: # Simulate scaling on combined bounding box scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) else: scaleglyph_dim = scale_glyph_data[1] - if not scaleglyph_dim['advance']: + if scaleglyph_dim['advance'] is None: # On monospaced symbol collections use their advance with, otherwise align horizontally individually scaleglyph_dim['xmin'] = sym_dim['xmin'] scaleglyph_dim['xmax'] = sym_dim['xmax'] @@ -1250,7 +1258,7 @@ class font_patcher: for glyph_list, scale, box in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales'], scaleGlyph['bbdims']): if symbol_unicode in glyph_list: return (scale, box) - return False + return None def replace_font_name(font_name, replacement_dict): From c9b466ffb17f64a81a91df5e5938e374e8dd7844 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Tue, 3 Jan 2023 09:58:10 +0100 Subject: [PATCH 16/25] font-patcher: Fix monospaced glyph collection detection [why] When only one symbol glyph is examined we conclude that it comes from a monospaced glyph set. This might be correct or not, but when we can not positively say it is monospaced we should not handle it as monospaced. [how] We require at least TWO glyphs with the same width (and no glyph with a different width) until we set the 'advance' bounding box property. Which says that this particular glyph subset is monospaced. Signed-off-by: Fini Jastrow --- font-patcher | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/font-patcher b/font-patcher index 0d49b9c44f..bcadff598f 100755 --- a/font-patcher +++ b/font-patcher @@ -1316,11 +1316,14 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None): bbox[2] = gbb[2] if bbox[2] is None or bbox[2] < gbb[2] else bbox[2] bbox[3] = gbb[3] if bbox[3] is None or bbox[3] < gbb[3] else bbox[3] if not bbox[4]: - bbox[4] = gadvance + bbox[4] = -gadvance # Negative for one/first glyph else: - if bbox[4] != gadvance: + if abs(bbox[4]) != gadvance: bbox[4] = -1 # Marker for not-monospaced + else: + bbox[4] = gadvance # Positive for 2 or more glyphs if bbox[4] and bbox[4] < 0: + # Not monospaced when only one glyph is used or multiple glyphs with different advance widths bbox[4] = None return { 'xmin' : bbox[0], From a26dafce09f491b77d234e273728634da4e87e7d Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Tue, 3 Jan 2023 11:26:01 +0100 Subject: [PATCH 17/25] font-patcher: Fix wrong advance width after group scale [why] The advance width in the bounding box data is sometimes wrong (usually to small). Turned out only AFTER the glyph scaling. [how] The wrong scaling factor has been used *duh*. Advance width is of course X axis. Signed-off-by: Fini Jastrow --- font-patcher | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/font-patcher b/font-patcher index bcadff598f..867c3041ad 100755 --- a/font-patcher +++ b/font-patcher @@ -1346,7 +1346,7 @@ def scale_bounding_box(bbox, scale_x, scale_y): 'ymin' : int(bbox['ymin'] * scale_y), 'xmax' : int(bbox['xmax'] * scale_x), 'ymax' : int(bbox['ymax'] * scale_y), - 'advance': int(bbox['advance'] * scale_y) if bbox['advance'] else None, + 'advance': int(bbox['advance'] * scale_x) if bbox['advance'] else None, } new_dim['width'] = new_dim['xmax'] + (-new_dim['xmin']) new_dim['height'] = new_dim['ymax'] + (-new_dim['ymin']) From f86d6e1e0e40df3342c01a49ea9460072488cdd2 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Tue, 3 Jan 2023 11:32:38 +0100 Subject: [PATCH 18/25] font-patcher: Simplify bounding box scaling [why] The code looks so compliacted while in fact it is not (so much). Rounding sometimes and sometimes not is hard to reaon about. The un-rounded values should in principle be better, but there is some rounding hidden in the font that we can not really simulate, so simulate what we can. [how] Always scale (even if factor is one) and round to integer the BB. [note] Also use 'is not None' ideom. Signed-off-by: Fini Jastrow --- font-patcher | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/font-patcher b/font-patcher index 867c3041ad..16dfac1e95 100755 --- a/font-patcher +++ b/font-patcher @@ -1072,11 +1072,7 @@ class font_patcher: sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]) # Use combined bounding box? if scale_glyph_data is not None and scale_glyph_data[1] is not None: - if scale_ratio_x != 1 or scale_ratio_y != 1: - # Simulate scaling on combined bounding box - scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) - else: - scaleglyph_dim = scale_glyph_data[1] + scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) if scaleglyph_dim['advance'] is None: # On monospaced symbol collections use their advance with, otherwise align horizontally individually scaleglyph_dim['xmin'] = sym_dim['xmin'] @@ -1341,12 +1337,13 @@ def get_glyph_dimensions(glyph): def scale_bounding_box(bbox, scale_x, scale_y): """ Return a scaled version of a glyph dimensions dict """ + # Simulate scaling on combined bounding box, round values for better simulation new_dim = { 'xmin' : int(bbox['xmin'] * scale_x), 'ymin' : int(bbox['ymin'] * scale_y), 'xmax' : int(bbox['xmax'] * scale_x), 'ymax' : int(bbox['ymax'] * scale_y), - 'advance': int(bbox['advance'] * scale_x) if bbox['advance'] else None, + 'advance': int(bbox['advance'] * scale_x) if bbox['advance'] is not None else None, } new_dim['width'] = new_dim['xmax'] + (-new_dim['xmin']) new_dim['height'] = new_dim['ymax'] + (-new_dim['ymin']) From ed43e2bd4dbb70515f70ffd055a27f94bd98a2ae Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Tue, 3 Jan 2023 19:10:13 +0100 Subject: [PATCH 19/25] font-patcher: Rename new GlyphsToScale [why] We now have the 'old' and the 'new' GlyphsToScale things, which behave differently, but they have the same name. That can lead to confusion. At least I am always confused when I look at the code after a month or so. [how] Call the 'new' method 'ScaleGroup' instead. The 'new' feature (which includes creating a combined bounding box and synchronized shifting) 'ScaleGroup'. The 'old' feature (which scales all glyphs as if they would have the size of one reference glyph; shifting is individual) still consists of 'ScaleGlyph' and 'GlyphsToScale'. Signed-off-by: Fini Jastrow --- font-patcher | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/font-patcher b/font-patcher index 16dfac1e95..977e20cfc6 100755 --- a/font-patcher +++ b/font-patcher @@ -748,7 +748,7 @@ class font_patcher: 'GlyphsToScale': [ (0xe6bd, 0xe6c3) # very small things ]} - FONTA_SCALE_LIST = {'GlyphsToScale': [ + FONTA_SCALE_LIST = {'ScaleGroups': [ [0xf005, 0xf006, 0xf089], # star, star empty, half star range(0xf026, 0xf028 + 1), # volume off, down, up range(0xf02b, 0xf02c + 1), # tag, tags @@ -778,7 +778,7 @@ class font_patcher: 0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons 0xf0ca, # dash ]} - WEATH_SCALE_LIST = {'GlyphsToScale': [ + WEATH_SCALE_LIST = {'ScaleGroups': [ range(0xf095, 0xf0b0 + 1), # moon phases range(0xf0b7, 0xf0c3 + 1), # wind strengths range(0xf053, 0xf055 + 1), # thermometer @@ -1199,16 +1199,16 @@ class font_patcher: def prepareScaleGlyph(self, scaleGlyph, symbolFont, destGlyph): """ Prepare raw ScaleGlyph data for use """ # The GlyphData is a dict with these (possible) entries: - # 'GlyphsToScale': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled - # 'scales': List of associated scale factors, one for each entry in 'GlyphsToScale' (generated by this function) - # 'bbdims': List of associated sym_dim dicts, one for each entry in 'GlyphsToScale' (generated by this function) - # Each sym_dim dict describes the combined bounding box of all glyphs in GlyphsToScale + # 'ScaleGroups': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled + # 'scales': List of associated scale factors, one for each entry in 'ScaleGroups' (generated by this function) + # 'bbdims': List of associated sym_dim dicts, one for each entry in 'ScaleGroups' (generated by this function) + # Each sym_dim dict describes the combined bounding box of all glyphs in one ScaleGroups group # Example: - # { 'GlyphsToScale': [ range(1, 3), [ 7, 10 ], ], - # 'scales': [ 1.23, 1.33, ], - # 'bbdims': [ dim_dict1, dim_dict2, ] } + # { 'ScaleGroups': [ range(1, 3), [ 7, 10 ], ], + # 'scales': [ 1.23, 1.33, ], + # 'bbdims': [ dim_dict1, dim_dict2, ] } # - # Each item in 'GlyphsToScale' (a range or an explicit list) forms a group of glyphs that shall be + # Each item in 'ScaleGroups' (a range or an explicit list) forms a group of glyphs that shall be # as rescaled all with the same and maximum possible (for the included glyphs) factor. # If the 'bbdims' is present they all shall be shifted in the same way. # @@ -1229,7 +1229,7 @@ class font_patcher: flat_list += list(range(i[0], i[1] + 1)) else: flat_list.append(i) - scaleGlyph['GlyphsToScale'] = [ flat_list ] + scaleGlyph['ScaleGroups'] = [ flat_list ] sym_dim = get_glyph_dimensions(symbolFont[scaleGlyph['ScaleGlyph']]) scale = self.get_scale_factor(sym_dim) scaleGlyph['scales'] = [ scale ] @@ -1238,7 +1238,7 @@ class font_patcher: else: scaleGlyph['scales'] = [] scaleGlyph['bbdims'] = [] - for group in scaleGlyph['GlyphsToScale']: + for group in scaleGlyph['ScaleGroups']: sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ], destGlyph) scale = self.get_scale_factor(sym_dim) scaleGlyph['scales'].append(scale) @@ -1251,7 +1251,7 @@ class font_patcher: if not dest_unicode in self.sourceFont: self.sourceFont.createChar(dest_unicode) self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[dest_unicode]) - for glyph_list, scale, box in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales'], scaleGlyph['bbdims']): + for glyph_list, scale, box in zip(scaleGlyph['ScaleGroups'], scaleGlyph['scales'], scaleGlyph['bbdims']): if symbol_unicode in glyph_list: return (scale, box) return None From d3b7be9a36ca89f4b9bc467921d151ffa73870d6 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Tue, 3 Jan 2023 19:26:15 +0100 Subject: [PATCH 20/25] font-patcher: Rename ScaleGlyph to ScaleRules [why] We have still a confusing naming. There are two different things that are called 'ScaleGlyph': - The setting in the patch set - The reference glyph for old style scaling [how] Rename the patch set member to ScaleRules, as this is what in contained. Also rename variable names accordingly. Signed-off-by: Fini Jastrow --- font-patcher | 106 +++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/font-patcher b/font-patcher index 977e20cfc6..f66ff6962d 100755 --- a/font-patcher +++ b/font-patcher @@ -299,7 +299,7 @@ class font_patcher: SrcStart = patch['SrcStart'] if not SrcStart: SrcStart = patch['SymStart'] - self.copy_glyphs(SrcStart, symfont, patch['SymStart'], patch['SymEnd'], patch['Exact'], patch['ScaleGlyph'], patch['Name'], patch['Attributes']) + self.copy_glyphs(SrcStart, symfont, patch['SymStart'], patch['SymEnd'], patch['Exact'], patch['ScaleRules'], patch['Name'], patch['Attributes']) if symfont: symfont.close() @@ -742,7 +742,7 @@ class font_patcher: # Most glyphs we want to maximize during the scale. However, there are some # that need to be small or stay relative in size to each other. # The following list are those glyphs. A tuple represents a range. - # See prepareScaleGlyph() for an explanation how this needs to look like. + # See prepareScaleRules() for an explanation how this needs to look like. # The codepoints mentioned here are symbol-font-codepoints. DEVI_SCALE_LIST = {'ScaleGlyph': 0xE60E, # Android logo 'GlyphsToScale': [ @@ -789,28 +789,28 @@ class font_patcher: # Define the character ranges # Symbol font ranges self.patch_set = [ - {'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE5AA, 'SrcStart': 0xE5FA, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'ScaleGlyph': 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, 'ScaleGlyph': 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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE}, - {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0A3, 'SymEnd': 0xE0A3, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE}, - {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0B4, 'SymEnd': 0xE0C8, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE}, - {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CA, 'SymEnd': 0xE0CA, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE}, - {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CC, 'SymEnd': 0xE0D4, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE}, - {'Enabled': self.args.pomicons, 'Name': "Pomicons", 'Filename': "Pomicons.otf", 'Exact': True, 'SymStart': 0xE000, 'SymEnd': 0xE00A, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': self.args.fontawesome, 'Name': "Font Awesome", 'Filename': "font-awesome/FontAwesome.otf", 'Exact': True, 'SymStart': 0xF000, 'SymEnd': 0xF2E0, 'SrcStart': None, 'ScaleGlyph': FONTA_SCALE_LIST, 'Attributes': SYM_ATTR_FONTA}, - {'Enabled': self.args.fontawesomeextension, 'Name': "Font Awesome Extension", 'Filename': "font-awesome-extension.ttf", 'Exact': False, 'SymStart': 0xE000, 'SymEnd': 0xE0A9, 'SrcStart': 0xE200, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Maximize - {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x23FB, 'SymEnd': 0x23FE, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Power, Power On/Off, Power On, Sleep - {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off) - {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'ScaleGlyph': WEATH_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': self.args.fontlogos, 'Name': "Font Logos", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF105, 'SrcStart': 0xF400, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Magnifying glass - {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0x2665, 'SymEnd': 0x2665, 'SrcStart': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Heart - {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': True, 'SymStart': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap - {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "octicons.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop - {'Enabled': self.args.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, - {'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': CUSTOM_ATTR} + {'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE5AA, 'SrcStart': 0xE5FA, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, + {'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}, + {'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}, + {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0A3, 'SymEnd': 0xE0A3, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE}, + {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0B4, 'SymEnd': 0xE0C8, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE}, + {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CA, 'SymEnd': 0xE0CA, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE}, + {'Enabled': self.args.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0xE0CC, 'SymEnd': 0xE0D4, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE}, + {'Enabled': self.args.pomicons, 'Name': "Pomicons", 'Filename': "Pomicons.otf", 'Exact': True, 'SymStart': 0xE000, 'SymEnd': 0xE00A, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, + {'Enabled': self.args.fontawesome, 'Name': "Font Awesome", 'Filename': "font-awesome/FontAwesome.otf", 'Exact': True, 'SymStart': 0xF000, 'SymEnd': 0xF2E0, 'SrcStart': None, 'ScaleRules': FONTA_SCALE_LIST, 'Attributes': SYM_ATTR_FONTA}, + {'Enabled': self.args.fontawesomeextension, 'Name': "Font Awesome Extension", 'Filename': "font-awesome-extension.ttf", 'Exact': False, 'SymStart': 0xE000, 'SymEnd': 0xE0A9, 'SrcStart': 0xE200, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, # Maximize + {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x23FB, 'SymEnd': 0x23FE, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, # Power, Power On/Off, Power On, Sleep + {'Enabled': self.args.powersymbols, 'Name': "Power Symbols", 'Filename': "Unicode_IEC_symbol_font.otf", 'Exact': True, 'SymStart': 0x2B58, 'SymEnd': 0x2B58, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off) + {'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesignicons-webfont.ttf", 'Exact': False, 'SymStart': 0xF001, 'SymEnd': 0xF847, 'SrcStart': 0xF500, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, + {'Enabled': self.args.weather, 'Name': "Weather Icons", 'Filename': "weather-icons/weathericons-regular-webfont.ttf", 'Exact': False, 'SymStart': 0xF000, 'SymEnd': 0xF0EB, 'SrcStart': 0xE300, 'ScaleRules': WEATH_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, + {'Enabled': self.args.fontlogos, 'Name': "Font Logos", 'Filename': "font-logos.ttf", 'Exact': True, 'SymStart': 0xF300, 'SymEnd': 0xF32F, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT}, + {'Enabled': self.args.octicons, 'Name': "Octicons", 'Filename': "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.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.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.ttf", 'Exact': False, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'ScaleRules': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop + {'Enabled': self.args.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleRules': None, '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} ] def setup_line_dimensions(self): @@ -921,7 +921,7 @@ class font_patcher: return scale_ratio - def copy_glyphs(self, sourceFontStart, symbolFont, symbolFontStart, symbolFontEnd, exactEncoding, scaleGlyph, setName, attributes): + def copy_glyphs(self, sourceFontStart, symbolFont, symbolFontStart, symbolFontEnd, exactEncoding, scaleRules, setName, attributes): """ Copies symbol glyphs into self.sourceFont """ progressText = '' careful = False @@ -1001,7 +1001,7 @@ class font_patcher: self.sourceFont[currentSourceFontGlyph].removePosSub("*") # This will destroy any content currently in currentSourceFontGlyph, so do it first - scale_glyph_data = self.get_glyph_scale(sym_glyph.encoding, scaleGlyph, symbolFont, currentSourceFontGlyph) if scaleGlyph is not None else None + glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, 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 @@ -1028,9 +1028,9 @@ class font_patcher: # to fit in both the x and y directions if sym_attr['stretch'] == 'pa': scale_ratio_x = None - if scale_glyph_data: + if glyph_scale_data: # We want to preserve the relative size of each glyph in a glyph group - scale_ratio_x = scale_glyph_data[0] + scale_ratio_x = glyph_scale_data[0] if scale_ratio_x is None: # In the remaining cases, each glyph is sized independently to each other scale_ratio_x = self.get_scale_factor(sym_dim) @@ -1039,8 +1039,8 @@ class font_patcher: if 'x' in sym_attr['stretch']: # Stretch the glyph horizontally to fit the entire available width scale_ratio_x = None - if scale_glyph_data is not None and scale_glyph_data[1] is not None: - scale_ratio_x = self.font_dim['width'] / scale_glyph_data[1]['width'] + if glyph_scale_data is not None and glyph_scale_data[1] is not None: + scale_ratio_x = self.font_dim['width'] / glyph_scale_data[1]['width'] if scale_ratio_x is None: scale_ratio_x = self.font_dim['width'] / sym_dim['width'] # end if single width @@ -1054,8 +1054,8 @@ class font_patcher: # Stretch the glyph vertically to total line height (good for powerline separators) # Currently stretching vertically for both monospace and double-width scale_ratio_y = None - if scale_glyph_data is not None and scale_glyph_data[1] is not None: - scale_ratio_y = self.font_dim['height'] / scale_glyph_data[1]['height'] + if glyph_scale_data is not None and glyph_scale_data[1] is not None: + scale_ratio_y = self.font_dim['height'] / glyph_scale_data[1]['height'] if scale_ratio_y is None: scale_ratio_y = self.font_dim['height'] / sym_dim['height'] @@ -1071,8 +1071,8 @@ class font_patcher: # Use the dimensions from the newly pasted and stretched glyph to avoid any rounding errors sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]) # Use combined bounding box? - if scale_glyph_data is not None and scale_glyph_data[1] is not None: - scaleglyph_dim = scale_bounding_box(scale_glyph_data[1], scale_ratio_x, scale_ratio_y) + if glyph_scale_data is not None and glyph_scale_data[1] is not None: + scaleglyph_dim = scale_bounding_box(glyph_scale_data[1], scale_ratio_x, scale_ratio_y) if scaleglyph_dim['advance'] is None: # On monospaced symbol collections use their advance with, otherwise align horizontally individually scaleglyph_dim['xmin'] = sym_dim['xmin'] @@ -1196,8 +1196,8 @@ class font_patcher: except: pass - def prepareScaleGlyph(self, scaleGlyph, symbolFont, destGlyph): - """ Prepare raw ScaleGlyph data for use """ + def prepareScaleRules(self, scaleRules, symbolFont, destGlyph): + """ Prepare raw ScaleRules data for use """ # The GlyphData is a dict with these (possible) entries: # 'ScaleGroups': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled # 'scales': List of associated scale factors, one for each entry in 'ScaleGroups' (generated by this function) @@ -1218,40 +1218,40 @@ class font_patcher: # Note that this allows only one group for the whle symbol font, and that the scaling factor is defined by # a specific character, which needs to be manually selected (on each symbol font update). # Previous entries are automatically rewritten to the new style. - if 'scales' in scaleGlyph: + if 'scales' in scaleRules: # Already prepared... must not happen, ignore call return - if 'ScaleGlyph' in scaleGlyph: + if 'ScaleGlyph' in scaleRules: # old method. Rewrite to new. flat_list = [] - for i in scaleGlyph['GlyphsToScale']: + for i in scaleRules['GlyphsToScale']: if isinstance(i, tuple): flat_list += list(range(i[0], i[1] + 1)) else: flat_list.append(i) - scaleGlyph['ScaleGroups'] = [ flat_list ] - sym_dim = get_glyph_dimensions(symbolFont[scaleGlyph['ScaleGlyph']]) + scaleRules['ScaleGroups'] = [ flat_list ] + sym_dim = get_glyph_dimensions(symbolFont[scaleRules['ScaleGlyph']]) scale = self.get_scale_factor(sym_dim) - scaleGlyph['scales'] = [ scale ] + scaleRules['scales'] = [ scale ] # The 'old' style keeps just the scale, not the positioning - scaleGlyph['bbdims'] = [ None ] + scaleRules['bbdims'] = [ None ] else: - scaleGlyph['scales'] = [] - scaleGlyph['bbdims'] = [] - for group in scaleGlyph['ScaleGroups']: + scaleRules['scales'] = [] + scaleRules['bbdims'] = [] + 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_factor(sym_dim) - scaleGlyph['scales'].append(scale) - scaleGlyph['bbdims'].append(sym_dim) + scaleRules['scales'].append(scale) + scaleRules['bbdims'].append(sym_dim) - def get_glyph_scale(self, symbol_unicode, scaleGlyph, symbolFont, dest_unicode): - """ Determines whether or not to use scaled glyphs for glyphs in passed glyph_list """ + def get_glyph_scale(self, symbol_unicode, scaleRules, 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 scaleGlyph: + if not 'scales' in scaleRules: if not dest_unicode in self.sourceFont: self.sourceFont.createChar(dest_unicode) - self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[dest_unicode]) - for glyph_list, scale, box in zip(scaleGlyph['ScaleGroups'], scaleGlyph['scales'], scaleGlyph['bbdims']): + self.prepareScaleRules(scaleRules, symbolFont, self.sourceFont[dest_unicode]) + for glyph_list, scale, box in zip(scaleRules['ScaleGroups'], scaleRules['scales'], scaleRules['bbdims']): if symbol_unicode in glyph_list: return (scale, box) return None From 3f69ab55611bf24082bf8060b0aa36f31e24ae4e Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 4 Jan 2023 10:50:37 +0100 Subject: [PATCH 21/25] font-patcher: Improve Nerd Font Propo with ScaleGroups [why] Assume a set of monospaced icons in a ScaleGroup that scatter all about (like Braille). With --variable-width-glyphs we forcefully remove all left side bearings and set the width to the width of the combined bounding box. This is not correct, usually with monospaced ScaleGroup icons we should preserve the original advance width. [how] Do not remove left side bearings on ScaleGroup glyphs in --variable-width-glyphs mode. Set the width of any glyph in --variable-width-glyphs to the 'monoscaped advance width' if that particular glyph has one (from a ScaleGroup). The effect is that all positive bearings will be 'added' and put on the right hand side of the glyph, while the glyph itself, or rather the combined bounding box, is still strictly left aligned. Signed-off-by: Fini Jastrow --- font-patcher | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/font-patcher b/font-patcher index f66ff6962d..5efef556c7 100755 --- a/font-patcher +++ b/font-patcher @@ -1089,8 +1089,9 @@ class font_patcher: # Handle glyph l/r/c alignment x_align_distance = 0 - if self.args.nonmono: + if self.args.nonmono and sym_dim['advance'] is None: # Remove left side bearing + # (i.e. do not remove left side bearing when combined BB is in use) x_align_distance = -self.sourceFont[currentSourceFontGlyph].left_side_bearing elif sym_attr['align']: # First find the baseline x-alignment (left alignment amount) @@ -1127,8 +1128,12 @@ class font_patcher: self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph]) else: # Target font with variable advance width get the icons with their native widths - # and keeping possible (negative) bearings in effect - width = sym_dim['width'] + # and keeping possible (right and/or negative) bearings in effect + if sym_dim['advance'] is not None: + # 'Width' from monospaced scale group + width = sym_dim['advance'] + 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'): width -= overlap_width From 7d9961c53959787b4aff4db6db610d92b866feb6 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 4 Jan 2023 12:22:48 +0100 Subject: [PATCH 22/25] font-patcher: Simplify some ifs [why] There are several instances of if x is True: This should be written as if x: instead. See PEP 8: https://peps.python.org/pep-0008/#programming-recommendations Signed-off-by: Fini Jastrow --- font-patcher | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/font-patcher b/font-patcher index 5efef556c7..cfec92e1bb 100755 --- a/font-patcher +++ b/font-patcher @@ -945,7 +945,7 @@ class font_patcher: symbolFontSelection = [ x for x in symbolFont.selection.byGlyphs if x.unicode >= 0 ] glyphSetLength = len(symbolFontSelection) - if self.args.quiet is False: + if not self.args.quiet: sys.stdout.write("Adding " + str(max(1, glyphSetLength)) + " Glyphs from " + setName + " Set \n") currentSourceFontGlyph = -1 # initialize for the exactEncoding case @@ -978,7 +978,7 @@ class font_patcher: currentSourceFontGlyph = sourceFontStart + sourceFontCounter sourceFontCounter += 1 - if self.args.quiet is False: + if not self.args.quiet: if self.args.progressbars: update_progress(round(float(index + 1) / glyphSetLength, 2)) else: @@ -989,7 +989,7 @@ class font_patcher: # check if a glyph already exists in this location if careful or 'careful' in sym_attr['params'] or currentSourceFontGlyph in self.essential: if currentSourceFontGlyph in self.sourceFont: - if self.args.quiet is False: + if not self.args.quiet: careful_type = 'essential' if currentSourceFontGlyph in self.essential else 'existing' print(" Found {} Glyph at {:X}. Skipping...".format(careful_type, currentSourceFontGlyph)) # We don't want to touch anything so move to next Glyph @@ -1152,7 +1152,7 @@ class font_patcher: # end for - if self.args.quiet is False or self.args.progressbars: + if not self.args.quiet or self.args.progressbars: sys.stdout.write("\n") @@ -1470,7 +1470,7 @@ def setup_arguments(): for alias in sym_font_arg_aliases: if alias in sys.argv: found = True - if found is not True: + if not found: font_complete = False args.complete = font_complete From 43a1a52fcb709abd72155f9263cbb14224914eb0 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 4 Jan 2023 16:06:54 +0100 Subject: [PATCH 23/25] font-patcher: Allow ScaleGroups and ScaleGlyph in one ScaleRules [why] If a ScaleGlyph is defined that ScaleRules will just be that one rule, even if in parallel the user specified some ScaleGroups. So it is either ScaleGroups or ScaleGlyph but not both. If someone specifies both there is no warning or check. [how] Just allow both. Rewrite the ScaleGlyph to an additional (last) entry in the ScaleGroups. Signed-off-by: Fini Jastrow --- font-patcher | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/font-patcher b/font-patcher index cfec92e1bb..0cdb718740 100755 --- a/font-patcher +++ b/font-patcher @@ -1226,6 +1226,17 @@ class font_patcher: if 'scales' in scaleRules: # Already prepared... must not happen, ignore call return + + scaleRules['scales'] = [] + scaleRules['bbdims'] = [] + if 'ScaleGroups' not in scaleRules: + 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_factor(sym_dim) + scaleRules['scales'].append(scale) + scaleRules['bbdims'].append(sym_dim) + if 'ScaleGlyph' in scaleRules: # old method. Rewrite to new. flat_list = [] @@ -1234,20 +1245,11 @@ class font_patcher: flat_list += list(range(i[0], i[1] + 1)) else: flat_list.append(i) - scaleRules['ScaleGroups'] = [ flat_list ] sym_dim = get_glyph_dimensions(symbolFont[scaleRules['ScaleGlyph']]) scale = self.get_scale_factor(sym_dim) - scaleRules['scales'] = [ scale ] - # The 'old' style keeps just the scale, not the positioning - scaleRules['bbdims'] = [ None ] - else: - scaleRules['scales'] = [] - scaleRules['bbdims'] = [] - 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_factor(sym_dim) - scaleRules['scales'].append(scale) - scaleRules['bbdims'].append(sym_dim) + scaleRules['ScaleGroups'].append(flat_list) + scaleRules['scales'].append(scale) + 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): """ Determines whether or not to use scaled glyphs for glyph in passed symbol_unicode """ From 0a9542e32168a381b24dcdbc2e4ce1c116dc757b Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 4 Jan 2023 16:09:44 +0100 Subject: [PATCH 24/25] font-patcher: Add documentation on ScaleGroups [why] The old doc was a bit unclear and I always had to read the code when changes / additions to ScaleRules were needed. Signed-off-by: Fini Jastrow --- font-patcher | 55 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/font-patcher b/font-patcher index 0cdb718740..ee2d95bdfa 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 = "3.2.2" +script_version = "3.3.0" version = "2.3.0-RC" projectName = "Nerd Fonts" @@ -739,11 +739,46 @@ class font_patcher: 'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}} } - # Most glyphs we want to maximize during the scale. However, there are some - # that need to be small or stay relative in size to each other. - # The following list are those glyphs. A tuple represents a range. - # See prepareScaleRules() for an explanation how this needs to look like. + # Most glyphs we want to maximize (individually) during the scale + # However, there are some that need to be small or stay relative in + # size to each other. + # The glyph-specific behavior can be given as ScaleRules in the patch-set. + # + # ScaleRules can contain two different kind of rules (possibly in parallel): + # - ScaleGlyph: + # Here one specific glyph is used as 'scale blueprint'. Other glyphs are + # scaled by the same factor as this glyph. This is useful if you have one + # 'biggest' glyph and all others should stay relatively in size. + # - 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. + # + # The ScaleGlyph method: You set 'ScaleGlyph' to the unicode of the reference glyph. + # Note that there can be only one per patch-set. + # Additionally you set 'GlyphsToScale' that contains all the glyphs that shall be + # handled like the reference glyph. + # It is a List of: ((glyph code) or (tuple of two glyph codes that form a closed range)) + # 'GlyphsToScale': [ + # 0x0100, 0x0300, 0x0400, # The single glyphs 0x0100, 0x0300, and 0x0400 + # (0x0200, 0x0210), # All glyphs 0x0200 to 0x0210 including both 0x0200 and 0x0210 + # ]} + # + # 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. + # You define the groups as value of 'ScaleGroups'. + # It is a List of: ((lists of glyph codes) or (ranges of glyph codes)) + # 'ScaleGroups': [ + # [0x0100, 0x0300, 0x0400], # One group consists of glyphs 0x0100, 0x0300, and 0x0400 + # range(0x0200, 0x0210 + 1), # Another group contains glyphs 0x0200 to 0x0210 incl. + # + # Note the subtle differences: tuple vs. range; closed vs open range; etc + # 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. + DEVI_SCALE_LIST = {'ScaleGlyph': 0xE60E, # Android logo 'GlyphsToScale': [ (0xe6bd, 0xe6c3) # very small things @@ -1203,11 +1238,11 @@ class font_patcher: def prepareScaleRules(self, scaleRules, symbolFont, destGlyph): """ Prepare raw ScaleRules data for use """ - # The GlyphData is a dict with these (possible) entries: + # 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 # 'scales': List of associated scale factors, one for each entry in 'ScaleGroups' (generated by this function) # 'bbdims': List of associated sym_dim dicts, one for each entry in 'ScaleGroups' (generated by this function) - # Each sym_dim dict describes the combined bounding box of all glyphs in one ScaleGroups group + # Each dim_dict describes the combined bounding box of all glyphs in one ScaleGroups group # Example: # { 'ScaleGroups': [ range(1, 3), [ 7, 10 ], ], # 'scales': [ 1.23, 1.33, ], @@ -1219,10 +1254,12 @@ class font_patcher: # # Previously this structure has been used: # 'ScaleGlyph' Lead glyph, which scaling factor is taken - # 'GlyphsToScale': List of (glyph code) or (list of two glyph codes that form a closed range)) that shall be scaled + # 'GlyphsToScale': List of ((glyph code) or (tuple of two glyph codes that form a closed range)) that shall be scaled # Note that this allows only one group for the whle symbol font, and that the scaling factor is defined by # a specific character, which needs to be manually selected (on each symbol font update). # Previous entries are automatically rewritten to the new style. + # + # Note that scaleRules is overwritten with the added data. if 'scales' in scaleRules: # Already prepared... must not happen, ignore call return @@ -1238,7 +1275,7 @@ class font_patcher: scaleRules['bbdims'].append(sym_dim) if 'ScaleGlyph' in scaleRules: - # old method. Rewrite to new. + # Rewrite to equivalent ScaleGroup flat_list = [] for i in scaleRules['GlyphsToScale']: if isinstance(i, tuple): From 14a982ac6bc91377819515ac6597e787f69bc898 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Wed, 4 Jan 2023 17:07:46 +0100 Subject: [PATCH 25/25] font-patcher: Add ratio limit to xy scale [why] The powerline glyphs (and only them) undergo a xy scaling, where both dimensions are maximized into the 'cell'. Normally cells are taller than wide, and everyting looks fine. But we have square cells (e.g. 2048 * 2048) with the SymbolsOnly font, and there might be some self patched font that has similar very-wide (in comparison to hight) cells. In these fonts some powerline glyphs look rather ugly. For example the 'half cicles' become very wide and un-round, the triangulars become very pointy. [how] Add a new patch-set attribute 'xy-ratio'. When that is set the vertical (y) scaling is done as usual but the horizontal (x) scaling is limited such that the width/height ratio is maximally the attributes value. For example setting it to 0.75 the height is maximized (as usual) but the width is maximized to be less then 0.75 times the hight (or as wide as the cell is, whatever is smaller). It will work with both, 'xy' and 'pa' scaling, at least theoretically. It has been only checked where it is used now, i.e. with 'xy'. A possible overlap is not taken into account. [note] The values are taken for this reasons: - 0.59: This is the original half-circle ratio, higher values make them loose the (half) circular appearance - 0.5: The half circle lines are more shallow - 0.7: The triangulars should not be too pointy (random number) Fixes: #658 Signed-off-by: Fini Jastrow --- font-patcher | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/font-patcher b/font-patcher index ee2d95bdfa..e2251d31ed 100755 --- a/font-patcher +++ b/font-patcher @@ -665,16 +665,16 @@ class font_patcher: 'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}}, # Arrow tips - 0xe0b0: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, - 0xe0b1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, - 0xe0b2: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, - 0xe0b3: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, + 0xe0b0: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'xy-ratio': 0.7}}, + 0xe0b1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'xy-ratio': 0.7}}, + 0xe0b2: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'xy-ratio': 0.7}}, + 0xe0b3: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'xy-ratio': 0.7}}, # Rounded arcs - 0xe0b4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}}, - 0xe0b5: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}}, - 0xe0b6: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}}, - 0xe0b7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}}, + 0xe0b4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01, 'xy-ratio': 0.59}}, + 0xe0b5: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01, 'xy-ratio': 0.5}}, + 0xe0b6: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01, 'xy-ratio': 0.59}}, + 0xe0b7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01, 'xy-ratio': 0.5}}, # Bottom Triangles 0xe0b8: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}, @@ -1100,6 +1100,17 @@ class font_patcher: if overlap: scale_ratio_x *= 1 + overlap scale_ratio_y *= 1 + overlap + # Size in x to size in y ratio limit (to prevent over-wide glyphs) + xy_ratio_max = sym_attr['params'].get('xy-ratio') + if (xy_ratio_max): + if glyph_scale_data is not None and glyph_scale_data[1] is not None: + dim = glyph_scale_data[1] + else: + dim = sym_dim + xy_ratio = dim['width'] * scale_ratio_x / (dim['height'] * scale_ratio_y) + if xy_ratio > xy_ratio_max: + scale_ratio_x = scale_ratio_x * xy_ratio_max / xy_ratio + self.sourceFont[currentSourceFontGlyph].transform(psMat.scale(scale_ratio_x, scale_ratio_y)) # We pasted and scaled now we want to align/move