1
0
mirror of https://github.com/ryanoasis/nerd-fonts.git synced 2025-03-03 14:36:18 +02:00

Merge pull request #1099 from ryanoasis/bugfix/panose-on-standard-variant

Bugfix Panose on "Nerd Font" variants
This commit is contained in:
Fini 2023-02-02 11:00:39 +01:00 committed by GitHub
commit 3eed4574bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 115 deletions

View File

@ -1,105 +0,0 @@
#!/usr/bin/env python3
# coding=utf8
import sys
import os.path
import fontforge
###### Some helpers
def is_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 is_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
###### Let's go!
if len(sys.argv) < 2:
print('Usage: {} font_name [font_name ...]\n'.format(sys.argv[0]))
sys.exit(1)
print('Examining {} font files'.format(len(sys.argv) - 1))
for filename in sys.argv[1:]:
fullfile = os.path.basename(filename)
fname = os.path.splitext(fullfile)[0]
font = fontforge.open(filename, 1)
panose = list(font.os2_panose)
mono = is_panose_monospaced(font)
if mono == -1:
monotxt = 'unknown'
elif mono == 0:
monotxt = 'false'
elif mono == 1:
monotxt = 'true'
else:
monotxt = '???'
width = -1
widthmono = True
widthmono = is_monospaced(font)
if (mono == 0 and widthmono) or (mono == 1 and not widthmono):
print('LIES {} (width {} / {} - {}) Panose says "monospace {}" {}'.format(fname, get_advance_width(font, False, True), get_advance_width(font, False, False), get_advance_width(font, True, False), monotxt, panose))
else:
print('MONO {} (width {} / {} - {}) Panose says "monospace {}" {}'.format(fname, get_advance_width(font, False, True), get_advance_width(font, False, False), get_advance_width(font, True, False), monotxt, panose))
if not widthmono:
print('NOT MONO {} (width {} / {} - {}) Panose says "monospace {}" {}'.format(fname, get_advance_width(font, False, True), get_advance_width(font, False, False), get_advance_width(font, True, False), monotxt, panose))
font.close()

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 = "3.5.2"
script_version = "3.5.3"
version = "2.3.3"
projectName = "Nerd Fonts"
@ -222,6 +222,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_monospace(font):
""" Forces the Panose flag to monospace 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'")
font.os2_panose = tuple(panose)
if panose[0] == 2 and panose[3] != 9:
panose[3] = 9 # 3 (4th value) = propotion; 9 = monospaced
print(" Setting Panose 'Proportion' to 'Monospace'")
font.os2_panose = tuple(panose)
def get_advance_width(font, extended, minimum):
""" Get the maximum/minimum advance width in the extended(?) range """
width = 0
@ -267,7 +281,7 @@ class font_patcher:
self.setup_version()
self.get_essential_references()
self.setup_name_backup(font)
if self.args.single:
if not self.args.nonmono:
self.assert_monospace()
self.remove_ligatures()
self.setup_patch_set()
@ -280,13 +294,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:
@ -693,12 +700,14 @@ 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_monospace(self.sourceFont)
def setup_patch_set(self):