mirror of
https://github.com/daylinmorgan/monolisa-nerdfont-patch.git
synced 2024-11-10 00:43:15 -06:00
Compare commits
6 commits
e577167bdc
...
56a06a32c9
Author | SHA1 | Date | |
---|---|---|---|
56a06a32c9 | |||
cdb6bacf37 | |||
a8175ab675 | |||
|
32a93386bd | ||
|
04081cc370 | ||
|
718ba2179f |
9 changed files with 446 additions and 161 deletions
|
@ -1,23 +1,23 @@
|
|||
exclude: "^(src/.*|bin/font-patcher)"
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.9.3
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
rev: 23.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 4.0.1
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.0.257'
|
||||
hooks:
|
||||
- id: flake8
|
||||
- id: ruff
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.9.0.2
|
||||
hooks:
|
||||
|
|
152
LICENSE
Normal file
152
LICENSE
Normal file
|
@ -0,0 +1,152 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Daylin Morgan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-------
|
||||
|
||||
See below for licensing information related to ./src/ and ./bin/font-patcher.
|
||||
|
||||
# Nerd Fonts Licensing
|
||||
|
||||
There are various sources used under various licenses:
|
||||
|
||||
* Nerd Fonts source fonts, patched fonts, and folders with explict OFL SIL files are licensed under SIL OPEN FONT LICENSE Version 1.1 (see below).
|
||||
* Nerd Fonts original source code files (such as `.sh`, `.py`, `font-patcher` and others) are licensed under the MIT License (MIT) (see below).
|
||||
* Many other licenses are present in this project for even more detailed breakdown see: [License Audit](https://github.com/ryanoasis/nerd-fonts/blob/-/license-audit.md).
|
||||
|
||||
## Source files not in folders containing an explicit license are using the MIT License (MIT)
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Ryan L McIntyre
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
## Various Fonts, Patched Fonts, SVGs, Glyph Fonts, and any files in a folder with explicit SIL OFL 1.1 License
|
||||
|
||||
Copyright (c) 2014, Ryan L McIntyre (https://ryanlmcintyre.com).
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
9
Makefile
9
Makefile
|
@ -1,13 +1,10 @@
|
|||
-include .env
|
||||
ARGS ?= -c
|
||||
|
||||
NF_SRC := $(shell ./bin/get-font-files src)
|
||||
FONT_FLAGS := $(shell ./bin/get-font-files MonoLisa 'otf,ttf,woff,woff2')
|
||||
|
||||
patch: ./bin/font-patcher ## apply nerd fonts patch |> -gs b_magenta -ms bold
|
||||
@./bin/patch-monolisa \
|
||||
$(FONT_FLAGS) \
|
||||
$(ARGS)
|
||||
@./patch-monolisa \
|
||||
$(ARGS) \
|
||||
-f MonoLisa/
|
||||
|
||||
update-fonts: ## move fonts and update fc-cache
|
||||
$(call msg,Adding Fonts To System)
|
||||
|
|
|
@ -11,7 +11,7 @@ tested w/ MonoLisa v2.003
|
|||
## Dependencies
|
||||
|
||||
- `python`
|
||||
- `make`
|
||||
- `make` (optional)
|
||||
- `fontforge` OR `docker`
|
||||
|
||||
|
||||
|
@ -52,7 +52,7 @@ you can easily apply the nerd font patches with `make`.
|
|||
To patch all font types use the default `patch` rule.
|
||||
|
||||
```bash
|
||||
make
|
||||
make # or ./patch-monolisa -f MonoLisa -c
|
||||
```
|
||||
|
||||
By default the complete (`-c`) flag is passed to the font-patcher script to include all icons/symbols.
|
||||
|
@ -62,7 +62,7 @@ You can change this by specifying the `ARGS` at runtime.
|
|||
ARGS="-c -w" make patch
|
||||
```
|
||||
|
||||
See `./bin/patch-monolisa --help` and `./bin/font-patcher --help` for available `ARGS`.
|
||||
See `./patch-monolisa --help` and `./bin/font-patcher --help` for available `ARGS`.
|
||||
|
||||
You can find your patched fonts in the `patched/` directory
|
||||
|
||||
|
|
375
bin/font-patcher
375
bin/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.5.2"
|
||||
script_version = "3.6.1"
|
||||
|
||||
version = "2.3.3"
|
||||
projectName = "Nerd Fonts"
|
||||
|
@ -21,6 +21,7 @@ from argparse import RawTextHelpFormatter
|
|||
import errno
|
||||
import subprocess
|
||||
import json
|
||||
from enum import Enum
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
|
@ -130,7 +131,7 @@ class TableHEADWriter:
|
|||
|
||||
def goto(self, where):
|
||||
""" Go to a named location in the file or to the specified index """
|
||||
if type(where) is str:
|
||||
if isinstance(where, str):
|
||||
positions = {'checksumAdjustment': 2+2+4,
|
||||
'flags': 2+2+4+4+4,
|
||||
'lowestRecPPEM': 2+2+4+4+4+2+2+8+8+2+2+2+2+2,
|
||||
|
@ -194,6 +195,14 @@ def panose_check_to_text(value, panose = False):
|
|||
return "Panose says \"monospaced\""
|
||||
return "Panose is invalid" + (" ({})".format(list(panose)) if panose else "")
|
||||
|
||||
def panose_proportion_to_text(value):
|
||||
""" Interpret a Panose proportion value (4th value) for family 2 (latin text) """
|
||||
proportion = {
|
||||
0: "Any", 1: "No Fit", 2: "Old Style", 3: "Modern", 4: "Even Width",
|
||||
5: "Extended", 6: "Condensed", 7: "Very Extended", 8: "Very Condensed",
|
||||
9: "Monospaced" }
|
||||
return proportion.get(value, "??? {}".format(value))
|
||||
|
||||
def is_monospaced(font):
|
||||
""" Check if a font is probably monospaced """
|
||||
# Some fonts lie (or have not any Panose flag set), spot check monospaced:
|
||||
|
@ -222,6 +231,20 @@ def is_monospaced(font):
|
|||
# We believe our own check more then Panose ;-D
|
||||
return (width_mono, None if width_mono else glyph)
|
||||
|
||||
def force_panose_monospaced(font):
|
||||
""" Forces the Panose flag to monospaced if they are unset or halfway ok already """
|
||||
# For some Windows applications (e.g. 'cmd'), they seem to honour the Panose table
|
||||
# https://forum.high-logic.com/postedfiles/Panose.pdf
|
||||
panose = list(font.os2_panose)
|
||||
if panose[0] == 0: # 0 (1st value) = family kind; 0 = any (default)
|
||||
panose[0] = 2 # make kind latin text and display
|
||||
print(" Setting Panose 'Family Kind' to 'Latin Text and Display' (was 'Any')")
|
||||
font.os2_panose = tuple(panose)
|
||||
if panose[0] == 2 and panose[3] != 9:
|
||||
print(" Setting Panose 'Proportion' to 'Monospaced' (was '{}')".format(panose_proportion_to_text(panose[3])))
|
||||
panose[3] = 9 # 3 (4th value) = proportion; 9 = monospaced
|
||||
font.os2_panose = tuple(panose)
|
||||
|
||||
def get_advance_width(font, extended, minimum):
|
||||
""" Get the maximum/minimum advance width in the extended(?) range """
|
||||
width = 0
|
||||
|
@ -248,6 +271,17 @@ def report_advance_widths(font):
|
|||
get_advance_width(font, True, True), get_advance_width(font, False, True),
|
||||
get_advance_width(font, False, False), get_advance_width(font, True, False))
|
||||
|
||||
def get_btb_metrics(font):
|
||||
""" Get the baseline to baseline distance for all three metrics """
|
||||
hhea_height = font.hhea_ascent - font.hhea_descent
|
||||
typo_height = font.os2_typoascent - font.os2_typodescent
|
||||
win_height = font.os2_winascent + font.os2_windescent
|
||||
win_gap = max(0, font.hhea_linegap - win_height + hhea_height)
|
||||
hhea_btb = hhea_height + font.hhea_linegap
|
||||
typo_btb = typo_height + font.os2_typolinegap
|
||||
win_btb = win_height + win_gap
|
||||
return (hhea_btb, typo_btb, win_btb, win_gap)
|
||||
|
||||
|
||||
class font_patcher:
|
||||
def __init__(self, args):
|
||||
|
@ -258,6 +292,7 @@ class font_patcher:
|
|||
self.patch_set = None # class 'list'
|
||||
self.font_dim = None # class 'dict'
|
||||
self.font_extrawide = False
|
||||
self.source_monospaced = None # Later True or False
|
||||
self.onlybitmaps = 0
|
||||
self.essential = set()
|
||||
self.config = configparser.ConfigParser(empty_lines_in_values=False, allow_no_value=True)
|
||||
|
@ -267,8 +302,7 @@ class font_patcher:
|
|||
self.setup_version()
|
||||
self.get_essential_references()
|
||||
self.setup_name_backup(font)
|
||||
if self.args.single:
|
||||
self.assert_monospace()
|
||||
self.assert_monospace()
|
||||
self.remove_ligatures()
|
||||
self.setup_patch_set()
|
||||
self.get_sourcefont_dimensions()
|
||||
|
@ -280,13 +314,6 @@ class font_patcher:
|
|||
# Force width to be equal on all glyphs to ensure the font is considered monospaced on Windows.
|
||||
# This needs to be done on all characters, as some information seems to be lost from the original font file.
|
||||
self.set_sourcefont_glyph_widths()
|
||||
# For some Windows applications (e.g. 'cmd') that is not enough. But they seem to honour the Panose table
|
||||
# https://forum.high-logic.com/postedfiles/Panose.pdf
|
||||
panose = list(self.sourceFont.os2_panose)
|
||||
if panose[0] == 0 or panose[0] == 2: # 0 (1st value) = family kind; 0 = any (default); 2 = latin text and display
|
||||
panose[0] = 2 # Assert kind
|
||||
panose[3] = 9 # 3 (4th value) = propotion; 9 = monospaced
|
||||
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:
|
||||
|
@ -316,6 +343,7 @@ class font_patcher:
|
|||
sys.exit("{}: Can not open symbol source for '{}'\n{:>{}} (i.e. {})".format(
|
||||
projectName, patch['Name'], '', len(projectName), self.args.glyphdir + patch['Filename']))
|
||||
symfont = fontforge.open(os.path.join(self.args.glyphdir, patch['Filename']))
|
||||
symfont.encoding = 'UnicodeFull'
|
||||
|
||||
# Match the symbol font size to the source font size
|
||||
symfont.em = self.sourceFont.em
|
||||
|
@ -422,8 +450,10 @@ class font_patcher:
|
|||
|
||||
def setup_font_names(self, font):
|
||||
font.fontname = font.persistent["fontname"]
|
||||
font.fullname = font.persistent["fullname"]
|
||||
font.familyname = font.persistent["familyname"]
|
||||
if isinstance(font.persistent["fullname"], str):
|
||||
font.fullname = font.persistent["fullname"]
|
||||
if isinstance(font.persistent["familyname"], str):
|
||||
font.familyname = font.persistent["familyname"]
|
||||
verboseAdditionalFontNameSuffix = " " + projectNameSingular
|
||||
if self.args.windows: # attempt to shorten here on the additional name BEFORE trimming later
|
||||
additionalFontNameSuffix = " " + projectNameAbbreviation
|
||||
|
@ -470,7 +500,7 @@ class font_patcher:
|
|||
verboseAdditionalFontNameSuffix += " Mono"
|
||||
|
||||
if FontnameParserOK and self.args.makegroups:
|
||||
use_fullname = type(font.fullname) == str # Usually the fullname is better to parse
|
||||
use_fullname = isinstance(font.fullname, str) # Usually the fullname is better to parse
|
||||
# Use fullname if it is 'equal' to the fontname
|
||||
if font.fullname:
|
||||
use_fullname |= font.fontname.lower() == FontnameTools.postscript_char_filter(font.fullname).lower()
|
||||
|
@ -686,6 +716,9 @@ class font_patcher:
|
|||
def assert_monospace(self):
|
||||
# Check if the sourcefont is monospaced
|
||||
width_mono, offending_char = is_monospaced(self.sourceFont)
|
||||
self.source_monospaced = width_mono
|
||||
if self.args.nonmono:
|
||||
return
|
||||
panose_mono = check_panose_monospaced(self.sourceFont)
|
||||
# The following is in fact "width_mono != panose_mono", but only if panose_mono is not 'unknown'
|
||||
if (width_mono and panose_mono == 0) or (not width_mono and panose_mono == 1):
|
||||
|
@ -693,19 +726,48 @@ class font_patcher:
|
|||
print(" {} and {}".format(
|
||||
report_advance_widths(self.sourceFont),
|
||||
panose_check_to_text(panose_mono, self.sourceFont.os2_panose)))
|
||||
if not width_mono:
|
||||
if self.args.single and not width_mono:
|
||||
print(" Warning: Sourcefont is not monospaced - forcing to monospace not advisable, results might be useless")
|
||||
if offending_char is not None:
|
||||
print(" Offending char: 0x{:X}".format(offending_char))
|
||||
if self.args.single <= 1:
|
||||
sys.exit(projectName + ": Font will not be patched! Give --mono (or -s, or --use-single-width-glyphs) twice to force patching")
|
||||
if width_mono:
|
||||
force_panose_monospaced(self.sourceFont)
|
||||
|
||||
|
||||
def setup_patch_set(self):
|
||||
""" Creates list of dicts to with instructions on copying glyphs from each symbol font into self.sourceFont """
|
||||
# Supported params: overlap | careful
|
||||
|
||||
box_enabled = self.source_monospaced # Box glyph only for monospaced
|
||||
if box_enabled:
|
||||
self.sourceFont.selection.select(("ranges",), 0x2500, 0x259f)
|
||||
box_glyphs_target = len(list(self.sourceFont.selection))
|
||||
box_glyphs_current = len(list(self.sourceFont.selection.byGlyphs))
|
||||
if box_glyphs_target > box_glyphs_current:
|
||||
# Sourcefont does not have all of these glyphs, do not mix sets
|
||||
if not self.args.quiet and box_glyphs_current > 0:
|
||||
print("INFO: {}/{} box drawing glyphs will be replaced".format(
|
||||
box_glyphs_current, box_glyphs_target))
|
||||
box_keep = False
|
||||
box_enabled = True
|
||||
else:
|
||||
box_keep = True # just scale do not copy
|
||||
box_enabled = False # Cowardly not scaling existing glyphs, although the code would allow this
|
||||
|
||||
# Stretch 'xz' or 'pa' (preserve aspect ratio)
|
||||
# Supported params: overlap | careful | xy-ratio | dont_copy
|
||||
# Overlap value is used horizontally but vertically limited to 0.01
|
||||
# Powerline dividers
|
||||
# Careful does not overwrite/modify existing glyphs
|
||||
# The xy-ratio limits the x-scale for a given y-scale to make the ratio <= this value (to prevent over-wide glyphs)
|
||||
# '1' means occupu 1 cell (default for 'xy')
|
||||
# '2' means occupy 2 cells (default for 'pa')
|
||||
# '!' means do the 'pa' scaling even with non mono fonts (else it just scales down, never up)
|
||||
# Dont_copy does not overwrite existing glyphs but rescales the preexisting ones
|
||||
|
||||
SYM_ATTR_DEFAULT = {
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}}
|
||||
}
|
||||
SYM_ATTR_POWERLINE = {
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||
|
||||
|
@ -722,16 +784,16 @@ class font_patcher:
|
|||
0xe0b7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'xy-ratio': 0.5}},
|
||||
|
||||
# Bottom Triangles
|
||||
0xe0b8: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||
0xe0b9: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||
0xe0ba: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||
0xe0bb: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||
0xe0b8: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||
0xe0b9: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0ba: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||
0xe0bb: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
|
||||
# Top Triangles
|
||||
0xe0bc: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||
0xe0bd: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||
0xe0be: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02}},
|
||||
0xe0bf: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||
0xe0bc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||
0xe0bd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0be: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||
0xe0bf: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
|
||||
# Flames
|
||||
0xe0c0: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||
|
@ -740,36 +802,34 @@ class font_patcher:
|
|||
0xe0c3: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {}},
|
||||
|
||||
# Small squares
|
||||
0xe0c4: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0c5: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0c4: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': -0.03, 'xy-ratio': 0.86}},
|
||||
0xe0c5: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': -0.03, 'xy-ratio': 0.86}},
|
||||
|
||||
# Bigger squares
|
||||
0xe0c6: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0c7: {'align': 'r', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0c6: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': -0.03, 'xy-ratio': 0.78}},
|
||||
0xe0c7: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': -0.03, 'xy-ratio': 0.78}},
|
||||
|
||||
# Waveform
|
||||
0xe0c8: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||
0xe0ca: {'align': 'r', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.01}},
|
||||
|
||||
# Hexagons
|
||||
0xe0cc: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||
0xe0cd: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0cc: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'overlap': 0.02, 'xy-ratio': 0.85}},
|
||||
0xe0cd: {'align': 'l', 'valign': 'c', 'stretch': 'xy2', 'params': {'xy-ratio': 0.865}},
|
||||
|
||||
# Legos
|
||||
0xe0ce: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0cf: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {}},
|
||||
0xe0d1: {'align': 'l', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02}},
|
||||
0xe0ce: {'align': 'l', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||
0xe0cf: {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||
0xe0d0: {'align': 'l', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||
0xe0d1: {'align': 'l', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||
|
||||
# Top and bottom trapezoid
|
||||
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, 'xy-ratio': 0.7}}
|
||||
}
|
||||
|
||||
SYM_ATTR_DEFAULT = {
|
||||
# 'pa' == preserve aspect ratio
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}}
|
||||
SYM_ATTR_TRIGRAPH = {
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa1!', 'params': {'overlap': -0.10, 'careful': True}}
|
||||
}
|
||||
|
||||
SYM_ATTR_FONTA = {
|
||||
# 'pa' == preserve aspect ratio
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {}},
|
||||
|
@ -779,7 +839,16 @@ class font_patcher:
|
|||
0xf0dd: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}},
|
||||
0xf0de: {'align': 'c', 'valign': '', 'stretch': 'pa', 'params': {}}
|
||||
}
|
||||
|
||||
SYM_ATTR_HEAVYBRACKETS = {
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'pa', 'params': {'careful': True}}
|
||||
}
|
||||
SYM_ATTR_BOX = {
|
||||
'default': {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'overlap': 0.02, 'dont_copy': box_keep}},
|
||||
# No overlap with checkered greys (commented out because that raises problems on rescaling clients)
|
||||
# 0x2591: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'dont_copy': box_keep}},
|
||||
# 0x2592: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'dont_copy': box_keep}},
|
||||
# 0x2593: {'align': 'c', 'valign': 'c', 'stretch': 'xy', 'params': {'dont_copy': box_keep}},
|
||||
}
|
||||
CUSTOM_ATTR = {
|
||||
# 'pa' == preserve aspect ratio
|
||||
'default': {'align': 'c', 'valign': '', 'stretch': '', 'params': {}}
|
||||
|
@ -828,10 +897,24 @@ class font_patcher:
|
|||
# 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
|
||||
BOX_SCALE_LIST = {'ScaleGroups': [
|
||||
[*range(0x2500, 0x2570 + 1), *range(0x2574, 0x257f + 1)], # box drawing
|
||||
range(0x2571, 0x2573 + 1), # diagonals
|
||||
[*range(0x2580, 0x2590 + 1), 0x2594, 0x2595], # blocks
|
||||
range(0x2591, 0x2593 + 1), # greys
|
||||
range(0x2594, 0x259f + 1), # quards (Note: quard 2597 in Hack is wrong, scales like block!)
|
||||
]}
|
||||
CODI_SCALE_LIST = {'ScaleGroups': [
|
||||
range(0xea99, 0xeaa1 + 1), # arrows
|
||||
range(0xeb6e, 0xeb71 + 1), # triangles
|
||||
range(0xeab4, 0xeab7 + 1), # chevrons
|
||||
[0xea71, *range(0xeaa6, 0xeaab + 1), 0xeabc, 0xeb18, 0xeb87, 0xeb88, 0xeb8a, 0xeb8c, 0xebb4], # cicles
|
||||
[0xeacc, 0xeaba], # dash
|
||||
]}
|
||||
DEVI_SCALE_LIST = {'ScaleGlyph': 0xE60E, # Android logo
|
||||
'GlyphsToScale': [
|
||||
(0xe6bd, 0xe6c3) # very small things
|
||||
]}
|
||||
]}
|
||||
FONTA_SCALE_LIST = {'ScaleGroups': [
|
||||
[0xf005, 0xf006, 0xf089], # star, star empty, half star
|
||||
range(0xf026, 0xf028 + 1), # volume off, down, up
|
||||
|
@ -853,7 +936,7 @@ class font_patcher:
|
|||
range(0xf221, 0xf22d + 1), # gender or so
|
||||
range(0xf255, 0xf25b + 1), # hand symbols
|
||||
]}
|
||||
OCTI_SCALE_LIST = {'ScaleGlyph': 0xF02E, # looking glass (probably biggest glyph?)
|
||||
OCTI_SCALE_LIST = {'ScaleGlyph': 0xF02E, # looking glass (probably biggest glyph?)
|
||||
'GlyphsToScale': [
|
||||
(0xf03d, 0xf040), # arrows
|
||||
0xf044, 0xf05a, 0xf05b, 0xf0aa, # triangles
|
||||
|
@ -861,23 +944,34 @@ class font_patcher:
|
|||
0xf071, 0xf09f, 0xf0a0, 0xf0a1, # small arrows
|
||||
0xf078, 0xf0a2, 0xf0a3, 0xf0a4, # chevrons
|
||||
0xf0ca, # dash
|
||||
]}
|
||||
]}
|
||||
WEATH_SCALE_LIST = {'ScaleGroups': [
|
||||
[0xf03c, 0xf042, 0xf045 ], # degree signs
|
||||
[0xf043, 0xf044, 0xf048, 0xf04b, 0xf04c, 0xf04d, 0xf057, 0xf058, 0xf087, 0xf088], # arrows
|
||||
range(0xf053, 0xf055 + 1), # thermometers
|
||||
[*range(0xf059, 0xf061 + 1), 0xf0b1], # wind directions
|
||||
range(0xf089, 0xf094 + 1), # clocks
|
||||
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
|
||||
[0xf06e, 0xf070 ], # solar/lunar eclipse
|
||||
# Note: Codepoints listed before that are also in the following range
|
||||
# will take the scaling of the previous group (the ScaleGroups are
|
||||
# searched through in definition order).
|
||||
# But be careful, the combined bounding box for the following group
|
||||
# _will_ include all glyphs in its definition: Make sure the exempt
|
||||
# glyphs from above are smaller (do not extend) the combined bounding
|
||||
# box of this range:
|
||||
range(0xf000, 0xf0cb + 1), # lots of clouds and other (Please read note above!)
|
||||
]}
|
||||
MDI_SCALE_LIST = None # Maybe later add some selected ScaleGroups
|
||||
|
||||
|
||||
# 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, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': True, 'Name': "Seti-UI + Custom", 'Filename': "original-source.otf", 'Exact': False, 'SymStart': 0xE4FA, 'SymEnd': 0xE5FF, 'SrcStart': 0xE5FA, 'ScaleRules': None, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': True, 'Name': "Heavy Angle Brackets", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0x276C, 'SymEnd': 0x2771, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_HEAVYBRACKETS},
|
||||
{'Enabled': box_enabled, 'Name': "Box Drawing", 'Filename': "extraglyphs.sfd", 'Exact': True, 'SymStart': 0x2500, 'SymEnd': 0x259F, 'SrcStart': None, 'ScaleRules': BOX_SCALE_LIST, 'Attributes': SYM_ATTR_BOX},
|
||||
{'Enabled': True, 'Name': "Devicons", 'Filename': "devicons.ttf", 'Exact': False, 'SymStart': 0xE600, 'SymEnd': 0xE6C5, 'SrcStart': 0xE700, 'ScaleRules': DEVI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.powerline, 'Name': "Powerline Symbols", 'Filename': "powerline-symbols/PowerlineSymbols.otf", 'Exact': True, 'SymStart': 0xE0A0, 'SymEnd': 0xE0A2, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_POWERLINE},
|
||||
{'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},
|
||||
|
@ -885,6 +979,7 @@ class font_patcher:
|
|||
{'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.powerlineextra, 'Name': "Powerline Extra Symbols", 'Filename': "PowerlineExtraSymbols.otf", 'Exact': True, 'SymStart': 0x2630, 'SymEnd': 0x2630, 'SrcStart': None, 'ScaleRules': None, 'Attributes': SYM_ATTR_TRIGRAPH},
|
||||
{'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
|
||||
|
@ -898,7 +993,7 @@ class font_patcher:
|
|||
{'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.codicons, 'Name': "Codicons", 'Filename': "codicons/codicon.ttf", 'Exact': True, 'SymStart': 0xEA60, 'SymEnd': 0xEBEB, 'SrcStart': None, 'ScaleRules': CODI_SCALE_LIST, 'Attributes': SYM_ATTR_DEFAULT},
|
||||
{'Enabled': self.args.custom, 'Name': "Custom", 'Filename': self.args.custom, 'Exact': True, 'SymStart': 0x0000, 'SymEnd': 0x0000, 'SrcStart': None, 'ScaleRules': None, 'Attributes': CUSTOM_ATTR}
|
||||
]
|
||||
|
||||
|
@ -928,9 +1023,10 @@ class font_patcher:
|
|||
self.add_glyphrefs_to_essential(altcode)
|
||||
# From fontforge documentation:
|
||||
# glyph.references return a tuple of tuples containing, for each reference in foreground,
|
||||
# a glyph name, a transformation matrix, and whether the reference is currently selected.
|
||||
# a glyph name, a transformation matrix, and (depending on ff version) whether the
|
||||
# reference is currently selected.
|
||||
references = self.sourceFont[unicode].references
|
||||
for refcode in [ self.sourceFont[n].unicode for n, m, s in references ]:
|
||||
for refcode in [ self.sourceFont[n].unicode for n, *_ in references ]: # tuple of 2 or 3 depending on ff version
|
||||
if refcode not in self.essential and refcode >= 0:
|
||||
self.add_glyphrefs_to_essential(refcode)
|
||||
|
||||
|
@ -961,39 +1057,37 @@ class font_patcher:
|
|||
# and we try to sort this out here
|
||||
# See also https://glyphsapp.com/learn/vertical-metrics
|
||||
# See also https://github.com/source-foundry/font-line
|
||||
hhea_height = self.sourceFont.hhea_ascent - self.sourceFont.hhea_descent
|
||||
typo_height = self.sourceFont.os2_typoascent - self.sourceFont.os2_typodescent
|
||||
win_height = self.sourceFont.os2_winascent + self.sourceFont.os2_windescent
|
||||
win_gap = max(0, self.sourceFont.hhea_linegap - win_height + hhea_height)
|
||||
hhea_btb = hhea_height + self.sourceFont.hhea_linegap
|
||||
typo_btb = typo_height + self.sourceFont.os2_typolinegap
|
||||
win_btb = win_height + win_gap
|
||||
(hhea_btb, typo_btb, win_btb, win_gap) = get_btb_metrics(self.sourceFont)
|
||||
use_typo = self.sourceFont.os2_use_typo_metrics != 0
|
||||
|
||||
Metric = Enum('Metric', ['HHEA', 'TYPO', 'WIN'])
|
||||
|
||||
# We use either TYPO (1) or WIN (2) and compare with HHEA
|
||||
# and use HHEA (0) if the fonts seems broken
|
||||
# and use HHEA (0) if the fonts seems broken - no WIN, see #1056
|
||||
our_btb = typo_btb if use_typo else win_btb
|
||||
if our_btb == hhea_btb:
|
||||
metrics = 1 if use_typo else 2 # conforming font
|
||||
metrics = Metric.TYPO if use_typo else Metric.WIN # conforming font
|
||||
else:
|
||||
# We trust the WIN metric more, see experiments in #1056
|
||||
print("{}: WARNING Font vertical metrics inconsistent (HHEA {} / TYPO {} / WIN {}), using WIN".format(projectName, hhea_btb, typo_btb, win_btb))
|
||||
our_btb = win_btb
|
||||
metrics = 1
|
||||
metrics = Metric.WIN
|
||||
|
||||
# print("FINI hhea {} typo {} win {} use {} {} {}".format(hhea_btb, typo_btb, win_btb, use_typo, our_btb != hhea_btb, self.sourceFont.fontname))
|
||||
|
||||
self.font_dim = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0, 'width' : 0, 'height': 0}
|
||||
|
||||
if metrics == 0:
|
||||
self.font_dim['ymin'] = self.sourceFont.hhea_descent + half_gap(self.sourceFont.hhea_linegap, False)
|
||||
if metrics == Metric.HHEA:
|
||||
self.font_dim['ymin'] = self.sourceFont.hhea_descent - half_gap(self.sourceFont.hhea_linegap, False)
|
||||
self.font_dim['ymax'] = self.sourceFont.hhea_ascent + half_gap(self.sourceFont.hhea_linegap, True)
|
||||
elif metrics == 1:
|
||||
self.font_dim['ymin'] = self.sourceFont.os2_typodescent + half_gap(self.sourceFont.os2_typolinegap, False)
|
||||
elif metrics == Metric.TYPO:
|
||||
self.font_dim['ymin'] = self.sourceFont.os2_typodescent - half_gap(self.sourceFont.os2_typolinegap, False)
|
||||
self.font_dim['ymax'] = self.sourceFont.os2_typoascent + half_gap(self.sourceFont.os2_typolinegap, True)
|
||||
else:
|
||||
self.font_dim['ymin'] = -self.sourceFont.os2_windescent + half_gap(win_gap, False)
|
||||
elif metrics == Metric.WIN:
|
||||
self.font_dim['ymin'] = -self.sourceFont.os2_windescent - half_gap(win_gap, False)
|
||||
self.font_dim['ymax'] = self.sourceFont.os2_winascent + half_gap(win_gap, True)
|
||||
else:
|
||||
pass # Will fail the metrics check some line later
|
||||
|
||||
# Calculate font height
|
||||
self.font_dim['height'] = -self.font_dim['ymin'] + self.font_dim['ymax']
|
||||
|
@ -1008,6 +1102,7 @@ class font_patcher:
|
|||
'width' : self.sourceFont.em,
|
||||
'height': self.sourceFont.descent + self.sourceFont.ascent,
|
||||
}
|
||||
our_btb = self.sourceFont.descent + self.sourceFont.ascent
|
||||
elif self.font_dim['height'] < 0:
|
||||
sys.exit("{}: Can not detect sane font height".format(projectName))
|
||||
|
||||
|
@ -1021,11 +1116,15 @@ class font_patcher:
|
|||
self.sourceFont.hhea_descent = self.sourceFont.os2_typodescent
|
||||
self.sourceFont.hhea_linegap = self.sourceFont.os2_typolinegap
|
||||
self.sourceFont.os2_use_typo_metrics = 1
|
||||
(check_hhea_btb, check_typo_btb, check_win_btb, _) = get_btb_metrics(self.sourceFont)
|
||||
if check_hhea_btb != check_typo_btb or check_typo_btb != check_win_btb or check_win_btb != our_btb:
|
||||
sys.exit("{}: Error in baseline to baseline code detected".format(projectName))
|
||||
|
||||
# Step 2
|
||||
# Find the biggest char width and advance width
|
||||
# 0x00-0x17f is the Latin Extended-A range
|
||||
warned = self.args.quiet or self.args.nonmono # Do not warn if quiet or proportional target
|
||||
warned1 = self.args.quiet or self.args.nonmono # Do not warn if quiet or proportional target
|
||||
warned2 = warned1
|
||||
for glyph in range(0x21, 0x17f):
|
||||
if glyph in range(0x7F, 0xBF) or glyph in [
|
||||
0x132, 0x133, # IJ, ij (in Overpass Mono)
|
||||
|
@ -1041,30 +1140,39 @@ class font_patcher:
|
|||
# print("WIDTH {:X} {} ({} {})".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||
if self.font_dim['width'] < self.sourceFont[glyph].width:
|
||||
self.font_dim['width'] = self.sourceFont[glyph].width
|
||||
if not warned and glyph > 0x7a: # NOT 'basic' glyph, which includes a-zA-Z
|
||||
if not warned1 and glyph > 0x7a: # NOT 'basic' glyph, which includes a-zA-Z
|
||||
print("Warning: Extended glyphs wider than basic glyphs, results might be useless\n {}".format(
|
||||
report_advance_widths(self.sourceFont)))
|
||||
warned = True
|
||||
warned1 = True
|
||||
# print("New MAXWIDTH-A {:X} {} -> {} {}".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||
if xmax > self.font_dim['xmax']:
|
||||
self.font_dim['xmax'] = xmax
|
||||
if not warned2 and glyph > 0x7a: # NOT 'basic' glyph, which includes a-zA-Z
|
||||
print("Info: Extended glyphs wider bounding box than basic glyphs")
|
||||
warned2 = True
|
||||
# print("New MAXWIDTH-B {:X} {} -> {} {}".format(glyph, self.sourceFont[glyph].width, self.font_dim['width'], xmax))
|
||||
if self.font_dim['width'] < self.font_dim['xmax']:
|
||||
if not self.args.quiet:
|
||||
print("Warning: Font has negative right side bearing in extended glyphs")
|
||||
self.font_dim['xmax'] = self.font_dim['width'] # In fact 'xmax' is never used
|
||||
# print("FINAL", self.font_dim)
|
||||
|
||||
|
||||
def get_target_width(self, stretch):
|
||||
""" Get the target width (1 or 2 'cell') for a given stretch parameter """
|
||||
# For monospaced fonts all chars need to be maximum 'one' space wide
|
||||
# other fonts allows double width glyphs for 'pa' or if requested with '2'
|
||||
if self.args.single or ('pa' not in stretch and '2' not in stretch) or '1' in stretch:
|
||||
return 1
|
||||
return 2
|
||||
|
||||
def get_scale_factors(self, sym_dim, stretch):
|
||||
""" 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)
|
||||
|
||||
# For monospaced fonts all chars need to be maximum 'one' space wide
|
||||
# other fonts allows double width glyphs for 'pa' or if requested with '2'
|
||||
if self.args.single or (stretch != 'pa' and '2' not in stretch):
|
||||
relative_width = 1.0
|
||||
else:
|
||||
relative_width = 2.0
|
||||
target_width = self.font_dim['width'] * relative_width
|
||||
target_width = self.font_dim['width'] * self.get_target_width(stretch)
|
||||
scale_ratio_x = target_width / sym_dim['width']
|
||||
|
||||
# font_dim['height'] represents total line height, keep our symbols sized based upon font's em
|
||||
|
@ -1072,10 +1180,10 @@ class font_patcher:
|
|||
target_height = self.font_dim['height']
|
||||
scale_ratio_y = target_height / sym_dim['height']
|
||||
|
||||
if stretch == 'pa':
|
||||
if 'pa' in stretch:
|
||||
# We want to preserve x/y aspect ratio, so find biggest scale factor that allows symbol to fit
|
||||
scale_ratio_x = min(scale_ratio_x, scale_ratio_y)
|
||||
if not self.args.single:
|
||||
if not self.args.single and not '!' in stretch:
|
||||
# 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
|
||||
|
@ -1093,7 +1201,6 @@ class font_patcher:
|
|||
""" Copies symbol glyphs into self.sourceFont """
|
||||
progressText = ''
|
||||
careful = False
|
||||
glyphSetLength = 0
|
||||
sourceFontCounter = 0
|
||||
|
||||
if self.args.careful:
|
||||
|
@ -1114,14 +1221,14 @@ class font_patcher:
|
|||
glyphSetLength = len(symbolFontSelection)
|
||||
|
||||
if not self.args.quiet:
|
||||
sys.stdout.write("Adding " + str(max(1, glyphSetLength)) + " Glyphs from " + setName + " Set \n")
|
||||
modify = attributes['default']['params'].get('dont_copy')
|
||||
sys.stdout.write("{} {} Glyphs from {} Set\n".format(
|
||||
"Adding" if not modify else "Rescaling", glyphSetLength, setName))
|
||||
|
||||
currentSourceFontGlyph = -1 # initialize for the exactEncoding case
|
||||
width_warning = False
|
||||
|
||||
for index, sym_glyph in enumerate(symbolFontSelection):
|
||||
index = max(1, index)
|
||||
|
||||
sym_attr = attributes.get(sym_glyph.unicode)
|
||||
if sym_attr is None:
|
||||
sym_attr = attributes['default']
|
||||
|
@ -1176,29 +1283,37 @@ class font_patcher:
|
|||
if currentSourceFontGlyph in self.sourceFont:
|
||||
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
|
||||
dont_copy = sym_attr['params'].get('dont_copy')
|
||||
|
||||
# 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
|
||||
symbolFont.selection.select(sym_glyph.encoding)
|
||||
symbolFont.copy()
|
||||
if dont_copy:
|
||||
# Just prepare scaling of existing glyphs
|
||||
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, self.sourceFont, currentSourceFontGlyph) if scaleRules is not None else None
|
||||
else:
|
||||
# This will destroy any content currently in currentSourceFontGlyph, so do it first
|
||||
glyph_scale_data = self.get_glyph_scale(sym_glyph.encoding, scaleRules, symbolFont, currentSourceFontGlyph) if scaleRules is not None else None
|
||||
|
||||
# Paste it
|
||||
self.sourceFont.selection.select(currentSourceFontGlyph)
|
||||
self.sourceFont.paste()
|
||||
self.sourceFont[currentSourceFontGlyph].glyphname = sym_glyph.glyphname
|
||||
self.sourceFont[currentSourceFontGlyph].manualHints = True # No autohints for symbols
|
||||
# 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
|
||||
symbolFont.selection.select(sym_glyph.encoding)
|
||||
symbolFont.copy()
|
||||
|
||||
# Paste it
|
||||
self.sourceFont.selection.select(currentSourceFontGlyph)
|
||||
self.sourceFont.paste()
|
||||
self.sourceFont[currentSourceFontGlyph].glyphname = sym_glyph.glyphname
|
||||
self.sourceFont[currentSourceFontGlyph].manualHints = True # No autohints for symbols
|
||||
|
||||
# Prepare symbol glyph dimensions
|
||||
sym_dim = get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph])
|
||||
if glyph_scale_data is not None:
|
||||
if glyph_scale_data[1] is not None:
|
||||
sym_dim = glyph_scale_data[1] # Use combined bounding box
|
||||
# This is roughly alike get_scale_factors(glyph_scale_data[1], 'pa')
|
||||
# Except we do not have glyph_scale_data[1] always...
|
||||
(scale_ratio_x, scale_ratio_y) = (glyph_scale_data[0], glyph_scale_data[0])
|
||||
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, sym_attr['stretch'])
|
||||
else:
|
||||
# This is roughly alike get_scale_factors(glyph_scale_data[1], 'pa')
|
||||
# Except we do not have glyph_scale_data[1] always...
|
||||
(scale_ratio_x, scale_ratio_y) = (glyph_scale_data[0], glyph_scale_data[0])
|
||||
else:
|
||||
(scale_ratio_x, scale_ratio_y) = self.get_scale_factors(sym_dim, sym_attr['stretch'])
|
||||
|
||||
|
@ -1252,17 +1367,24 @@ class font_patcher:
|
|||
x_align_distance += (self.font_dim['width'] / 2) - (sym_dim['width'] / 2)
|
||||
elif sym_attr['align'] == 'r':
|
||||
# Right align
|
||||
x_align_distance += self.font_dim['width'] - sym_dim['width']
|
||||
if not self.args.single and '2' in sym_attr['stretch']:
|
||||
x_align_distance += self.font_dim['width']
|
||||
x_align_distance += self.font_dim['width'] * self.get_target_width(sym_attr['stretch']) - sym_dim['width']
|
||||
# If symbol glyph is wider than target font cell, just left-align
|
||||
x_align_distance = max(self.font_dim['xmin'] - sym_dim['xmin'], x_align_distance)
|
||||
|
||||
if overlap:
|
||||
overlap_width = self.font_dim['width'] * overlap
|
||||
if sym_attr['align'] == 'l':
|
||||
x_align_distance -= overlap_width
|
||||
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
|
||||
elif sym_attr['align'] == 'c':
|
||||
if overlap_width > 0:
|
||||
x_align_distance -= overlap_width / 2
|
||||
elif sym_attr['align'] == 'r':
|
||||
# Check and correct overlap; it can go wrong if we have a xy-ratio limit
|
||||
target_xmax = (self.font_dim['xmin'] + self.font_dim['width']) * self.get_target_width(sym_attr['stretch'])
|
||||
target_xmax += overlap_width
|
||||
glyph_xmax = sym_dim['xmax'] + x_align_distance
|
||||
correction = target_xmax - glyph_xmax
|
||||
x_align_distance += correction
|
||||
|
||||
align_matrix = psMat.translate(x_align_distance, y_align_distance)
|
||||
self.sourceFont[currentSourceFontGlyph].transform(align_matrix)
|
||||
|
@ -1485,7 +1607,7 @@ def get_multiglyph_boundingBox(glyphs, destGlyph = None):
|
|||
if glyph is None:
|
||||
# Glyph has been in defining range but is not in the actual font
|
||||
continue
|
||||
if destGlyph:
|
||||
if destGlyph and glyph.font != destGlyph.font:
|
||||
glyph.font.selection.select(glyph)
|
||||
glyph.font.copy()
|
||||
destGlyph.font.selection.select(destGlyph)
|
||||
|
@ -1571,6 +1693,34 @@ def check_fontforge_min_version():
|
|||
sys.stderr.write("{}: Please use at least version: {}\n".format(projectName, minimumVersion))
|
||||
sys.exit(1)
|
||||
|
||||
def check_version_with_git(version):
|
||||
""" Upgraded the version to the current git tag version (starting with 'v') """
|
||||
git = subprocess.run("git describe --tags",
|
||||
cwd=os.path.dirname(__file__),
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
|
||||
).stdout.decode('utf-8')
|
||||
if len(git) == 0:
|
||||
return False
|
||||
tag = git.strip()
|
||||
if len(tag) == 0 or not tag.startswith('v'):
|
||||
return False
|
||||
tag = tag[1:]
|
||||
r = re.search('(.*?)(-[0-9]+)-g[0-9a-fA-F]+$', tag)
|
||||
if r:
|
||||
tag = r.group(1)
|
||||
patchlevel = r.group(2)
|
||||
else:
|
||||
patchlevel = ""
|
||||
# Inspired by Phaxmohdem's versiontuple https://stackoverflow.com/a/28568003
|
||||
|
||||
versiontuple = lambda v: tuple( p.zfill(8) for p in v.split(".") )
|
||||
if versiontuple(tag) > versiontuple(version):
|
||||
return tag + patchlevel
|
||||
if versiontuple(tag) == versiontuple(version) and len(patchlevel) > 0:
|
||||
return tag + patchlevel
|
||||
return False
|
||||
|
||||
def setup_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
|
@ -1699,7 +1849,12 @@ def setup_arguments():
|
|||
|
||||
|
||||
def main():
|
||||
print("{} Patcher v{} ({}) executing".format(projectName, version, script_version))
|
||||
global version
|
||||
git_version = check_version_with_git(version)
|
||||
print("{} Patcher v{} ({}) (ff {}) executing".format(
|
||||
projectName, git_version if git_version else version, script_version, fontforge.version()))
|
||||
if git_version:
|
||||
version = git_version
|
||||
check_fontforge_min_version()
|
||||
args = setup_arguments()
|
||||
patcher = font_patcher(args)
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
EXTS = ["otf", "ttf", "woff", "woff2"]
|
||||
|
||||
|
||||
def find_files(search_path, exts=None):
|
||||
return (
|
||||
[f for ext in exts for f in search_path.glob(f"**/*.{ext}")]
|
||||
if exts
|
||||
else [f for f in search_path.rglob("*") if f.is_file()]
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
print("please specify directory to search")
|
||||
exit(1)
|
||||
|
||||
search_path = Path(sys.argv[1])
|
||||
exts = sys.argv[2].split(",") if len(sys.argv) == 3 else EXTS
|
||||
|
||||
for f in find_files(search_path, exts):
|
||||
sys.stdout.write(f"-f '{f}' ")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -10,7 +10,7 @@ import time
|
|||
from pathlib import Path
|
||||
from typing import List, Set
|
||||
|
||||
FONT_SRC = (Path(__file__).parent.parent / "MonoLisa").absolute()
|
||||
FONT_SRC = (Path(__file__).parent / "MonoLisa").absolute()
|
||||
|
||||
|
||||
class Color:
|
||||
|
@ -159,7 +159,6 @@ def patch_single_font(
|
|||
f"{color.yellow}:::{color.end} Patching font "
|
||||
f"{color.bold}{font_path.name}{color.end}... "
|
||||
):
|
||||
|
||||
run_cmd(cmd, font_path, verbose)
|
||||
|
||||
echo(f"{rel_path} patched!", hue="green")
|
||||
|
@ -205,6 +204,17 @@ def patch_font_dir_docker(
|
|||
echo(f"{font_dir_path.name}/ fonts patched!", hue="green")
|
||||
|
||||
|
||||
def get_font_files(p):
|
||||
exts = ["otf", "ttf", "woff", "woff2"]
|
||||
|
||||
if p.is_file():
|
||||
return (p,)
|
||||
|
||||
if p.is_dir():
|
||||
return (f for ext in exts for f in p.glob(f"**/*.{ext}"))
|
||||
return ()
|
||||
|
||||
|
||||
def echo(msg: str, header=False, hue="cyan") -> None:
|
||||
if header:
|
||||
print(f"==>{color.magenta} {msg} {color.end}<==")
|
||||
|
@ -213,7 +223,6 @@ def echo(msg: str, header=False, hue="cyan") -> None:
|
|||
|
||||
|
||||
def main():
|
||||
|
||||
echo("MonoLisa NerdFont Patcher", header=True)
|
||||
args, fp_args = get_args()
|
||||
fp_args = " ".join(fp_args)
|
||||
|
@ -223,12 +232,15 @@ def main():
|
|||
echo("Patching the following files")
|
||||
for fontfile in args.font_path:
|
||||
sys.stdout.write(f" {color.magenta}->{color.end} {fontfile}\n")
|
||||
|
||||
fontfiles = [f for p in args.font_path for f in get_font_files(p)]
|
||||
|
||||
if args.docker:
|
||||
echo("==> DOCKER MODE ENABLED")
|
||||
for font_dir in collect_files_by_dir(args.font_path):
|
||||
for font_dir in collect_files_by_dir(fontfiles):
|
||||
patch_font_dir_docker(font_dir, args.output, fp_args, args.verbose)
|
||||
else:
|
||||
for fontfile in args.font_path:
|
||||
for fontfile in fontfiles:
|
||||
patch_single_font(Path(fontfile), args.output, fp_args, args.verbose)
|
||||
|
||||
echo("fonts are patched", hue="green")
|
||||
|
@ -236,6 +248,5 @@ def main():
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
color = Color()
|
||||
main()
|
Binary file not shown.
BIN
src/glyphs/extraglyphs.sfd
Normal file
BIN
src/glyphs/extraglyphs.sfd
Normal file
Binary file not shown.
Loading…
Reference in a new issue