mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-11-10 00:43:15 -06:00
Compare commits
3 commits
2425723226
...
2b201ce570
Author | SHA1 | Date | |
---|---|---|---|
|
2b201ce570 | ||
2daca7d2e8 | |||
d0adac8824 |
10 changed files with 495 additions and 210 deletions
34
.github/workflows/ci.yml
vendored
Normal file
34
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
name: "Update Nerd Fonts"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
# 1st and 15th at 12:00 AM
|
||||||
|
- cron: "0 0 1,15 * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
change-batteries:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Git Bot
|
||||||
|
run: |
|
||||||
|
git --version
|
||||||
|
git config user.name github-actions
|
||||||
|
git config user.email github-actions@github.com
|
||||||
|
|
||||||
|
- name: Check Nerd-Fonts Source
|
||||||
|
run: make update-src
|
||||||
|
|
||||||
|
- name: Commit Updates
|
||||||
|
run: |
|
||||||
|
if [[ -n "$(git status --porcelain)" ]]; then
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore: change batteries"
|
||||||
|
git push
|
||||||
|
fi
|
|
@ -76,10 +76,6 @@ make update-fonts
|
||||||
|
|
||||||
You can verify the fonts have been added with `make check`.
|
You can verify the fonts have been added with `make check`.
|
||||||
|
|
||||||
## Changing the Batteries
|
|
||||||
|
|
||||||
If I haven't committed to this repo in a while it's likely a good idea to run `make update-src` to update the fonts, icons and patcher script from nerd fonts.
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Before making changes to to any of the scripts in `bin` you should first install `pre-commit`.
|
Before making changes to to any of the scripts in `bin` you should first install `pre-commit`.
|
||||||
|
|
622
bin/font-patcher
622
bin/font-patcher
|
@ -6,7 +6,7 @@
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
# Change the script version when you edit this script:
|
# Change the script version when you edit this script:
|
||||||
script_version = "3.2.2"
|
script_version = "3.4.3"
|
||||||
|
|
||||||
version = "2.3.0-RC"
|
version = "2.3.0-RC"
|
||||||
projectName = "Nerd Fonts"
|
projectName = "Nerd Fonts"
|
||||||
|
@ -88,8 +88,8 @@ class TableHEADWriter:
|
||||||
checksum = (checksum + extra) & 0xFFFFFFFF
|
checksum = (checksum + extra) & 0xFFFFFFFF
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
def find_head_table(self, idx):
|
def find_table(self, tablenames, idx):
|
||||||
""" Search all tables for the HEAD table and store its metadata """
|
""" Search all tables for one of the tables in tablenames and store its metadata """
|
||||||
# Use font with index idx if this is a font collection file
|
# Use font with index idx if this is a font collection file
|
||||||
self.f.seek(0, 0)
|
self.f.seek(0, 0)
|
||||||
tag = self.f.read(4)
|
tag = self.f.read(4)
|
||||||
|
@ -117,9 +117,17 @@ class TableHEADWriter:
|
||||||
self.tab_check = self.getlong()
|
self.tab_check = self.getlong()
|
||||||
self.tab_offset = self.getlong()
|
self.tab_offset = self.getlong()
|
||||||
self.tab_length = self.getlong()
|
self.tab_length = self.getlong()
|
||||||
if tab_name == b'head':
|
if tab_name in tablenames:
|
||||||
return
|
return True
|
||||||
raise Exception('No HEAD table found in font idx {}'.format(idx))
|
return False
|
||||||
|
|
||||||
|
def find_head_table(self, idx):
|
||||||
|
""" Search all tables for the HEAD table and store its metadata """
|
||||||
|
# Use font with index idx if this is a font collection file
|
||||||
|
found = self.find_table([ b'head' ], idx)
|
||||||
|
if not found:
|
||||||
|
raise Exception('No HEAD table found in font idx {}'.format(idx))
|
||||||
|
|
||||||
|
|
||||||
def goto(self, where):
|
def goto(self, where):
|
||||||
""" Go to a named location in the file or to the specified index """
|
""" Go to a named location in the file or to the specified index """
|
||||||
|
@ -237,6 +245,7 @@ class font_patcher:
|
||||||
self.sourceFont = None # class 'fontforge.font'
|
self.sourceFont = None # class 'fontforge.font'
|
||||||
self.patch_set = None # class 'list'
|
self.patch_set = None # class 'list'
|
||||||
self.font_dim = None # class 'dict'
|
self.font_dim = None # class 'dict'
|
||||||
|
self.font_extrawide = False
|
||||||
self.onlybitmaps = 0
|
self.onlybitmaps = 0
|
||||||
self.essential = set()
|
self.essential = set()
|
||||||
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
|
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
|
||||||
|
@ -267,6 +276,11 @@ class font_patcher:
|
||||||
panose[3] = 9 # 3 (4th value) = propotion; 9 = monospaced
|
panose[3] = 9 # 3 (4th value) = propotion; 9 = monospaced
|
||||||
self.sourceFont.os2_panose = tuple(panose)
|
self.sourceFont.os2_panose = tuple(panose)
|
||||||
|
|
||||||
|
# For very wide (almost square or wider) fonts we do not want to generate 2 cell wide Powerline glyphs
|
||||||
|
if self.font_dim['height'] * 1.8 < self.font_dim['width'] * 2:
|
||||||
|
print("Very wide and short font, disabling 2 cell Powerline glyphs")
|
||||||
|
self.font_extrawide = True
|
||||||
|
|
||||||
# Prevent opening and closing the fontforge font. Makes things faster when patching
|
# Prevent opening and closing the fontforge font. Makes things faster when patching
|
||||||
# multiple ranges using the same symbol font.
|
# multiple ranges using the same symbol font.
|
||||||
PreviousSymbolFilename = ""
|
PreviousSymbolFilename = ""
|
||||||
|
@ -299,7 +313,7 @@ class font_patcher:
|
||||||
SrcStart = patch['SrcStart']
|
SrcStart = patch['SrcStart']
|
||||||
if not SrcStart:
|
if not SrcStart:
|
||||||
SrcStart = patch['SymStart']
|
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:
|
if symfont:
|
||||||
symfont.close()
|
symfont.close()
|
||||||
|
@ -315,6 +329,11 @@ class font_patcher:
|
||||||
|
|
||||||
def generate(self, sourceFonts):
|
def generate(self, sourceFonts):
|
||||||
sourceFont = sourceFonts[0]
|
sourceFont = sourceFonts[0]
|
||||||
|
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
||||||
|
if int(fontforge.version()) >= 20201107:
|
||||||
|
gen_flags = (str('opentype'), str('PfEd-comments'), str('no-FFTM-table'))
|
||||||
|
else:
|
||||||
|
gen_flags = (str('opentype'), str('PfEd-comments'))
|
||||||
if len(sourceFonts) > 1:
|
if len(sourceFonts) > 1:
|
||||||
layer = None
|
layer = None
|
||||||
# use first non-background layer
|
# use first non-background layer
|
||||||
|
@ -325,9 +344,8 @@ class font_patcher:
|
||||||
outfile = os.path.normpath(os.path.join(
|
outfile = os.path.normpath(os.path.join(
|
||||||
sanitize_filename(self.args.outputdir, True),
|
sanitize_filename(self.args.outputdir, True),
|
||||||
sanitize_filename(sourceFont.familyname) + ".ttc"))
|
sanitize_filename(sourceFont.familyname) + ".ttc"))
|
||||||
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
sourceFonts[0].generateTtc(outfile, sourceFonts[1:], flags=gen_flags, layer=layer)
|
||||||
sourceFonts[0].generateTtc(outfile, sourceFonts[1:], flags=(str('opentype'), str('PfEd-comments')), layer=layer)
|
message = " Generated {} fonts\n \===> '{}'".format(len(sourceFonts), outfile)
|
||||||
message = "\nGenerated: {} fonts in '{}'".format(len(sourceFonts), outfile)
|
|
||||||
else:
|
else:
|
||||||
fontname = sourceFont.fullname
|
fontname = sourceFont.fullname
|
||||||
if not fontname:
|
if not fontname:
|
||||||
|
@ -335,39 +353,45 @@ class font_patcher:
|
||||||
outfile = os.path.normpath(os.path.join(
|
outfile = os.path.normpath(os.path.join(
|
||||||
sanitize_filename(self.args.outputdir, True),
|
sanitize_filename(self.args.outputdir, True),
|
||||||
sanitize_filename(fontname) + self.args.extension))
|
sanitize_filename(fontname) + self.args.extension))
|
||||||
# the `PfEd-comments` flag is required for Fontforge to save '.comment' and '.fontlog'.
|
|
||||||
bitmaps = str()
|
bitmaps = str()
|
||||||
if len(self.sourceFont.bitmapSizes):
|
if len(self.sourceFont.bitmapSizes):
|
||||||
print("Preserving bitmaps {}".format(self.sourceFont.bitmapSizes))
|
if not self.args.quiet:
|
||||||
|
print("Preserving bitmaps {}".format(self.sourceFont.bitmapSizes))
|
||||||
bitmaps = str('otf') # otf/ttf, both is bf_ttf
|
bitmaps = str('otf') # otf/ttf, both is bf_ttf
|
||||||
sourceFont.generate(outfile, bitmap_type=bitmaps, flags=(str('opentype'), str('PfEd-comments')))
|
sourceFont.generate(outfile, bitmap_type=bitmaps, flags=gen_flags)
|
||||||
message = "\nGenerated: {} in '{}'".format(self.sourceFont.fullname, outfile)
|
message = " {}\n \===> '{}'".format(self.sourceFont.fullname, outfile)
|
||||||
|
|
||||||
# Adjust flags that can not be changed via fontforge
|
# Adjust flags that can not be changed via fontforge
|
||||||
try:
|
if re.search('\\.[ot]tf$', self.args.font, re.IGNORECASE) and re.search('\\.[ot]tf$', outfile, re.IGNORECASE):
|
||||||
source_font = TableHEADWriter(self.args.font)
|
|
||||||
dest_font = TableHEADWriter(outfile)
|
|
||||||
for idx in range(source_font.num_fonts):
|
|
||||||
print("{}: Tweaking {}/{}".format(projectName, idx + 1, source_font.num_fonts))
|
|
||||||
source_font.find_head_table(idx)
|
|
||||||
dest_font.find_head_table(idx)
|
|
||||||
if source_font.flags & 0x08 == 0 and dest_font.flags & 0x08 != 0:
|
|
||||||
print("Changing flags from 0x{:X} to 0x{:X}".format(dest_font.flags, dest_font.flags & ~0x08))
|
|
||||||
dest_font.putshort(dest_font.flags & ~0x08, 'flags') # clear 'ppem_to_int'
|
|
||||||
if source_font.lowppem != dest_font.lowppem:
|
|
||||||
print("Changing lowestRecPPEM from {} to {}".format(dest_font.lowppem, source_font.lowppem))
|
|
||||||
dest_font.putshort(source_font.lowppem, 'lowestRecPPEM')
|
|
||||||
if dest_font.modified:
|
|
||||||
dest_font.reset_table_checksum()
|
|
||||||
dest_font.reset_full_checksum()
|
|
||||||
except Exception as error:
|
|
||||||
print("Can not handle font flags ({})".format(repr(error)))
|
|
||||||
finally:
|
|
||||||
try:
|
try:
|
||||||
source_font.close()
|
source_font = TableHEADWriter(self.args.font)
|
||||||
dest_font.close()
|
dest_font = TableHEADWriter(outfile)
|
||||||
except:
|
for idx in range(source_font.num_fonts):
|
||||||
pass
|
if not self.args.quiet:
|
||||||
|
print("{}: Tweaking {}/{}".format(projectName, idx + 1, source_font.num_fonts))
|
||||||
|
source_font.find_head_table(idx)
|
||||||
|
dest_font.find_head_table(idx)
|
||||||
|
if source_font.flags & 0x08 == 0 and dest_font.flags & 0x08 != 0:
|
||||||
|
if not self.args.quiet:
|
||||||
|
print("Changing flags from 0x{:X} to 0x{:X}".format(dest_font.flags, dest_font.flags & ~0x08))
|
||||||
|
dest_font.putshort(dest_font.flags & ~0x08, 'flags') # clear 'ppem_to_int'
|
||||||
|
if source_font.lowppem != dest_font.lowppem:
|
||||||
|
if not self.args.quiet:
|
||||||
|
print("Changing lowestRecPPEM from {} to {}".format(dest_font.lowppem, source_font.lowppem))
|
||||||
|
dest_font.putshort(source_font.lowppem, 'lowestRecPPEM')
|
||||||
|
if dest_font.modified:
|
||||||
|
dest_font.reset_table_checksum()
|
||||||
|
dest_font.reset_full_checksum()
|
||||||
|
except Exception as error:
|
||||||
|
print("Can not handle font flags ({})".format(repr(error)))
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
source_font.close()
|
||||||
|
dest_font.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if self.args.is_variable:
|
||||||
|
print("Warning: Source font is a variable open type font (VF) and the patch results will most likely not be what you want")
|
||||||
print(message)
|
print(message)
|
||||||
|
|
||||||
if self.args.postprocess:
|
if self.args.postprocess:
|
||||||
|
@ -488,13 +512,16 @@ class font_patcher:
|
||||||
subFamily = fallbackStyle
|
subFamily = fallbackStyle
|
||||||
|
|
||||||
# some fonts have inaccurate 'SubFamily', if it is Regular let us trust the filename more:
|
# some fonts have inaccurate 'SubFamily', if it is Regular let us trust the filename more:
|
||||||
if subFamily == "Regular":
|
if subFamily == "Regular" and len(fallbackStyle):
|
||||||
subFamily = fallbackStyle
|
subFamily = fallbackStyle
|
||||||
|
|
||||||
# This is meant to cover the case where the SubFamily is "Italic" and the filename is *-BoldItalic.
|
# This is meant to cover the case where the SubFamily is "Italic" and the filename is *-BoldItalic.
|
||||||
if len(subFamily) < len(fallbackStyle):
|
if len(subFamily) < len(fallbackStyle):
|
||||||
subFamily = fallbackStyle
|
subFamily = fallbackStyle
|
||||||
|
|
||||||
|
if len(subFamily) == 0:
|
||||||
|
subFamily = "Regular"
|
||||||
|
|
||||||
if self.args.windows:
|
if self.args.windows:
|
||||||
maxFamilyLength = 31
|
maxFamilyLength = 31
|
||||||
maxFontLength = maxFamilyLength - len('-' + subFamily)
|
maxFontLength = maxFamilyLength - len('-' + subFamily)
|
||||||
|
@ -638,8 +665,6 @@ class font_patcher:
|
||||||
print("Failed to remove subtable:", subtable)
|
print("Failed to remove subtable:", subtable)
|
||||||
elif self.args.removeligatures:
|
elif self.args.removeligatures:
|
||||||
print("Unable to read configfile, unable to remove ligatures")
|
print("Unable to read configfile, unable to remove ligatures")
|
||||||
else:
|
|
||||||
print("No configfile given, skipping configfile related actions")
|
|
||||||
|
|
||||||
|
|
||||||
def assert_monospace(self):
|
def assert_monospace(self):
|
||||||
|
@ -660,39 +685,40 @@ class font_patcher:
|
||||||
def setup_patch_set(self):
|
def setup_patch_set(self):
|
||||||
""" Creates list of dicts to with instructions on copying glyphs from each symbol font into self.sourceFont """
|
""" Creates list of dicts to with instructions on copying glyphs from each symbol font into self.sourceFont """
|
||||||
# Supported params: overlap | careful
|
# Supported params: overlap | careful
|
||||||
|
# Overlap value is used horizontally but vertically limited to 0.01
|
||||||
# Powerline dividers
|
# Powerline dividers
|
||||||
SYM_ATTR_POWERLINE = {
|
SYM_ATTR_POWERLINE = {
|
||||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||||
|
|
||||||
# Arrow tips
|
# Arrow tips
|
||||||
0xe0b0: {'align': 'l', '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}},
|
0xe0b1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'xy-ratio': 0.7}},
|
||||||
0xe0b2: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
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}},
|
0xe0b3: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'xy-ratio': 0.7}},
|
||||||
|
|
||||||
# Rounded arcs
|
# Rounded arcs
|
||||||
0xe0b4: {'align': 'l', '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}},
|
0xe0b5: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'xy-ratio': 0.5}},
|
||||||
0xe0b6: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
|
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}},
|
0xe0b7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'xy-ratio': 0.5}},
|
||||||
|
|
||||||
# Bottom Triangles
|
# Bottom Triangles
|
||||||
0xe0b8: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0b8: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||||
0xe0b9: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0b9: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||||
0xe0ba: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0ba: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||||
0xe0bb: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0bb: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||||
|
|
||||||
# Top Triangles
|
# Top Triangles
|
||||||
0xe0bc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0bc: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||||
0xe0bd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0bd: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||||
0xe0be: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0be: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||||
0xe0bf: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0bf: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||||
|
|
||||||
# Flames
|
# Flames
|
||||||
0xe0c0: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
|
0xe0c0: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||||
0xe0c1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
|
0xe0c1: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||||
0xe0c2: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
|
0xe0c2: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||||
0xe0c3: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
|
0xe0c3: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||||
|
|
||||||
# Small squares
|
# Small squares
|
||||||
0xe0c4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
0xe0c4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||||
|
@ -703,10 +729,11 @@ class font_patcher:
|
||||||
0xe0c7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
0xe0c7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||||
|
|
||||||
# Waveform
|
# Waveform
|
||||||
0xe0c8: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.01}},
|
0xe0c8: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||||
|
0xe0ca: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||||
|
|
||||||
# Hexagons
|
# Hexagons
|
||||||
0xe0cc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
0xe0cc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||||
0xe0cd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
0xe0cd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||||
|
|
||||||
# Legos
|
# Legos
|
||||||
|
@ -715,8 +742,8 @@ class font_patcher:
|
||||||
0xe0d1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0d1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||||
|
|
||||||
# Top and bottom trapezoid
|
# Top and bottom trapezoid
|
||||||
0xe0d2: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
0xe0d2: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'xy-ratio': 0.7}},
|
||||||
0xe0d4: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}}
|
0xe0d4: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'xy-ratio': 0.7}}
|
||||||
}
|
}
|
||||||
|
|
||||||
SYM_ATTR_DEFAULT = {
|
SYM_ATTR_DEFAULT = {
|
||||||
|
@ -739,14 +766,54 @@ class font_patcher:
|
||||||
'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
|
'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Most glyphs we want to maximize during the scale. However, there are some
|
# Most glyphs we want to maximize (individually) during the scale
|
||||||
# that need to be small or stay relative in size to each other.
|
# However, there are some that need to be small or stay relative in
|
||||||
# The following list are those glyphs. A tuple represents a range.
|
# 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.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# 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 (scaled) 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
|
||||||
|
# ]}
|
||||||
|
# If you want to not only scale but also shift as the refenerce glyph you give the
|
||||||
|
# data as 'GlyphsToScale+'. Note that only one set is used and the plus version is preferred.
|
||||||
|
#
|
||||||
|
# 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
|
DEVI_SCALE_LIST = {'ScaleGlyph': 0xE60E, # Android logo
|
||||||
'GlyphsToScale': [
|
'GlyphsToScale': [
|
||||||
(0xe6bd, 0xe6c3) # very small things
|
(0xe6bd, 0xe6c3) # very small things
|
||||||
]}
|
]}
|
||||||
FONTA_SCALE_LIST = {'GlyphsToScale': [
|
FONTA_SCALE_LIST = {'ScaleGroups': [
|
||||||
[0xf005, 0xf006, 0xf089], # star, star empty, half star
|
[0xf005, 0xf006, 0xf089], # star, star empty, half star
|
||||||
range(0xf026, 0xf028 + 1), # volume off, down, up
|
range(0xf026, 0xf028 + 1), # volume off, down, up
|
||||||
range(0xf02b, 0xf02c + 1), # tag, tags
|
range(0xf02b, 0xf02c + 1), # tag, tags
|
||||||
|
@ -756,8 +823,10 @@ class font_patcher:
|
||||||
range(0xf060, 0xf063 + 1), # arrows
|
range(0xf060, 0xf063 + 1), # arrows
|
||||||
[0xf053, 0xf054, 0xf077, 0xf078], # chevron all directions
|
[0xf053, 0xf054, 0xf077, 0xf078], # chevron all directions
|
||||||
range(0xf07d, 0xf07e + 1), # resize
|
range(0xf07d, 0xf07e + 1), # resize
|
||||||
[0xf0d7, 0xf0da, 0xf0dc, 0xf0fe], # caret all directions and same looking sort
|
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(0xf100, 0xf107 + 1), # angle
|
||||||
|
range(0xf130, 0xf131 + 1), # mic
|
||||||
range(0xf141, 0xf142 + 1), # ellipsis
|
range(0xf141, 0xf142 + 1), # ellipsis
|
||||||
range(0xf153, 0xf15a + 1), # currencies
|
range(0xf153, 0xf15a + 1), # currencies
|
||||||
range(0xf175, 0xf178 + 1), # long arrows
|
range(0xf175, 0xf178 + 1), # long arrows
|
||||||
|
@ -774,32 +843,44 @@ class font_patcher:
|
||||||
0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons
|
0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons
|
||||||
0xf0ca, # dash
|
0xf0ca, # dash
|
||||||
]}
|
]}
|
||||||
|
WEATH_SCALE_LIST = {'ScaleGroups': [
|
||||||
|
range(0xf095, 0xf0b0 + 1), # moon phases
|
||||||
|
range(0xf0b7, 0xf0c3 + 1), # wind strengths
|
||||||
|
range(0xf053, 0xf055 + 1), # thermometer
|
||||||
|
[0xf06e, 0xf070 ], # solar eclipse
|
||||||
|
[0xf042, 0xf045 ], # degree sign
|
||||||
|
]}
|
||||||
|
MDI_SCALE_LIST = {'ScaleGlyph': 0xf068d, # 'solid' fills complete design space
|
||||||
|
'GlyphsToScale+': [
|
||||||
|
(0xf0000, 0xfffff) # all because they are very well scaled already
|
||||||
|
]}
|
||||||
|
|
||||||
# Define the character ranges
|
# Define the character ranges
|
||||||
# Symbol font ranges
|
# Symbol font ranges
|
||||||
self.patch_set = [
|
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': "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, 'ScaleGlyph': DEVI_SCALE_LIST, '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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_POWERLINE},
|
{'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, '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, '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, '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, '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, '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, '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, '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, '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, '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, 'ScaleRules': 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.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, 'ScaleGlyph': FONTA_SCALE_LIST, 'Attributes': SYM_ATTR_FONTA},
|
{'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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Maximize
|
{'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, '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': 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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT}, # Heavy Circle (aka Power Off)
|
{'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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'Enabled': self.args.material, 'Name': "Material legacy", '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, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'Enabled': self.args.material, 'Name': "Material", 'Filename': "materialdesign/MaterialDesignIconsDesktop.ttf", 'Exact': True, 'SymStart': 0xF0001,'SymEnd': 0xF1AF0,'SrcStart': None, 'ScaleRules': MDI_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.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.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.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': 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': 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': 0X26A1, 'SymEnd': 0X26A1, 'SrcStart': None, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Zap
|
{'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': False, 'SymStart': 0xF27C, 'SymEnd': 0xF27C, 'SrcStart': 0xF4A9, 'ScaleGlyph': OCTI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT}, # Desktop
|
{'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.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': SYM_ATTR_DEFAULT},
|
{'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.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleGlyph': None, 'Attributes': CUSTOM_ATTR}
|
{'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):
|
def setup_line_dimensions(self):
|
||||||
|
@ -870,6 +951,7 @@ class font_patcher:
|
||||||
if gap > 0:
|
if gap > 0:
|
||||||
gap_top = int(gap / 2)
|
gap_top = int(gap / 2)
|
||||||
gap_bottom = gap - gap_top
|
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['ymin'] -= gap_bottom
|
||||||
self.font_dim['ymax'] += gap_top
|
self.font_dim['ymax'] += gap_top
|
||||||
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
||||||
|
@ -881,35 +963,69 @@ class font_patcher:
|
||||||
# Ignore the y-values, os2_winXXXXX values set above are used for line height
|
# Ignore the y-values, os2_winXXXXX values set above are used for line height
|
||||||
#
|
#
|
||||||
# 0x00-0x17f is the Latin Extended-A range
|
# 0x00-0x17f is the Latin Extended-A range
|
||||||
|
warned = self.args.quiet or self.args.nonmono # Do not warn if quiet or proportional target
|
||||||
for glyph in range(0x21, 0x17f):
|
for glyph in range(0x21, 0x17f):
|
||||||
if glyph in range(0x7F, 0xBF):
|
if glyph in range(0x7F, 0xBF) or glyph in [
|
||||||
continue # ignore special characters like '1/4' etc
|
0x132, 0x134, # IJ, ij (in Overpass Mono)
|
||||||
|
0x022, 0x027, 0x060, # Single and double quotes in Inconsolata LGC
|
||||||
|
0x0D0, 0x10F, 0x110, 0x111, 0x127, 0x13E, 0x140, 0x165, # Eth and others with stroke or caron in RobotoMono
|
||||||
|
]:
|
||||||
|
continue # ignore special characters like '1/4' etc and some specifics
|
||||||
try:
|
try:
|
||||||
(_, _, xmax, _) = self.sourceFont[glyph].boundingBox()
|
(_, _, xmax, _) = self.sourceFont[glyph].boundingBox()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
continue
|
continue
|
||||||
|
# print("WIDTH {:X} {} ({} {})".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||||
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
||||||
self.font_dim['width'] = self.sourceFont[glyph].width
|
self.font_dim['width'] = self.sourceFont[glyph].width
|
||||||
# print("New MAXWIDTH-A {} {} {}".format(glyph, self.sourceFont[glyph].width, xmax))
|
if not warned and glyph > 0x7a: # NOT 'basic' glyph, which includes a-zA-Z
|
||||||
|
print("Extended glyphs wider than basic glyphs")
|
||||||
|
warned = True
|
||||||
|
# print("New MAXWIDTH-A {} {} -> {} {}".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||||
if xmax > self.font_dim['xmax']:
|
if xmax > self.font_dim['xmax']:
|
||||||
self.font_dim['xmax'] = xmax
|
self.font_dim['xmax'] = xmax
|
||||||
# print("New MAXWIDTH-B {} {} {}".format(glyph, self.sourceFont[glyph].width, xmax))
|
# print("New MAXWIDTH-B {} {} -> {} {}".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||||
|
# print("FINAL", self.font_dim)
|
||||||
|
|
||||||
|
|
||||||
def get_scale_factor(self, sym_dim):
|
def get_scale_factors(self, sym_dim, stretch):
|
||||||
scale_ratio = 1
|
""" 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)
|
||||||
|
|
||||||
# We want to preserve x/y aspect ratio, so find biggest scale factor that allows symbol to fit
|
# For monospaced fonts all chars need to be maximum 'one' space wide
|
||||||
scale_ratio_x = self.font_dim['width'] / sym_dim['width']
|
# other fonts allows double width glyphs for 'pa' or if requested with '2'
|
||||||
scale_ratio_y = self.font_dim['height'] / sym_dim['height']
|
if self.args.single or (stretch != 'pa' and '2' not in stretch):
|
||||||
if scale_ratio_x > scale_ratio_y:
|
relative_width = 1.0
|
||||||
scale_ratio = scale_ratio_y
|
|
||||||
else:
|
else:
|
||||||
scale_ratio = scale_ratio_x
|
relative_width = 2.0
|
||||||
return scale_ratio
|
target_width = self.font_dim['width'] * relative_width
|
||||||
|
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']
|
||||||
|
scale_ratio_y = target_height / sym_dim['height']
|
||||||
|
|
||||||
|
if stretch == 'pa':
|
||||||
|
# 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:
|
||||||
|
# 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
|
||||||
|
else:
|
||||||
|
# Keep the not-stretched direction
|
||||||
|
if not 'x' in stretch:
|
||||||
|
scale_ratio_x = 1.0
|
||||||
|
if not 'y' in stretch:
|
||||||
|
scale_ratio_y = 1.0
|
||||||
|
|
||||||
|
return (scale_ratio_x, scale_ratio_y)
|
||||||
|
|
||||||
|
|
||||||
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 """
|
""" Copies symbol glyphs into self.sourceFont """
|
||||||
progressText = ''
|
progressText = ''
|
||||||
careful = False
|
careful = False
|
||||||
|
@ -933,19 +1049,23 @@ class font_patcher:
|
||||||
symbolFontSelection = [ x for x in symbolFont.selection.byGlyphs if x.unicode >= 0 ]
|
symbolFontSelection = [ x for x in symbolFont.selection.byGlyphs if x.unicode >= 0 ]
|
||||||
glyphSetLength = len(symbolFontSelection)
|
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")
|
sys.stdout.write("Adding " + str(max(1, glyphSetLength)) + " Glyphs from " + setName + " Set \n")
|
||||||
|
|
||||||
currentSourceFontGlyph = -1 # initialize for the exactEncoding case
|
currentSourceFontGlyph = -1 # initialize for the exactEncoding case
|
||||||
|
width_warning = False
|
||||||
|
|
||||||
for index, sym_glyph in enumerate(symbolFontSelection):
|
for index, sym_glyph in enumerate(symbolFontSelection):
|
||||||
index = max(1, index)
|
index = max(1, index)
|
||||||
|
|
||||||
try:
|
sym_attr = attributes.get(sym_glyph.unicode)
|
||||||
sym_attr = attributes[sym_glyph.unicode]
|
if sym_attr is None:
|
||||||
except KeyError:
|
|
||||||
sym_attr = attributes['default']
|
sym_attr = attributes['default']
|
||||||
|
|
||||||
|
if self.font_extrawide:
|
||||||
|
# Do not allow 'xy2' scaling
|
||||||
|
sym_attr['stretch'] = sym_attr['stretch'].replace('2', '')
|
||||||
|
|
||||||
if exactEncoding:
|
if exactEncoding:
|
||||||
# Use the exact same hex values for the source font as for the symbol font.
|
# Use the exact same hex values for the source font as for the symbol font.
|
||||||
# Problem is we do not know the codepoint of the sym_glyph and because it
|
# Problem is we do not know the codepoint of the sym_glyph and because it
|
||||||
|
@ -966,7 +1086,11 @@ class font_patcher:
|
||||||
currentSourceFontGlyph = sourceFontStart + sourceFontCounter
|
currentSourceFontGlyph = sourceFontStart + sourceFontCounter
|
||||||
sourceFontCounter += 1
|
sourceFontCounter += 1
|
||||||
|
|
||||||
if self.args.quiet is False:
|
# For debugging process only limited glyphs
|
||||||
|
# if currentSourceFontGlyph != 0xe7bd:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
if not self.args.quiet:
|
||||||
if self.args.progressbars:
|
if self.args.progressbars:
|
||||||
update_progress(round(float(index + 1) / glyphSetLength, 2))
|
update_progress(round(float(index + 1) / glyphSetLength, 2))
|
||||||
else:
|
else:
|
||||||
|
@ -977,7 +1101,7 @@ class font_patcher:
|
||||||
# check if a glyph already exists in this location
|
# check if a glyph already exists in this location
|
||||||
if careful or 'careful' in sym_attr['params'] or currentSourceFontGlyph in self.essential:
|
if careful or 'careful' in sym_attr['params'] or currentSourceFontGlyph in self.essential:
|
||||||
if currentSourceFontGlyph in self.sourceFont:
|
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'
|
careful_type = 'essential' if currentSourceFontGlyph in self.essential else 'existing'
|
||||||
print(" Found {} Glyph at {:X}. Skipping...".format(careful_type, currentSourceFontGlyph))
|
print(" Found {} Glyph at {:X}. Skipping...".format(careful_type, currentSourceFontGlyph))
|
||||||
# We don't want to touch anything so move to next Glyph
|
# We don't want to touch anything so move to next Glyph
|
||||||
|
@ -988,6 +1112,9 @@ class font_patcher:
|
||||||
if currentSourceFontGlyph in self.sourceFont:
|
if currentSourceFontGlyph in self.sourceFont:
|
||||||
self.sourceFont[currentSourceFontGlyph].removePosSub("*")
|
self.sourceFont[currentSourceFontGlyph].removePosSub("*")
|
||||||
|
|
||||||
|
# This will destroy any content currently in currentSourceFontGlyph, so do it first
|
||||||
|
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, symbolFont, currentSourceFontGlyph) if scaleRules is not None else None
|
||||||
|
|
||||||
# Select and copy symbol from its encoding point
|
# Select and copy symbol from its encoding point
|
||||||
# We need to do this select after the careful check, this way we don't
|
# We need to do this select after the careful check, this way we don't
|
||||||
# reset our selection before starting the next loop
|
# reset our selection before starting the next loop
|
||||||
|
@ -998,54 +1125,48 @@ class font_patcher:
|
||||||
self.sourceFont.selection.select(currentSourceFontGlyph)
|
self.sourceFont.selection.select(currentSourceFontGlyph)
|
||||||
self.sourceFont.paste()
|
self.sourceFont.paste()
|
||||||
self.sourceFont[currentSourceFontGlyph].glyphname = sym_glyph.glyphname
|
self.sourceFont[currentSourceFontGlyph].glyphname = sym_glyph.glyphname
|
||||||
scale_ratio_x = 1
|
self.sourceFont[currentSourceFontGlyph].manualHints = True # No autohints for symbols
|
||||||
scale_ratio_y = 1
|
|
||||||
|
|
||||||
# Prepare symbol glyph dimensions
|
# Prepare symbol glyph dimensions
|
||||||
sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph])
|
sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph])
|
||||||
|
if glyph_scale_data is not None:
|
||||||
# Now that we have copy/pasted the glyph, if we are creating a monospace
|
if glyph_scale_data[1] is not None:
|
||||||
# font we need to scale and move the glyphs. It is possible to have
|
sym_dim = glyph_scale_data[1] # Use combined bounding box
|
||||||
# empty glyphs, so we need to skip those.
|
# This is roughly alike get_scale_factors(glyph_scale_data[1], 'pa')
|
||||||
if self.args.single and sym_dim['width'] and sym_dim['height']:
|
# Except we do not have glyph_scale_data[1] always...
|
||||||
# If we want to preserve that aspect ratio of the glyphs we need to
|
(scale_ratio_x, scale_ratio_y) = (glyph_scale_data[0], glyph_scale_data[0])
|
||||||
# find the largest possible scaling factor that will allow the glyph
|
else:
|
||||||
# to fit in both the x and y directions
|
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, sym_attr['stretch'])
|
||||||
if sym_attr['stretch'] == 'pa':
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
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']
|
|
||||||
# end if single width
|
|
||||||
|
|
||||||
# non-monospace (double width glyphs)
|
|
||||||
# elif sym_dim['width'] and sym_dim['height']:
|
|
||||||
# any special logic we want to apply for double-width variation
|
|
||||||
# would go here
|
|
||||||
|
|
||||||
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']
|
|
||||||
|
|
||||||
overlap = sym_attr['params'].get('overlap')
|
overlap = sym_attr['params'].get('overlap')
|
||||||
|
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
|
||||||
|
|
||||||
if scale_ratio_x != 1 or scale_ratio_y != 1:
|
# Size in x to size in y ratio limit (to prevent over-wide glyphs)
|
||||||
if overlap:
|
xy_ratio_max = sym_attr['params'].get('xy-ratio')
|
||||||
scale_ratio_x *= 1 + overlap
|
if (xy_ratio_max):
|
||||||
scale_ratio_y *= 1 + overlap
|
xy_ratio = sym_dim['width'] * scale_ratio_x / (sym_dim['height'] * scale_ratio_y)
|
||||||
|
if xy_ratio > xy_ratio_max:
|
||||||
|
scale_ratio_x = scale_ratio_x * xy_ratio_max / xy_ratio
|
||||||
|
|
||||||
|
if scale_ratio_x != 1.0 or scale_ratio_y != 1.0:
|
||||||
self.sourceFont[currentSourceFontGlyph].transform(psMat.scale(scale_ratio_x, scale_ratio_y))
|
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])
|
sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph])
|
||||||
|
# Use combined bounding box?
|
||||||
|
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']
|
||||||
|
scaleglyph_dim['xmax'] = sym_dim['xmax']
|
||||||
|
scaleglyph_dim['width'] = sym_dim['width']
|
||||||
|
sym_dim = scaleglyph_dim
|
||||||
|
|
||||||
y_align_distance = 0
|
y_align_distance = 0
|
||||||
if sym_attr['valign'] == 'c':
|
if sym_attr['valign'] == 'c':
|
||||||
# Center the symbol vertically by matching the center of the line height and center of symbol
|
# Center the symbol vertically by matching the center of the line height and center of symbol
|
||||||
|
@ -1055,7 +1176,11 @@ class font_patcher:
|
||||||
|
|
||||||
# Handle glyph l/r/c alignment
|
# Handle glyph l/r/c alignment
|
||||||
x_align_distance = 0
|
x_align_distance = 0
|
||||||
if sym_attr['align']:
|
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)
|
# First find the baseline x-alignment (left alignment amount)
|
||||||
x_align_distance = self.font_dim['xmin'] - sym_dim['xmin']
|
x_align_distance = self.font_dim['xmin'] - sym_dim['xmin']
|
||||||
if sym_attr['align'] == 'c':
|
if sym_attr['align'] == 'c':
|
||||||
|
@ -1064,12 +1189,15 @@ class font_patcher:
|
||||||
elif sym_attr['align'] == 'r':
|
elif sym_attr['align'] == 'r':
|
||||||
# Right align
|
# Right align
|
||||||
x_align_distance += self.font_dim['width'] - sym_dim['width']
|
x_align_distance += self.font_dim['width'] - sym_dim['width']
|
||||||
|
if not self.args.single and '2' in sym_attr['stretch']:
|
||||||
|
x_align_distance += self.font_dim['width']
|
||||||
|
|
||||||
if overlap:
|
if overlap:
|
||||||
overlap_width = self.font_dim['width'] * overlap
|
overlap_width = self.font_dim['width'] * overlap
|
||||||
if sym_attr['align'] == 'l':
|
if sym_attr['align'] == 'l':
|
||||||
x_align_distance -= overlap_width
|
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
|
x_align_distance += overlap_width
|
||||||
|
|
||||||
align_matrix = psMat.translate(x_align_distance, y_align_distance)
|
align_matrix = psMat.translate(x_align_distance, y_align_distance)
|
||||||
|
@ -1085,11 +1213,24 @@ class font_patcher:
|
||||||
# same width for all character glyphs. This needs to be done for all glyphs,
|
# 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.
|
# even the ones that are empty and didn't go through the scaling operations.
|
||||||
# It should come after setting the glyph bearings
|
# It should come after setting the glyph bearings
|
||||||
self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph])
|
if not self.args.nonmono:
|
||||||
|
self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph])
|
||||||
# Re-remove negative bearings for target font with variable advance width
|
else:
|
||||||
if self.args.nonmono:
|
# Target font with variable advance width get the icons with their native widths
|
||||||
self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph])
|
# 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
|
||||||
|
# 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
|
# Check if the inserted glyph is scaled correctly for monospace
|
||||||
if self.args.single:
|
if self.args.single:
|
||||||
|
@ -1100,7 +1241,7 @@ class font_patcher:
|
||||||
|
|
||||||
# end for
|
# 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")
|
sys.stdout.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1149,53 +1290,82 @@ class font_patcher:
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def prepareScaleGlyph(self, scaleGlyph, symbolFont, destGlyph):
|
def prepareScaleRules(self, scaleRules, symbolFont, destGlyph):
|
||||||
""" Prepare raw ScaleGlyph data for use """
|
""" 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:
|
||||||
# 'GlyphsToScale': List of ((lists of glyph codes) or (ranges of glyph codes)) that shall be scaled
|
# '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 'GlyphsToScale' (generated by this function)
|
# '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 dim_dict describes the combined bounding box of all glyphs in one ScaleGroups group
|
||||||
# Example:
|
# Example:
|
||||||
# { 'GlyphsToScale': [ range(1, 3), [ 7, 10 ], ],
|
# { 'ScaleGroups': [ 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
|
# 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.
|
# as rescaled all with the same and maximum possible (for the included glyphs) 'pa' factor.
|
||||||
|
# If the 'bbdims' is present they all shall be shifted in the same way.
|
||||||
#
|
#
|
||||||
# Previously this structure has been used:
|
# Previously this structure has been used:
|
||||||
# 'ScaleGlyph' Lead glyph, which scaling factor is taken
|
# '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
|
# 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).
|
# a specific character, which needs to be manually selected (on each symbol font update).
|
||||||
# Previous entries are automatically rewritten to the new style.
|
# Previous entries are automatically rewritten to the new style.
|
||||||
if 'scales' in scaleGlyph:
|
#
|
||||||
|
# Note that scaleRules is overwritten with the added data.
|
||||||
|
if 'scales' in scaleRules:
|
||||||
# Already prepared... must not happen, ignore call
|
# Already prepared... must not happen, ignore call
|
||||||
return
|
return
|
||||||
if 'ScaleGlyph' in scaleGlyph:
|
|
||||||
# old method. Rewrite to new.
|
|
||||||
flat_list = []
|
|
||||||
for i in scaleGlyph['GlyphsToScale']:
|
|
||||||
if isinstance(i, tuple):
|
|
||||||
flat_list += list(range(i[0], i[1] + 1))
|
|
||||||
else:
|
|
||||||
flat_list.append(i)
|
|
||||||
scaleGlyph['GlyphsToScale'] = [ flat_list ]
|
|
||||||
sym_dim = get_glyph_dimensions(symbolFont[scaleGlyph['ScaleGlyph']])
|
|
||||||
scaleGlyph['scales'] = [ self.get_scale_factor(sym_dim) ]
|
|
||||||
else:
|
|
||||||
scaleGlyph['scales'] = []
|
|
||||||
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))
|
|
||||||
|
|
||||||
def get_glyph_scale(self, unicode_value, scaleGlyph, symbolFont):
|
scaleRules['scales'] = []
|
||||||
""" Determines whether or not to use scaled glyphs for glyphs in passed glyph_list """
|
scaleRules['bbdims'] = []
|
||||||
# Potentially destorys the contents of self.sourceFont[unicode_value]
|
if 'ScaleGroups' not in scaleRules:
|
||||||
if not 'scales' in scaleGlyph:
|
scaleRules['ScaleGroups'] = []
|
||||||
self.prepareScaleGlyph(scaleGlyph, symbolFont, self.sourceFont[unicode_value])
|
for group in scaleRules['ScaleGroups']:
|
||||||
for glyph_list, scale in zip(scaleGlyph['GlyphsToScale'], scaleGlyph['scales']):
|
sym_dim = get_multiglyph_boundingBox([ symbolFont[g] if g in symbolFont else None for g in group ], destGlyph)
|
||||||
if unicode_value in glyph_list:
|
scale = self.get_scale_factors(sym_dim, 'pa')[0]
|
||||||
return scale
|
scaleRules['scales'].append(scale)
|
||||||
return False
|
scaleRules['bbdims'].append(sym_dim)
|
||||||
|
|
||||||
|
if 'ScaleGlyph' in scaleRules:
|
||||||
|
# Rewrite to equivalent ScaleGroup
|
||||||
|
group_list = []
|
||||||
|
if 'GlyphsToScale+' in scaleRules:
|
||||||
|
key = 'GlyphsToScale+'
|
||||||
|
plus = True
|
||||||
|
else:
|
||||||
|
key = 'GlyphsToScale'
|
||||||
|
plus = False
|
||||||
|
for i in scaleRules[key]:
|
||||||
|
if isinstance(i, tuple):
|
||||||
|
group_list.append(range(i[0], i[1] + 1))
|
||||||
|
else:
|
||||||
|
group_list.append(i)
|
||||||
|
sym_dim = get_glyph_dimensions(symbolFont[scaleRules['ScaleGlyph']])
|
||||||
|
scale = self.get_scale_factors(sym_dim, 'pa')[0]
|
||||||
|
scaleRules['ScaleGroups'].append(group_list)
|
||||||
|
scaleRules['scales'].append(scale)
|
||||||
|
if plus:
|
||||||
|
scaleRules['bbdims'].append(sym_dim)
|
||||||
|
else:
|
||||||
|
scaleRules['bbdims'].append(None) # The 'old' style keeps just the scale, not the positioning
|
||||||
|
|
||||||
|
def get_glyph_scale(self, symbol_unicode, scaleRules, symbolFont, dest_unicode):
|
||||||
|
""" Determines whether or not to use scaled glyphs for glyph in passed symbol_unicode """
|
||||||
|
# Potentially destorys the contents of self.sourceFont[dest_unicode]
|
||||||
|
if not 'scales' in scaleRules:
|
||||||
|
if not dest_unicode in self.sourceFont:
|
||||||
|
self.sourceFont.createChar(dest_unicode)
|
||||||
|
self.prepareScaleRules(scaleRules, symbolFont, self.sourceFont[dest_unicode])
|
||||||
|
for glyph_list, scale, box in zip(scaleRules['ScaleGroups'], scaleRules['scales'], scaleRules['bbdims']):
|
||||||
|
for e in glyph_list:
|
||||||
|
if isinstance(e, range):
|
||||||
|
if symbol_unicode in e:
|
||||||
|
return (scale, box)
|
||||||
|
elif symbol_unicode == e:
|
||||||
|
return (scale, box)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def replace_font_name(font_name, replacement_dict):
|
def replace_font_name(font_name, replacement_dict):
|
||||||
|
@ -1232,7 +1402,7 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None):
|
||||||
# If destGlyph is given the glyph(s) are first copied over into that
|
# If destGlyph is given the glyph(s) are first copied over into that
|
||||||
# glyph and measured in that font (to avoid rounding errors)
|
# glyph and measured in that font (to avoid rounding errors)
|
||||||
# Leaves the destGlyph in unknown state!
|
# Leaves the destGlyph in unknown state!
|
||||||
bbox = [ None, None, None, None ]
|
bbox = [ None, None, None, None, None ]
|
||||||
for glyph in glyphs:
|
for glyph in glyphs:
|
||||||
if glyph is None:
|
if glyph is None:
|
||||||
# Glyph has been in defining range but is not in the actual font
|
# Glyph has been in defining range but is not in the actual font
|
||||||
|
@ -1244,23 +1414,52 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None):
|
||||||
destGlyph.font.paste()
|
destGlyph.font.paste()
|
||||||
glyph = destGlyph
|
glyph = destGlyph
|
||||||
gbb = glyph.boundingBox()
|
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
|
||||||
bbox[0] = gbb[0] if bbox[0] is None or bbox[0] > gbb[0] else bbox[0]
|
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[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[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]
|
bbox[3] = gbb[3] if bbox[3] is None or bbox[3] < gbb[3] else bbox[3]
|
||||||
|
if not bbox[4]:
|
||||||
|
bbox[4] = -gadvance # Negative for one/first glyph
|
||||||
|
else:
|
||||||
|
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 {
|
return {
|
||||||
'xmin' : bbox[0],
|
'xmin' : bbox[0],
|
||||||
'ymin' : bbox[1],
|
'ymin' : bbox[1],
|
||||||
'xmax' : bbox[2],
|
'xmax' : bbox[2],
|
||||||
'ymax' : bbox[3],
|
'ymax' : bbox[3],
|
||||||
'width' : bbox[2] + (-bbox[0]),
|
'width' : bbox[2] + (-bbox[0]),
|
||||||
'height': bbox[3] + (-bbox[1]),
|
'height' : bbox[3] + (-bbox[1]),
|
||||||
|
'advance': bbox[4], # advance width if monospaced
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_glyph_dimensions(glyph):
|
def get_glyph_dimensions(glyph):
|
||||||
""" Returns dict of the dimesions of the glyph passed to it. """
|
""" Returns dict of the dimesions of the glyph passed to it. """
|
||||||
return get_multiglyph_boundingBox([ glyph ])
|
return get_multiglyph_boundingBox([ 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'] is not None 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):
|
def update_progress(progress):
|
||||||
""" Updates progress bar length.
|
""" Updates progress bar length.
|
||||||
|
|
||||||
|
@ -1377,7 +1576,7 @@ def setup_arguments():
|
||||||
for alias in sym_font_arg_aliases:
|
for alias in sym_font_arg_aliases:
|
||||||
if alias in sys.argv:
|
if alias in sys.argv:
|
||||||
found = True
|
found = True
|
||||||
if found is not True:
|
if not found:
|
||||||
font_complete = False
|
font_complete = False
|
||||||
args.complete = font_complete
|
args.complete = font_complete
|
||||||
|
|
||||||
|
@ -1385,7 +1584,7 @@ def setup_arguments():
|
||||||
args.windows = False
|
args.windows = False
|
||||||
|
|
||||||
if args.nonmono and args.single:
|
if args.nonmono and args.single:
|
||||||
print("Warniung: Specified contradicting --variable-width-glyphs and --use-single-width-glyph. Ignoring --variable-width-glyphs.")
|
print("Warning: Specified contradicting --variable-width-glyphs and --use-single-width-glyph. Ignoring --variable-width-glyphs.")
|
||||||
args.nonmono = False
|
args.nonmono = False
|
||||||
|
|
||||||
make_sure_path_exists(args.outputdir)
|
make_sure_path_exists(args.outputdir)
|
||||||
|
@ -1394,6 +1593,18 @@ def setup_arguments():
|
||||||
if not os.access(args.font, os.R_OK):
|
if not os.access(args.font, os.R_OK):
|
||||||
sys.exit("{}: Can not open font file for reading: {}".format(projectName, args.font))
|
sys.exit("{}: Can not open font file for reading: {}".format(projectName, args.font))
|
||||||
is_ttc = len(fontforge.fontsInFile(args.font)) > 1
|
is_ttc = len(fontforge.fontsInFile(args.font)) > 1
|
||||||
|
try:
|
||||||
|
source_font_test = TableHEADWriter(args.font)
|
||||||
|
args.is_variable = source_font_test.find_table([b'avar', b'cvar', b'fvar', b'gvarb', b'HVAR', b'MVAR', b'VVAR'], 0)
|
||||||
|
if args.is_variable:
|
||||||
|
print(" Warning: Source font is a variable open type font (VF), opening might fail...")
|
||||||
|
except:
|
||||||
|
args.is_variable = False
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
source_font_test.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if args.extension == "":
|
if args.extension == "":
|
||||||
args.extension = os.path.splitext(args.font)[1]
|
args.extension = os.path.splitext(args.font)[1]
|
||||||
|
@ -1418,7 +1629,8 @@ def main():
|
||||||
sourceFonts = []
|
sourceFonts = []
|
||||||
all_fonts = fontforge.fontsInFile(args.font)
|
all_fonts = fontforge.fontsInFile(args.font)
|
||||||
for i, subfont in enumerate(all_fonts):
|
for i, subfont in enumerate(all_fonts):
|
||||||
print("\n{}: Processing {} ({}/{})".format(projectName, subfont, i + 1, len(all_fonts)))
|
if len(all_fonts) > 1:
|
||||||
|
print("\n{}: Processing {} ({}/{})".format(projectName, subfont, i + 1, len(all_fonts)))
|
||||||
try:
|
try:
|
||||||
sourceFonts.append(fontforge.open("{}({})".format(args.font, subfont), 1)) # 1 = ("fstypepermitted",))
|
sourceFonts.append(fontforge.open("{}({})".format(args.font, subfont), 1)) # 1 = ("fstypepermitted",))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -1427,7 +1639,7 @@ def main():
|
||||||
|
|
||||||
patcher.patch(sourceFonts[-1])
|
patcher.patch(sourceFonts[-1])
|
||||||
|
|
||||||
print("\nDone with Patch Sets, generating font...\n")
|
print("Done with Patch Sets, generating font...")
|
||||||
for f in sourceFonts:
|
for f in sourceFonts:
|
||||||
patcher.setup_font_names(f)
|
patcher.setup_font_names(f)
|
||||||
patcher.generate(sourceFonts)
|
patcher.generate(sourceFonts)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
REPO_URL=git@github.com:ryanoasis/nerd-fonts.git
|
REPO_URL='https://github.com/ryanoasis/nerd-fonts.git'
|
||||||
|
|
||||||
rm -rf nerd-fonts
|
rm -rf nerd-fonts
|
||||||
|
|
||||||
|
|
Binary file not shown.
20
src/glyphs/materialdesign/LICENSE
Normal file
20
src/glyphs/materialdesign/LICENSE
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Pictogrammers Free License
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
This icon collection is released as free, open source, and GPL friendly by
|
||||||
|
the [Pictogrammers](http://pictogrammers.com/) icon group. You may use it
|
||||||
|
for commercial projects, open source projects, or anything really.
|
||||||
|
|
||||||
|
# Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
Some of the icons are redistributed under the Apache 2.0 license. All other
|
||||||
|
icons are either redistributed under their respective licenses or are
|
||||||
|
distributed under the Apache 2.0 license.
|
||||||
|
|
||||||
|
# Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
All web and desktop fonts are distributed under the Apache 2.0 license. Web
|
||||||
|
and desktop fonts contain some icons that are redistributed under the Apache
|
||||||
|
2.0 license. All other icons are either redistributed under their respective
|
||||||
|
licenses or are distributed under the Apache 2.0 license.
|
||||||
|
|
||||||
|
# Code: MIT (https://opensource.org/licenses/MIT)
|
||||||
|
The MIT license applies to all non-font and non-icon files.
|
BIN
src/glyphs/materialdesign/MaterialDesignIconsDesktop.ttf
Normal file
BIN
src/glyphs/materialdesign/MaterialDesignIconsDesktop.ttf
Normal file
Binary file not shown.
BIN
src/glyphs/materialdesign/MaterialDesignIconsDesktop_orig.ttf
Normal file
BIN
src/glyphs/materialdesign/MaterialDesignIconsDesktop_orig.ttf
Normal file
Binary file not shown.
23
src/glyphs/materialdesign/README.md
Normal file
23
src/glyphs/materialdesign/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
This folder contains the source for the (current) Material Design Icons.
|
||||||
|
Source is https://github.com/Templarian/MaterialDesign-Font
|
||||||
|
|
||||||
|
Last fetch date is Oct 6, 2022.
|
||||||
|
|
||||||
|
After fetching a new file one needs to correct our cheat-sheet by updating `bin/scripts/lib/i_md.sh`.
|
||||||
|
Use the tool:
|
||||||
|
```
|
||||||
|
cd bin/scripts
|
||||||
|
mv lib/i_md.sh lib/i_md.sh_
|
||||||
|
python3 generate-glyph-info-from-set.py --start f0001 -end f1af0 -font ../../src/glyphs/materialdesign/MaterialDesignIconsDesktop.ttf -offset 0 -prefix md > lib/i_md.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Open old and new definitions shell script and copy the header from the old file to the autogenerated file. Adapt the values in the new file's header. Remove the last line in the new file (it contains the number of glyphs that is needed for the updated header). Yes, that is some manual labor.
|
||||||
|
|
||||||
|
|
||||||
|
## Source bugs fixed
|
||||||
|
|
||||||
|
Glyph 0xF1522 is broken in the original font. We fixed that one glyph manually.
|
||||||
|
|
||||||
|
See https://github.com/Templarian/MaterialDesign-Font/issues/9
|
Binary file not shown.
Loading…
Reference in a new issue