Skip to content

Commit

Permalink
font-patcher: Prevent --mono on proportional fonts
Browse files Browse the repository at this point in the history
[why]
When the source font is proportional we can not really create a
monospaced (patched) font from it. The glyph width is for example very
small for 'i' but wide for 'W'.
The glyphs are all left aligned, leaving very strange separation between
smallish glyphs.
Even if we would center the glyphs, the look would be strange and
completely differenmt from the source font's look.

[how]
For proportional fonts do not allow to patch with `--mono`.

The fact if a source font is monospaced is determined by examining some
(very few) glyphs. But testing all our source fonts in the repo shows
that it is sufficient.
Furthermore the Panose flag is checked and differences between the flag
and what the glyph examination found are reported.

The user can enforce `Nerd Font Mono` generation with double specifying
the command line option `--mono --mono`. Still a warning will be issued.

[note]
Because `gotta-patch-em-all-font-patcher!.sh` does not really count the
variations but calculates them in a separate loop it does not know
anymore how many variations are created per family. The numbers are
wrong.
But probably we should count the result font files in the end anyhow.

Because the information is not needed (in an automated manner) this is
not corrected here.

It seems wrong anyhow:
  total_variation_count=$((total_variation_count+combination_count))
  total_count=$((total_count+complete_variations_per_family+combination_count))

Signed-off-by: Fini Jastrow <[email protected]>
  • Loading branch information
Finii committed Sep 4, 2022
1 parent cb6ff0f commit 6b25d4b
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 4 deletions.
4 changes: 2 additions & 2 deletions bin/scripts/gotta-patch-em-all-font-patcher!.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ function patch_font {
}
# Use absolute path to allow fontforge being an AppImage (used in CI)
PWD=`pwd`
fontforge -quiet -script $PWD/font-patcher "$f" -q --also-windows $powerline $post_process --complete --no-progressbars --outputdir "${patched_font_dir}complete/" $config_patch_flags 2>/dev/null
fontforge -quiet -script $PWD/font-patcher "$f" -q -s ${font_config} --also-windows $powerline $post_process --complete --no-progressbars --outputdir "${patched_font_dir}complete/" $config_patch_flags 2>/dev/null
fontforge -quiet -script $PWD/font-patcher "$f" -q --also-windows $powerline $post_process --complete --no-progressbars --outputdir "${patched_font_dir}complete/" $config_patch_flags 2>/dev/null || echo "Patcher run aborted!"
fontforge -quiet -script $PWD/font-patcher "$f" -q -s ${font_config} --also-windows $powerline $post_process --complete --no-progressbars --outputdir "${patched_font_dir}complete/" $config_patch_flags 2>/dev/null || echo "Patcher run aborted!"
# wait for this group of background processes to finish to avoid forking too many processes
# that can add up quickly with the number of combinations
#wait
Expand Down
77 changes: 75 additions & 2 deletions font-patcher
Original file line number Diff line number Diff line change
Expand Up @@ -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.0.3"
script_version = "3.0.4"

version = "2.2.1"
projectName = "Nerd Fonts"
Expand Down Expand Up @@ -155,6 +155,65 @@ class TableHEADWriter:
self.lowppem = self.getshort('lowestRecPPEM')
self.checksum_adj = self.getlong('checksumAdjustment')

def check_panose_monospaced(font):
""" Check if the font's Panose flags say it is monospaced """
# https://forum.high-logic.com/postedfiles/Panose.pdf
panose = list(font.os2_panose)
if panose[0] < 2 or panose[0] > 5:
return -1 # invalid Panose info
panose_mono = ((panose[0] == 2 and panose[3] == 9) or
(panose[0] == 3 and panose[3] == 3))
return 1 if panose_mono else 0

