diff --git a/drawbot_proofing/proofing_helpers/names.py b/drawbot_proofing/proofing_helpers/names.py index a705dd3..fb43d11 100644 --- a/drawbot_proofing/proofing_helpers/names.py +++ b/drawbot_proofing/proofing_helpers/names.py @@ -10,6 +10,9 @@ def get_fi_dict(ufo_file): + ''' + return fontinfo.plist as a dictionary + ''' fontinfo_path = ufo_file.joinpath('fontinfo.plist') with open(fontinfo_path, 'rb') as fi_blob: fi_dict = plistlib.load(fi_blob) @@ -25,8 +28,8 @@ def get_ps_name(input_file): fi_dict = get_fi_dict(input_file) ps_name = fi_dict.get('postscriptFontName', None) if not ps_name: - family_name = fi_dict.get('familyName', 'Family Name') - style_name = fi_dict.get('styleName', 'Style Name') + family_name = fi_dict.get('familyName', 'No Family Name') + style_name = fi_dict.get('styleName', 'No Style Name') joined_name = f'{family_name}-{style_name}' ps_name = joined_name.replace(' ', '') diff --git a/drawbot_proofing/verticalMetricsComparisonProof.py b/drawbot_proofing/verticalMetricsComparisonProof.py index cb9651d..b3f6c10 100644 --- a/drawbot_proofing/verticalMetricsComparisonProof.py +++ b/drawbot_proofing/verticalMetricsComparisonProof.py @@ -20,14 +20,16 @@ import defcon import drawBot as db -from .verticalMetricsProof import MARGIN, PT_SIZE, FontInfo, finish_drawing +from .verticalMetricsProof import ( + MARGIN, PT_SIZE, FontInfo, finish_drawing, report_metrics +) from .proofing_helpers import fontSorter from .proofing_helpers.files import get_font_paths, get_ufo_paths from .proofing_helpers.formatter import RawDescriptionAndDefaultsFormatter from .proofing_helpers.drawing import draw_glyph from .proofing_helpers.globals import FONT_MONO -from .proofing_helpers.names import get_style_name +from .proofing_helpers.names import get_style_name, get_ps_name def get_args(): @@ -63,17 +65,38 @@ def get_args(): return parser.parse_args() -def draw_metrics_page_ufo( - character, fo_list, cmap_list, page_width=1000, normalize_upm=False -): +def draw_metrics_page_ufo(character, fo_list, cmap_list, normalize_upm=False): + + upm_list = [f.info.unitsPerEm for f in fo_list] + height_list = [f.info.ascender - f.info.descender for f in fo_list] + descender_list = [f.info.descender for f in fo_list] - db.newPage(page_width, 250) + # get combined width of glyphs + page_width = 0 + for i, fo in enumerate(fo_list): + char_map = cmap_list[i] + char_gname = char_map.get(ord(character), '.notdef') + char_width = fo[char_gname].width + page_width += char_width * PT_SIZE / fo.info.unitsPerEm + page_width += 2 * MARGIN + + heights = [ + height_list[i] * PT_SIZE / upm_list[i] + for i, fo in enumerate(fo_list)] + descenders = [ + descender_list[i] * PT_SIZE / upm_list[i] + for i, fo in enumerate(fo_list)] + + lowest_descender = min(descenders) + page_height = max(heights) + 2 * MARGIN + + db.newPage(page_width, page_height) x_offset = MARGIN for i, font in enumerate(fo_list): upm = font.info.unitsPerEm scale_factor = PT_SIZE / upm - baseline = db.height() / 3 / scale_factor + baseline = (abs(lowest_descender) + MARGIN) / scale_factor line_y = ( font.info.descender if font.info.descender else -250, 0, @@ -115,28 +138,43 @@ def draw_metrics_page_ufo( align='center') db.text( get_style_name(font.path), - (glyph.width / 2, -baseline + 4 / scale_factor), + (glyph.width / 2, -baseline + (MARGIN / 2) / scale_factor), align='center') def draw_metrics_page_font( - character, font_info_list, page_width=1000, normalize_upm=False + character, font_info_list, normalize_upm=False ): - - db.newPage(page_width, 250) + # Calculate width/height of the page. + # Using local UPM values here, supporting different UPMs on the same line + + page_width = 0 + for fi in font_info_list: + char_gname = fi.char_map.get(ord(character), '.notdef') + char_width = fi.advance_widths.get(char_gname) + page_width += char_width * PT_SIZE / fi.upm + page_width += 2 * MARGIN + + heights = [ + (fi.ascender - fi.descender) * PT_SIZE / fi.upm + for fi in font_info_list] + descenders = [ + fi.descender * PT_SIZE / fi.upm for fi in font_info_list] + + lowest_descender = min(descenders) + page_height = max(heights) + 2 * MARGIN + + db.newPage(page_width, page_height) x_offset = MARGIN for f_info in font_info_list: upm = f_info.upm scale_factor = PT_SIZE / upm - baseline = db.height() / 3 / scale_factor + baseline = (abs(lowest_descender) + MARGIN) / scale_factor line_y = ( - f_info.descender, - 0, - f_info.xHeight, - f_info.capHeight, - f_info.ascender + f_info.descender, 0, f_info.xHeight, + f_info.capHeight, f_info.ascender ) glyph_name = f_info.char_map.get(ord(character)) with db.savedState(): @@ -173,7 +211,7 @@ def draw_metrics_page_font( align='center') db.text( get_style_name(f_info.path), - (glyph_width / 2, - baseline + 4 / scale_factor), + (glyph_width / 2, -baseline + (MARGIN / 2) / scale_factor), align='center') @@ -182,67 +220,66 @@ def process_font_paths(font_paths, args): font_info_list = [FontInfo(font_path, args) for font_path in font_list] extension = font_list[0].suffix.upper() family_name = font_info_list[0].familyName + name_length = max([len(fi.ps_name) for fi in font_info_list]) + if args.output_file_name: doc_name = f'comparison {args.output_file_name}' else: doc_name = f'comparison {family_name} ({extension[1:]})' - page_width = sum( - [fi.cap_H_width * PT_SIZE / fi.upm for fi in font_info_list] - ) + 2 * MARGIN - for f_info in font_info_list: - print('{:20s} {:>3d} 0 {:>3d} {:>3d} {:>3d}'.format( - f_info.styleName, - f_info.descender, - f_info.xHeight, - f_info.capHeight, - f_info.ascender)) + report_metrics(f_info, 0, name_length) for char in args.sample_string: draw_metrics_page_font( - char, font_info_list, page_width, args.normalize_upm) + char, font_info_list, args.normalize_upm) finish_drawing(doc_name) +def report_ufo_metrics(fo, name_width=20): + ''' + report ps name, descender, baseline, x-height, cap height, ascender, + ''' + + format_dict = { + 'styleName': fo.info.styleName, + 'ps_name': ( + fo.info.postscriptFontName if fo.info.postscriptFontName else + get_ps_name(fo.path)), + 'descender': fo.info.descender if fo.info.descender else 0, + 'xHeight': fo.info.xHeight if fo.info.xHeight else 0, + 'capHeight': fo.info.capHeight if fo.info.capHeight else 0, + 'ascender': fo.info.ascender if fo.info.ascender else 0, + } + print( + f'{format_dict.get("ps_name"):{name_width}s} ' + f'{format_dict.get("descender"):>5d} 0 ' + f'{format_dict.get("xHeight"):>4d} ' + f'{format_dict.get("capHeight"):>4d} ' + f'{format_dict.get("ascender"):>4d} ' + ) + + def process_ufo_paths(ufo_paths, args): - font_list = fontSorter.sort_fonts(ufo_paths) - fo_list = [defcon.Font(f) for f in font_list] - upm_list = [f.info.unitsPerEm for f in fo_list] + ufo_list = fontSorter.sort_fonts(ufo_paths) + name_length = max([len(get_ps_name(f)) for f in ufo_list]) + + fo_list = [defcon.Font(f) for f in ufo_list] cmap_list = [{g.unicode: g.name for g in f if g.unicode} for f in fo_list] - gnames_H = [cmap.get(ord('H')) for cmap in cmap_list] family_name = fo_list[0].info.familyName if args.output_file_name: doc_name = f'comparison {args.output_file_name}' else: doc_name = f'comparison {family_name} (UFO)' - # get combined width of Hs – no matter which glyph name or UPM they have - page_width = sum( - [fo[gnames_H[i]].width * PT_SIZE / upm_list[i] for i, fo in enumerate(fo_list)] - ) + 2 * MARGIN for fo in fo_list: - # terminal feedback - format_dict = { - 'styleName': fo.info.styleName, - 'descender': fo.info.descender if fo.info.descender else 0, - 'xHeight': fo.info.xHeight if fo.info.xHeight else 0, - 'capHeight': fo.info.capHeight if fo.info.capHeight else 0, - 'ascender': fo.info.ascender if fo.info.ascender else 0, - } - - print('{:20s} {:>3d} 0 {:>3d} {:>3d} {:>3d}'.format( - format_dict.get('styleName'), - format_dict.get('descender'), - format_dict.get('xHeight'), - format_dict.get('capHeight'), - format_dict.get('ascender'))) + report_ufo_metrics(fo, name_length) for char in args.sample_string: draw_metrics_page_ufo( - char, fo_list, cmap_list, page_width, args.normalize_upm) + char, fo_list, cmap_list, args.normalize_upm) finish_drawing(doc_name) diff --git a/drawbot_proofing/verticalMetricsProof.py b/drawbot_proofing/verticalMetricsProof.py index e96731a..d1f9efe 100644 --- a/drawbot_proofing/verticalMetricsProof.py +++ b/drawbot_proofing/verticalMetricsProof.py @@ -93,7 +93,6 @@ def __init__(self, font_path, args): self.descender = 0 self.xHeight = 0 self.capHeight = 0 - self.cap_H_width = 0 self.sample_string = args.sample_string self.parse_cmap() self.extract_vertical_metrics() @@ -104,7 +103,6 @@ def __init__(self, font_path, args): self.extract_names() self.extract_widths() self.extract_upm() - self.extract_cap_H_width() # sTypoAscender # sTypoDescender # sxHeight @@ -171,11 +169,6 @@ def parse_cmap(self): gname: chr(c_index) for c_index, gname in self.char_map.items() } - def extract_cap_H_width(self): - # do not assume the glyph name for 'H' to be 'H' - cap_H_gname = self.char_map.get(ord('H'), '.notdef') - self.cap_H_width = self.advance_widths.get(cap_H_gname) - def get_glyph_names(font_info): ''' @@ -213,18 +206,16 @@ def get_string_bounds(f_info, glyph_names): return min(x_extent), min(y_extent), max(x_extent), max(y_extent) -def draw_metrics_page(f_info, normalize_upm=False): +def draw_metrics_page( + f_info, page_width, page_height, descender_global, normalize_upm=False +): + upm = f_info.upm - glyph_names = get_glyph_names(f_info) scale_factor = PT_SIZE / upm - x_offset = MARGIN_L / scale_factor - - x_min, y_min, x_max, y_max = get_string_bounds(f_info, glyph_names) - line_height = sum([abs(y_min), y_max]) - baseline = abs(y_min) + 2 * MARGIN / scale_factor + ruling_start = MARGIN_L / scale_factor + ruling_end = (page_width - MARGIN - MARGIN_L) / scale_factor - page_width = x_max * PT_SIZE / f_info.upm + MARGIN_L + MARGIN - page_height = line_height * scale_factor + 4 * MARGIN + baseline = -descender_global / scale_factor + MARGIN / scale_factor db.newPage(page_width, page_height) line_labels = ( @@ -244,10 +235,11 @@ def draw_metrics_page(f_info, normalize_upm=False): # sorting the labels according to their value line_labels = sorted(line_labels, key=lambda label: label[1]) + glyph_names = get_glyph_names(f_info) with db.savedState(): db.scale(scale_factor) - db.translate(x_offset, baseline) + db.translate(ruling_start, baseline) with db.savedState(): # draw all the glyphs for glyph_name in glyph_names: @@ -262,7 +254,7 @@ def draw_metrics_page(f_info, normalize_upm=False): for y_value in set([value for _, value in line_labels]): db.stroke(0) db.strokeWidth(1) - db.line((-4 / scale_factor, y_value), (x_max, y_value)) + db.line((-4 / scale_factor, y_value), (ruling_end, y_value)) with db.savedState(): line_height = 10 / scale_factor @@ -314,29 +306,23 @@ def draw_metrics_page(f_info, normalize_upm=False): db.stroke(None) # font name below - db.text( - f_info.ps_name, - (MARGIN_L, 0 - line_height), - align='left') - + db.text(f_info.ps_name, (MARGIN_L, 0 - line_height), align='left') -def process_font_path(font_path, args): - fi = FontInfo(font_path, args) - print('{:20s} {:>3d} 0 {:>3d} {:>3d} {:>3d}'.format( - fi.styleName, - fi.descender, - fi.xHeight, - fi.capHeight, - fi.ascender)) +def report_metrics(fi, extremes, name_length=20): + ''' + report ps name, descender, baseline, x-height, cap height, ascender, + and potentially the most extreme glyphs on each end + ''' + print( + f'{fi.ps_name:{name_length}s} {fi.descender:>5d} 0 ' + f'{fi.xHeight:>4d} {fi.capHeight:>4d} {fi.ascender:>4d} ' + ) - extremes = args.extremes if extremes > 1: print(f'{"":20s} lo {extremes}: {" ".join(fi.g_ymin)}') print(f'{"":20s} hi {extremes}: {" ".join(fi.g_ymax)}') - draw_metrics_page(fi, args.normalize_upm) - def finish_drawing(doc_name): output_path = Path( @@ -347,6 +333,36 @@ def finish_drawing(doc_name): db.endDrawing() +def get_global_metrics(fi_objects): + ''' + measure all the FontInfo objects to see which one is the widest and + tallest, from there deduce the dimensions of the page, and a global + baseline + ''' + string_heights = [] + descenders = [] + x_max_values = [] + for fi in fi_objects: + glyph_names = get_glyph_names(fi) + upm = fi.upm + scale_factor = PT_SIZE / upm + + x_min, y_min, x_max, y_max = get_string_bounds(fi, glyph_names) + x_max_values.append(x_max * scale_factor) + string_height = sum([abs(y_min), y_max]) + string_heights.append(string_height * scale_factor) + descender = min(fi.descender, fi.hheaDescender, -fi.winDescent) + descenders.append(descender * scale_factor) + + x_max = max(x_max_values) + descender = min(descenders) + string_height = max(string_heights) + + page_width = x_max + MARGIN_L + MARGIN + page_height = string_height + 4 * MARGIN + return page_width, page_height, descender + + def main(): if IN_UI: file_or_folder = getFileOrFolder(allowsMultipleSelection=False) @@ -367,8 +383,14 @@ def main(): fontSorter.sort_fonts(fonts, alternate_italics=False)) if font_paths: - for font_path in font_paths: - process_font_path(font_path, args) + fi_objects = [FontInfo(fp, args) for fp in font_paths] + name_length = max([len(fi.ps_name) for fi in fi_objects]) + + page_width, page_height, descender_gl = get_global_metrics(fi_objects) + for fi in fi_objects: + report_metrics(fi, args.extremes, name_length) + draw_metrics_page( + fi, page_width, page_height, descender_gl, args.normalize_upm) if args.output_file_name: doc_name = args.output_file_name @@ -380,7 +402,6 @@ def main(): doc_name = name_overlap else: doc_name = get_path_overlap(font_paths) - print(doc_name) if not IN_UI: finish_drawing(doc_name)