From 76cb86f66ff34a8110f1f9a2dc5f3633e14f8876 Mon Sep 17 00:00:00 2001 From: Fini Jastrow Date: Sat, 10 Sep 2022 09:35:03 +0200 Subject: [PATCH] Add query_mono debugging script Signed-off-by: Fini Jastrow --- bin/scripts/name_parser/README.md | 1 + bin/scripts/name_parser/query_mono | 105 +++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100755 bin/scripts/name_parser/query_mono diff --git a/bin/scripts/name_parser/README.md b/bin/scripts/name_parser/README.md index fed910789..fa981ed67 100644 --- a/bin/scripts/name_parser/README.md +++ b/bin/scripts/name_parser/README.md @@ -159,6 +159,7 @@ explanations that might do not need much attention. There are some helper scripts that help examining the font files. Of course there are other, more professional tools to dump font information, but here we get all we need in a concise way: +* `query_mono` `font_name [font_name ...]` * `query_names` `font_name [font_name ...]` * `query_panose` `font_name` * `query_sftn` `[] font_name` diff --git a/bin/scripts/name_parser/query_mono b/bin/scripts/name_parser/query_mono new file mode 100755 index 000000000..70fba3f5c --- /dev/null +++ b/bin/scripts/name_parser/query_mono @@ -0,0 +1,105 @@ +#!/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() +