def is_monospaced(font):
""" Check if a font is probably monospaced """
# Some fonts lie (or have not any Panose flag set), spot check monospaced:
width = -1
width_mono = True
for glyph in [ 0x49, 0x4D, 0x57, 0x61, 0x69, 0x2E ]: # wide and slim glyphs 'I', 'M', 'W', 'a', 'i', '.'
if not glyph in font:
# A 'strange' font, believe Panose
return check_panose_monospaced(font) == 1
# print(" -> {} {}".format(glyph, font[glyph].width))
if width < 0:
width = font[glyph].width
continue
if font[glyph].width != width:
# Exception for fonts like Code New Roman Regular or Hermit Light/Bold:
# Allow small 'i' and dot to be smaller than normal
# I believe the source fonts are buggy
if glyph in [ 0x69, 0x2E ]:
if width > font[glyph].width:
continue
(xmin, _, xmax, _) = font[glyph].boundingBox()
if width > xmax - xmin:
continue
width_mono = False
break
# We believe our own check more then Panose ;-D
return width_mono

def get_advance_width(font, extended, minimum):
""" Get the maximum/minimum advance width in the extended(?) range """
width = 0
if extended:
end = 0x17f
else:
end = 0x07e
for glyph in range(0x21, end):
if not glyph in font:
continue
if glyph in range(0x7F, 0xBF):
continue # ignore special characters like '1/4' etc
if width == 0:
width = font[glyph].width
continue
if not minimum and width < font[glyph].width:
width = font[glyph].width
elif minimum and width > font[glyph].width:
width = font[glyph].width
return width


class font_patcher:
def __init__(self):
Expand Down Expand Up @@ -203,6 +262,18 @@ class font_patcher:
print("{} Patcher v{} ({}) executing\n".format(projectName, version, script_version))

if self.args.single:
# Check if the sourcefont is monospaced
width_mono = is_monospaced(self.sourceFont)
panose_mono = check_panose_monospaced(self.sourceFont)
if (width_mono and panose_mono == 0) or (not width_mono and panose_mono == 1):
print(' Warning: Monospaced check: Panose assumed to be wrong')
print(' Glyph widths {} / {} - {} and Panose says "monospace {}" ({})'.format(get_advance_width(self.sourceFont, False, True),
get_advance_width(self.sourceFont, False, False), get_advance_width(self.sourceFont, True, False), panose_mono, list(self.sourceFont.os2_panose)))
if not width_mono:
print(' Warning: Sourcefont is not monospaced - forcing to monospace not advisable, results might be useless')
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")

# 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()
Expand Down Expand Up @@ -306,7 +377,7 @@ class font_patcher:
# optional arguments
parser.add_argument('font', help='The path to the font to patch (e.g., Inconsolata.otf)')
parser.add_argument('-v', '--version', action='version', version=projectName + ": %(prog)s (" + version + ")")
parser.add_argument('-s', '--mono', '--use-single-width-glyphs', dest='single', default=False, action='store_true', help='Whether to generate the glyphs as single-width not double-width (default is double-width)')
parser.add_argument('-s', '--mono', '--use-single-width-glyphs', dest='single', default=False, action='count', help='Whether to generate the glyphs as single-width not double-width (default is double-width)')
parser.add_argument('-l', '--adjust-line-height', dest='adjustLineHeight', default=False, action='store_true', help='Whether to adjust line heights (attempt to center powerline separators more evenly)')
parser.add_argument('-q', '--quiet', '--shutup', dest='quiet', default=False, action='store_true', help='Do not generate verbose output')
parser.add_argument('-w', '--windows', dest='windows', default=False, action='store_true', help='Limit the internal font name to 31 characters (for Windows compatibility)')
Expand Down Expand Up @@ -863,8 +934,10 @@ class font_patcher:
continue
if 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 xmax > self.font_dim['xmax']:
self.font_dim['xmax'] = xmax
# print('New MAXWIDTH-B {} {} {}'.format(glyph, self.sourceFont[glyph].width, xmax))

# Calculate font height
self.font_dim['height'] = abs(self.font_dim['ymin']) + self.font_dim['ymax']
Expand Down

0 comments on commit 6b25d4b

Please sign in to comment.