font-patcher: Allow to rehint some Cascadia glyphs

[why]
Some Caskaydia Cove glyphs that are used in ligatures (to create endless
arrows for example) show uneven line thickness on some platforms.

The reason is the not-matching hinting of glyphs that are places next
to each other and so minuscule differences are quite visible.

Note that the 'original' hinting is generated by VTT on the static
Cascadia Code instances, which upstream just have ttfautohint hints that
are different from the hints in the variable fonts and people complained
that the look is different.

[how]
Add a new field to the config.cfg file that holds regexes of glyph
names for glyphs that should be re-hinted by fontforge on patching time.

In principle we could also implement that as an additional pre-step that
needs to be manually done after running VTT on the static font files.

I'm not sure which is better.

Note that fontforge generates a lot of debug output because the hinting
is not ideal - the global tables are kept and probably less compatible
to fontforge's own hinting... But the results are good.

Fixes: #1291
Fixes: #1609

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
This commit is contained in:
Fini Jastrow 2024-04-18 16:46:14 +02:00
parent 88d63ff827
commit 378d97cf1b
3 changed files with 43 additions and 5 deletions

View file

@ -6,7 +6,7 @@
from __future__ import absolute_import, print_function, unicode_literals
# Change the script version when you edit this script:
script_version = "4.13.1"
script_version = "4.14.0"
version = "3.2.1"
projectName = "Nerd Fonts"
@ -339,7 +339,9 @@ class font_patcher:
self.sourceFont = font
self.setup_version()
self.assert_monospace()
self.load_config()
self.remove_ligatures()
self.manipulate_hints()
self.get_essential_references()
self.get_sourcefont_dimensions()
self.setup_patch_set()
@ -769,11 +771,18 @@ class font_patcher:
# print("Version now is {}".format(sourceFont.version))
def load_config(self):
""" Try to read the config file (if specified) """
if self.args.configfile:
if not self.config.read(self.args.configfile):
logger.error("Unable to read configfile")
def remove_ligatures(self):
# let's deal with ligatures (mostly for monospaced fonts)
# Usually removes 'fi' ligs that end up being only one cell wide, and 'ldot'
if self.args.configfile and self.config.read(self.args.configfile):
if self.args.removeligatures:
if self.args.removeligatures:
if self.args.configfile:
logger.info("Removing ligatures from configfile `Subtables` section")
ligature_subtables = json.loads(self.config.get("Subtables", "ligatures"))
for subtable in ligature_subtables:
@ -783,10 +792,29 @@ class font_patcher:
logger.debug("Successfully removed subtable: %s", subtable)
except Exception:
logger.error("Failed to remove subtable: %s", subtable)
elif self.args.removeligatures:
logger.error("Unable to read configfile, unable to remove ligatures")
else:
logger.error("No configfile, unable to remove ligatures")
def manipulate_hints(self):
""" Redo the hinting on some problematic glyphs """
if not self.args.configfile:
return
redo = json.loads(self.config.get("Hinting", "re_hint"))
if not len(redo):
return
logger.debug("Working on {} rehinting rules".format(len(redo)))
count = 0
for gname in self.sourceFont:
for regex in redo:
if re.fullmatch(regex, gname):
glyph = self.sourceFont[gname]
glyph.autoHint()
glyph.autoInstr()
count += 1
break
logger.info("Rehinted {} glyphs".format(count))
def assert_monospace(self):
# Check if the sourcefont is monospaced
width_mono, offending_char = is_monospaced(self.sourceFont)

View file

@ -1,2 +1,7 @@
[Config]
commandline: --makegroups 4
[Hinting]
re_hint: [
".*hyphen.*\\.(liga|seq)",
".*equal.*\\.(liga|seq)",
".*numbersign.*\\.(liga|seq)" ]

View file

@ -1,2 +1,7 @@
[Config]
commandline: --makegroups 4
[Hinting]
re_hint: [
".*hyphen.*\\.(liga|seq)",
".*equal.*\\.(liga|seq)",
".*numbersign.*\\.(liga|seq)" ]