diff --git a/README.md b/README.md index 810a377..eba29da 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Simple script to check figure spacing in fonts or UFOs (without kerning). For each figure suffix found (such as .tosf), a new spacing page is made. Input (pick one): -* folder(s) containing UFO files or font files +* folder(s) containing UFO- or font files * individual UFO- or font files * designspace file (for proofing UFO sources) @@ -132,7 +132,7 @@ Other modes include * `overlay` (superimposed outline view) Input (pick one): -* folder(s) containing UFO files or font files +* folder(s) containing UFO- or font files * individual UFO- or font files * designspace file (for proofing UFO sources) @@ -158,7 +158,7 @@ The glyphset can be filtered with a regular expression (for example, use `-r ".*dieresis"` to show all glyphs whose names end with -dieresis). Input (pick one): -* folder(s) containing UFO files or font files +* folder(s) containing UFO- or font files * individual UFO- or font files * designspace file (for proofing UFO sources) @@ -277,7 +277,7 @@ Input (pick one): Creates simple view which illustrates all vertical metrics set in the font metadata. Additionally, tallest and lowest glyphs are shown. -Using the -n option, the number of extreme glyphs can be increased. +Using the -e option, the number of reported extreme glyphs can be modified. Input: * font file(s), or folder(s) containing font files diff --git a/drawbot_proofing/__init__.py b/drawbot_proofing/__init__.py index 9b34438..a50a4a5 100644 --- a/drawbot_proofing/__init__.py +++ b/drawbot_proofing/__init__.py @@ -1,12 +1,12 @@ """ DrawBot Proofing Tools -A collection of font proofing tools using DrawBot for type designers and developers. +A collection of font proofing tools for type designers and font developers. """ -__version__ = "1.0.3" +__version__ = "1.0.4" __author__ = "Adobe" __email__ = "opensource@adobe.com" -# Import key modules for easier access + from . import proofing_helpers diff --git a/drawbot_proofing/accentProof.py b/drawbot_proofing/accentProof.py index e83d24d..1b00b79 100644 --- a/drawbot_proofing/accentProof.py +++ b/drawbot_proofing/accentProof.py @@ -33,6 +33,27 @@ from .proofing_helpers.globals import FONT_MONO, ADOBE_BLANK +def get_args(args=None): + + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) + + parser.add_argument( + '--headless', + default=False, + action='store_true', + help='do not open result PDF after generating') + + parser.add_argument( + 'input', + metavar='INPUT', + nargs='+', + help='font file(s) or folder(s)') + + return parser.parse_args(args) + + def get_supported_chars(font): ''' characters supported by a font @@ -285,28 +306,8 @@ def get_atomic_latin(start=0): return sorted(atomic) -def get_options(args=None): - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) - parser.add_argument( - 'input', - metavar='INPUT', - nargs='+', - help='font file(s) or folder(s)', - ) - parser.add_argument( - '--headless', - default=False, - action='store_true', - help='do not open result PDF after generating', - ) - return parser.parse_args(args) - - def main(test_args=None): - args = get_options(test_args) + args = get_args(test_args) accents_dict = get_cmb_accents_dict() atomic = get_atomic_latin(start=ord('ß')) atomic_dict = {cp: chr(cp) for cp in atomic} diff --git a/drawbot_proofing/alphabetProof.py b/drawbot_proofing/alphabetProof.py index abaf83c..8244578 100644 --- a/drawbot_proofing/alphabetProof.py +++ b/drawbot_proofing/alphabetProof.py @@ -45,19 +45,18 @@ from .proofing_helpers.formatter import RawDescriptionAndDefaultsFormatter from .proofing_helpers.globals import FONT_MONO, ADOBE_BLANK from .proofing_helpers.names import ( - get_name_overlap, get_path_overlap, get_ps_name) + get_name_overlap, get_path_overlap, get_ps_name, get_unique_name) from .proofing_helpers.stamps import timestamp -def get_options(): +def get_args(): mode_choices = ['proof', 'spacing', 'sample', 'all'] ws_choices = ['lat', 'grk', 'cyr', 'figures', 'auto'] parser = argparse.ArgumentParser( description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( '-m', '--mode', @@ -217,6 +216,7 @@ def make_proof(args, fonts, output_path): font=FONT_MONO, fontSize=10, align='right') + caption += f' | {get_unique_name(font)}' if args.kerning_off: caption += ' | no kerning' caption += f' | {timestamp(readable=True)}' @@ -289,7 +289,7 @@ def make_pdf_name(args, fonts): def main(): - args = get_options() + args = get_args() fonts = [] for item in args.input: if Path(item).exists(): diff --git a/drawbot_proofing/charsetProof.py b/drawbot_proofing/charsetProof.py index 8e00d8b..8f8966c 100644 --- a/drawbot_proofing/charsetProof.py +++ b/drawbot_proofing/charsetProof.py @@ -34,42 +34,41 @@ def get_args(args=None): + available_charsets = [ cs.upper() for cs in dir(charsets) if not cs.startswith('_')] parser = argparse.ArgumentParser( description=(__doc__), - formatter_class=RawDescriptionAndDefaultsFormatter, - ) + formatter_class=RawDescriptionAndDefaultsFormatter) - parser.add_argument( - 'input', - metavar='INPUT', - nargs='+', - help='font file(s) or folder(s)' - ) parser.add_argument( '-p', '--pointsize', metavar='PT', action='store', default=40, type=int, - help='point size for sample' - ) + help='point size for sample') + parser.add_argument( '-s', '--spacer', action='store', metavar='CHR', default='', - help=r'spacing character (may need to be escaped with \)' - ) + help=r'spacing character (may need to be escaped with \)') + parser.add_argument( '-c', '--charset', action='store', default='AL3', # choices=available_charsets, # ugly - help=f'character set ({", ".join(available_charsets)})', - ) + help=f'character set ({", ".join(available_charsets)})',) + + parser.add_argument( + 'input', + metavar='INPUT', + nargs='+', + help='font file(s) or folder(s)') return parser.parse_args(args) diff --git a/drawbot_proofing/contextProof.py b/drawbot_proofing/contextProof.py index 08564b3..af4c75e 100644 --- a/drawbot_proofing/contextProof.py +++ b/drawbot_proofing/contextProof.py @@ -33,11 +33,11 @@ default_wl = Path(__file__).parent / "_content" / "en_10k.txt" -def get_options(): +def get_args(): + parser = argparse.ArgumentParser( description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( '-p', '--point_size', @@ -52,12 +52,6 @@ def get_options(): action='store', help='wordlist file') - parser.add_argument( - '-d', '--date', - default=False, - action='store_true', - help='date output file') - parser.add_argument( '-a', '--word_amount', default=300, @@ -85,6 +79,7 @@ def get_options(): parser.add_argument( 'input', + metavar='INPUT', nargs='+', help='input font file(s)') @@ -180,18 +175,15 @@ def make_output_path(args): flag = 'letters' else: flag = 'letter' - output_name = f'contextProof ({flag} {args.letters})' + output_name = f'context proof ({flag} {args.letters})' else: - output_name = f'contextProof (combination {args.combination})' - - if args.date: - output_name = f'{timestamp()} ' + output_name + output_name = f'context proof (combination {args.combination})' return Path(f'~/Desktop/{output_name}.pdf').expanduser() def main(): - args = get_options() + args = get_args() wordlist_path = Path(args.wordlist) output_path = make_output_path(args) diff --git a/drawbot_proofing/figureSpacingProof.py b/drawbot_proofing/figureSpacingProof.py index 453d575..e99ee4a 100644 --- a/drawbot_proofing/figureSpacingProof.py +++ b/drawbot_proofing/figureSpacingProof.py @@ -10,7 +10,7 @@ For each figure suffix found (such as .tosf), a new spacing page is made. Input (pick one): -* folder(s) containing UFO files or font files +* folder(s) containing UFO- or font files * individual UFO- or font files * designspace file (for proofing UFO sources) @@ -29,9 +29,38 @@ from .proofing_helpers.files import get_font_paths, get_ufo_paths from .proofing_helpers.formatter import RawDescriptionAndDefaultsFormatter from .proofing_helpers.globals import FONT_MONO +from .proofing_helpers.names import get_family_name, get_name_overlap from .proofing_helpers.stamps import timestamp +def get_args(): + + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) + + parser.add_argument( + '--point_size', + default=100, + action='store', + type=int, + help='font size') + + parser.add_argument( + '-s', '--suffixes', + action='store', + help='suffixes', + nargs='*') + + parser.add_argument( + 'input', + nargs='+', + metavar='INPUT', + help='input file(s) or folder(s)') + + return parser.parse_args() + + def joinit(iterable, delimiter): ''' https://stackoverflow.com/a/5656097 @@ -181,44 +210,41 @@ def get_figure_suffixes(gnames, custom_suffixes, report=False): return suffixes -def get_options(): - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) +def make_output_name(paths): + ''' + Make a sensible filename for the PDF proof created. - parser.add_argument( - '--point_size', - default=100, - action='store', - type=int, - help='font size', - ) + ''' + chunks = ['figure spacing'] - parser.add_argument( - '-s', '--suffixes', - action='store', - help='suffixes', - nargs='*', - ) + all_family_names = sorted(set([get_family_name(font) for font in paths])) + family_name_overlap = get_name_overlap(all_family_names) - parser.add_argument( - 'path', - help='folder containing UFO or font files') + if family_name_overlap: + chunks.append(family_name_overlap) + else: + if len(all_family_names) == 1: + chunks.append(all_family_names[0]) + elif len(all_family_names) == 2: + chunks.append(', '.join(all_family_names)) + else: + chunks.append(', '.join(all_family_names[:2]) + ' etc') - return parser.parse_args() + pdf_name = ' '.join(chunks) + '.pdf' + return pdf_name def main(): - args = get_options() + args = get_args() - input_path = Path(args.path) + input_paths = [Path(i) for i in args.input] input_list = [] - input_list.extend(get_ufo_paths(input_path)) - input_list.extend(get_font_paths(input_path)) + for input_path in input_paths: + input_list.extend(get_ufo_paths(input_path)) + input_list.extend(get_font_paths(input_path)) - output_pdf = f'figure spacing {input_path.name}.pdf' - output_path = Path(f'~/Desktop/{output_pdf}').expanduser() + output_name = make_output_name(input_list) + output_path = Path(f'~/Desktop/{output_name}').expanduser() db.newDrawing() for input_file in input_list: diff --git a/drawbot_proofing/glyphProof.py b/drawbot_proofing/glyphProof.py index 31046e0..da9b5de 100644 --- a/drawbot_proofing/glyphProof.py +++ b/drawbot_proofing/glyphProof.py @@ -15,7 +15,7 @@ * `overlay` (superimposed outline view) Input (pick one): -* folder(s) containing UFO files or font files +* folder(s) containing UFO- or font files * individual UFO- or font files * designspace file (for proofing UFO sources) @@ -54,6 +54,64 @@ MARGIN = BOX_HEIGHT * 0.1 +def get_args(args=None): + + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) + + parser.add_argument( + '-m', '--mode', + choices=['default', 'single', 'overlay', 'gradient'], + default='default', + required=False, + help='output mode') + + parser.add_argument( + '-a', '--anchors', + default=False, + action='store_true', + help='draw anchors') + + parser.add_argument( + '-o', '--outlines', + default=False, + action='store_true', + help='only proof glyphs with outlines') + + parser.add_argument( + '-r', '--regex', + action='store', + metavar='REGEX', + type=str, + help='filter glyph list with regular expression') + + parser.add_argument( + '-s', '--stroke_colors', + action='store_true', + default=False, + help='color strokes in overlay mode') + + parser.add_argument( + '-c', '--columns', + required=False, + type=int, + help='override column calculation in default mode') + + parser.add_argument( + '--headless', + action='store_true', + help='do not automatically open PDF files') + + parser.add_argument( + 'input', + metavar='INPUT', + nargs='+', + help='file(s) or folder(s)') + + return parser.parse_args(args) + + class FontContainer(object): ''' an object that contains @@ -960,67 +1018,9 @@ def make_stroke_colors(font_list, args): return stroke_colors -def get_options(args=None): - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) - - parser.add_argument( - '-a', '--anchors', - default=False, - action='store_true', - help='draw anchors') - - parser.add_argument( - '-c', '--contours', - default=False, - action='store_true', - help='only draw glyphs with contours') - - parser.add_argument( - '-r', '--regex', - action='store', - metavar='REGEX', - type=str, - help='regular expression to filter glyph list') - - parser.add_argument( - '-m', '--mode', - choices=['single', 'overlay', 'gradient'], - default='default', - required=False, - help='alternate output modes') - - parser.add_argument( - '-s', '--stroke_colors', - action='store_true', - default=False, - help='color strokes in overlay mode') - - parser.add_argument( - '-l', '--columns', - required=False, - type=int, - help='override automatic number of columns') - - parser.add_argument( - '--headless', - action='store_true', - help='do not automatically open PDF files') - - parser.add_argument( - 'input', - metavar='INPUT', - nargs='+', - help='file(s) or folder(s)') - - return parser.parse_args(args) - - def make_proof(proofing_fonts, args): - glyph_list = get_glyph_names(proofing_fonts, args.contours) + glyph_list = get_glyph_names(proofing_fonts, args.outlines) if args.regex: glyph_list = filter_glyph_names(glyph_list, args.regex) @@ -1083,7 +1083,7 @@ def make_proof(proofing_fonts, args): def main(test_args=None): - args = get_options(test_args) + args = get_args(test_args) proofing_fonts = build_proofing_fonts(args.input) if proofing_fonts: for pf in proofing_fonts: diff --git a/drawbot_proofing/glyphsetProof.py b/drawbot_proofing/glyphsetProof.py index ea43815..cd1871b 100644 --- a/drawbot_proofing/glyphsetProof.py +++ b/drawbot_proofing/glyphsetProof.py @@ -13,7 +13,7 @@ use `-r ".*dieresis"` to show all glyphs whose names end with -dieresis). Input (pick one): -* folder(s) containing UFO files or font files +* folder(s) containing UFO- or font files * individual UFO- or font files * designspace file (for proofing UFO sources) @@ -37,16 +37,10 @@ def get_args(args=None): + parser = argparse.ArgumentParser( description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) - - parser.add_argument( - 'input', - nargs='+', - metavar='INPUT', - help='file(s) or folder(s)') + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( '-r', '--regex', @@ -67,6 +61,12 @@ def get_args(args=None): default=False, help='draw a stroke around glyph boxes') + parser.add_argument( + 'input', + nargs='+', + metavar='INPUT', + help='file(s) or folder(s)') + return parser.parse_args(args) diff --git a/drawbot_proofing/overlayProof.py b/drawbot_proofing/overlayProof.py index b9a19f3..9eb1cd8 100644 --- a/drawbot_proofing/overlayProof.py +++ b/drawbot_proofing/overlayProof.py @@ -32,21 +32,14 @@ from .proofing_helpers.fonts import make_temp_font from .proofing_helpers.formatter import RawDescriptionAndDefaultsFormatter from .proofing_helpers.globals import FONT_MONO -from .proofing_helpers.names import get_ps_name +from .proofing_helpers.names import get_ps_name, get_unique_name def get_args(default_args=None): + parser = argparse.ArgumentParser( description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) - parser.add_argument( - 'ff_a', - help='font or folder a') - - parser.add_argument( - 'ff_b', - help='font or folder b') + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( '-p', '--pt_size', @@ -54,6 +47,12 @@ def get_args(default_args=None): type=int, help='point size') + parser.add_argument( + 'input', + nargs=2, + metavar='INPUT', + help='two font files or folders') + return parser.parse_args(default_args) @@ -131,10 +130,8 @@ def make_page(txt, font_a, font_b, color_a, color_b, pt_size): db.newPage('A4') db.blendMode('multiply') - f_a = TTFont(font_a) - f_b = TTFont(font_b) - uname_a = f_a['name'].getDebugName(3) - uname_b = f_b['name'].getDebugName(3) + uname_a = get_unique_name(font_a) + uname_b = get_unique_name(font_b) content = db.FormattedString( txt, font=font_a, fontSize=pt_size, fill=color_a) @@ -285,7 +282,7 @@ def collect_font_pairs(paths): def main(): args = get_args() - paths = [Path(i) for i in [args.ff_a, args.ff_b]] + paths = [Path(i) for i in args.input] txt = get_text() if all([p.is_dir() for p in paths]): @@ -319,7 +316,7 @@ def main(): ps_names = [get_ps_name(f) for f in fonts] temp_fonts = [ - make_temp_font(fi, f) for fi, f in enumerate(fonts)] + Path(make_temp_font(fi, f)) for fi, f in enumerate(fonts)] make_pages(txt, temp_fonts, args.pt_size) else: diff --git a/drawbot_proofing/proofing_helpers/names.py b/drawbot_proofing/proofing_helpers/names.py index 0880950..a705dd3 100644 --- a/drawbot_proofing/proofing_helpers/names.py +++ b/drawbot_proofing/proofing_helpers/names.py @@ -9,29 +9,30 @@ from fontTools import ttLib +def get_fi_dict(ufo_file): + fontinfo_path = ufo_file.joinpath('fontinfo.plist') + with open(fontinfo_path, 'rb') as fi_blob: + fi_dict = plistlib.load(fi_blob) + return fi_dict + + def get_ps_name(input_file): ''' Return the PS name for a font or UFO. If the UFO PS name is not filled in, synthesize it. ''' - if input_file.suffix == '.ufo': - - fontinfo_path = input_file.joinpath('fontinfo.plist') - with open(fontinfo_path, 'rb') as fi_blob: - fi_dict = plistlib.load(fi_blob) - + if input_file.suffix.lower() == '.ufo': + 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') - ps_name = '-'.join([ - family_name.replace(' ', ''), - style_name.replace(' ', '') - ]) + joined_name = f'{family_name}-{style_name}' + ps_name = joined_name.replace(' ', '') else: - ttf = ttLib.TTFont(input_file.resolve()) - name_table = ttf.get('name') + f = ttLib.TTFont(input_file) + name_table = f.get('name') ps_name = name_table.getDebugName(6) if not ps_name: ps_name = '' @@ -39,6 +40,64 @@ def get_ps_name(input_file): return ps_name +def get_family_name(input_file): + + if input_file.suffix.lower() == '.ufo': + fi_dict = get_fi_dict(input_file) + family_name = fi_dict.get('familyName') + + else: + f = ttLib.TTFont(input_file) + name_table = f.get('name') + family_name = name_table.getDebugName(16) + if not family_name: + family_name = name_table.getDebugName(1) + + if not family_name: + family_name = '[no family name]' + + return family_name + + +def get_style_name(input_file): + + if input_file.suffix.lower() == '.ufo': + fi_dict = get_fi_dict(input_file) + style_name = fi_dict.get('styleName') + + else: + f = ttLib.TTFont(input_file) + name_table = f.get('name') + style_name = name_table.getDebugName(17) + if not style_name: + style_name = name_table.getDebugName(2) + + if not style_name: + style_name = '[no style name]' + + return style_name + + +def get_unique_name(input_file): + + if input_file.suffix.lower() == '.ufo': + fi_dict = get_fi_dict(input_file) + version_maj = fi_dict.get('versionMajor', 0) + version_min = fi_dict.get('versionMinor', 0) + manufacturer = fi_dict.get('openTypeNameManufacturer', 'NONE') + + version_str = f'{version_maj}.{version_min:>03}' + ps_name = get_ps_name(input_file) + unique_name = f'{version_str};{manufacturer};{ps_name};' + + else: + f = ttLib.TTFont(input_file) + name_table = f.get('name') + unique_name = name_table.getDebugName(3) + + return unique_name + + def get_overlap_index(list_of_strings, start_char=0): ''' For a list of strings, find the index at which they stop overlapping. diff --git a/drawbot_proofing/referenceProof.py b/drawbot_proofing/referenceProof.py index d26605c..6101589 100644 --- a/drawbot_proofing/referenceProof.py +++ b/drawbot_proofing/referenceProof.py @@ -50,6 +50,60 @@ ] +def get_args(args=None): + + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) + + parser.add_argument( + 'chars', + metavar='', + nargs='?', + action='store', + help='characters to sample') + + parser.add_argument( + '-r', '--regex', + action='store', + metavar='REGEX', + type=str, + help='regular expression to filter font list') + + parser.add_argument( + '--pt', + action='store', + metavar='POINTS', + default=30, + help='point size') + + parser.add_argument( + '--headless', + default=False, + action='store_true', + help='do not open result PDF after generating') + + parser.add_argument( + '-d', '--directory', + action='store', + metavar='FOLDER', + help='folder to crawl (default is using all installed fonts)') + + parser.add_argument( + '--pagesize', + choices=[size for size in db.sizes() if "Landscape" not in size], + default="Letter", + help='page size') + + parser.add_argument( + '--landscape', + default=False, + action='store_true', + help='landscape orientation (default is portrait)') + + return parser.parse_args(args) + + class FontFileInfo(object): def __init__(self, font_path, ps_name, font_number=0): self.path = font_path @@ -166,14 +220,14 @@ def get_available_fonts(args): and a font number (if applicable). ''' available_fonts = [] - if args.input_path: - input_path = Path(args.input_path) - if input_path.is_dir(): # find fonts in input_path + if args.directory: + directory = Path(args.directory) + if directory.is_dir(): # find fonts in directory font_paths = ( - list(input_path.rglob('*.otf')) + - list(input_path.rglob('*.OTF')) + - list(input_path.rglob('*.tt[fc]')) + - list(input_path.rglob('*.TT[FC]'))) + list(directory.rglob('*.otf')) + + list(directory.rglob('*.OTF')) + + list(directory.rglob('*.tt[fc]')) + + list(directory.rglob('*.TT[FC]'))) for font_path in font_paths: if ( @@ -183,10 +237,10 @@ def get_available_fonts(args): ): available_fonts.extend(font_path_to_ffi(font_path)) - elif input_path.is_file(): - available_fonts.extend(font_path_to_ffi(input_path)) + elif directory.is_file(): + available_fonts.extend(font_path_to_ffi(directory)) else: - print(f'{args.input_path} seems to be invalid.') + print(f'{args.directory} seems to be invalid.') else: # use installed fonts installed_fonts = db.installedFonts() @@ -235,12 +289,12 @@ def collect_font_objects(args): def make_pdf_name(args): chars_safe = args.chars.replace('/', '_') # remove slash from path - if args.input_path: - short_dir = Path(args.input_path).name - pdf_name = f'comparisonProof {chars_safe} ({short_dir}).pdf' + if args.directory: + short_dir = Path(args.directory).name + pdf_name = f'reference proof {chars_safe} ({short_dir}).pdf' else: - pdf_name = f'comparisonProof {chars_safe} (installed fonts).pdf' + pdf_name = f'reference proof {chars_safe} (installed fonts).pdf' return pdf_name @@ -288,62 +342,8 @@ def make_document(args, formatted_strings): return output_path -def get_options(args=None): - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) - - parser.add_argument( - 'chars', - metavar='', - nargs='?', - action='store', - help='characters to sample') - - parser.add_argument( - '-r', '--regex', - action='store', - metavar='REGEX', - type=str, - help='regular expression to filter font list') - - parser.add_argument( - '--pt', - action='store', - metavar='POINTS', - default=30, - help='point size') - - parser.add_argument( - '--headless', - default=False, - action='store_true', - help='do not open result PDF after generating') - - parser.add_argument( - '-d', '--input_path', - action='store', - metavar='FOLDER', - help='folder to crawl (default is using all installed fonts)') - - parser.add_argument( - '--pagesize', - choices=[size for size in db.sizes() if "Landscape" not in size], - default="Letter", - help='page size') - - parser.add_argument( - '--landscape', - default=False, - action='store_true', - help='landscape orientation (default is portrait)') - - return parser.parse_args(args) - - def main(test_args=None): - args = get_options(test_args) + args = get_args(test_args) if not args.chars: print('Please specify sample characters.') else: diff --git a/drawbot_proofing/textProof.py b/drawbot_proofing/textProof.py index cacaac3..5871e46 100644 --- a/drawbot_proofing/textProof.py +++ b/drawbot_proofing/textProof.py @@ -53,31 +53,18 @@ from .proofing_helpers.stamps import timestamp -DOC_SIZE = 'Letter' -MARGIN = 12 - - -class TextContainer(object): - def __init__(self, text, italic=False, paragraph=False): - self.text = text.strip() - self.italic = italic - self.paragraph = paragraph +def get_args(): + charset_choices = [name for name in dir(cs) if not name.startswith('_')] -def get_options(): parser = argparse.ArgumentParser( description=__doc__, - formatter_class=RawDescriptionAndDefaultsFormatter - ) - - charset_choices = [name for name in dir(cs) if not name.startswith('_')] + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( - # '-f', '--fonts', 'fonts', nargs='+', - # required=True, - metavar='FONT', + metavar='INPUT', help='font file or folder') group = parser.add_mutually_exclusive_group(required=False) @@ -134,15 +121,26 @@ def get_options(): help='report information about the characters used') parser.add_argument( - '-s', '--secondary_fonts', + '-s', '--secondary_font', nargs='+', - metavar='FONT', + metavar='INPUT', default=[], help='secondary font file or folder') return parser.parse_args() +DOC_SIZE = 'Letter' +MARGIN = 12 + + +class TextContainer(object): + def __init__(self, text, italic=False, paragraph=False): + self.text = text.strip() + self.italic = italic + self.paragraph = paragraph + + def merge_chunks(chunks, chunk_length=5): output = [] appended = 0 @@ -505,10 +503,10 @@ def get_fonts(input_paths): def main(): - args = get_options() + args = get_args() fonts_a = get_fonts(args.fonts) - fonts_b = get_fonts(args.secondary_fonts) + fonts_b = get_fonts(args.secondary_font) gpp_count = 0 for i, font in enumerate(fonts_a + fonts_b): @@ -523,7 +521,7 @@ def main(): if args.text_file: content_list = get_content_from_text_file(args.text_file) output_name = make_output_name( - args.fonts, args.secondary_fonts, + args.fonts, args.secondary_font, 'text file', args.pt_size, args.full) formatted_content = format_content(content_list, len_limit, False) @@ -532,7 +530,7 @@ def main(): charset = validate_charset(charset_name) content_list = get_content_from_charset(charset_name) output_name = make_output_name( - args.fonts, args.secondary_fonts, + args.fonts, args.secondary_font, charset_name, args.pt_size, args.full) formatted_content = make_formatted_content( content_list, charset, diff --git a/drawbot_proofing/unicodeChartProof.py b/drawbot_proofing/unicodeChartProof.py index 513dc5a..1a79227 100644 --- a/drawbot_proofing/unicodeChartProof.py +++ b/drawbot_proofing/unicodeChartProof.py @@ -43,11 +43,10 @@ from vanilla.dialogs import getFileOrFolder # noqa: F401 -def get_options(args=None, description=__doc__): +def get_args(args=None, description=__doc__): parser = argparse.ArgumentParser( description=description, - formatter_class=RawDescriptionAndDefaultsFormatter, - ) + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( 'input_dir', @@ -340,10 +339,10 @@ def main(): if IN_UI: file_or_folder = getFileOrFolder(allowsMultipleSelection=False) input_dir = str(file_or_folder[0]) - args = get_options([input_dir]) + args = get_args([input_dir]) else: - args = get_options() + args = get_args() font_paths = get_font_paths(args.input_dir) sorted_font_paths = sort_fonts(font_paths) diff --git a/drawbot_proofing/verticalMetricsComparisonProof.py b/drawbot_proofing/verticalMetricsComparisonProof.py index 2b639d6..8a5329e 100644 --- a/drawbot_proofing/verticalMetricsComparisonProof.py +++ b/drawbot_proofing/verticalMetricsComparisonProof.py @@ -16,30 +16,63 @@ ''' +import argparse import defcon import drawBot as db -from .verticalMetricsProof import ( - MARGIN, PT_SIZE, - FontInfo, - finish_drawing, get_options) +from .verticalMetricsProof import MARGIN, PT_SIZE, FontInfo, finish_drawing 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 -EXAMPLE_CHARS = list('Hnxphlg') +def get_args(): + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) -def draw_metrics_page_ufo(character, fo_list, cmap_list, page_width=1000): + parser.add_argument( + 'input', + metavar='INPUT', + nargs='+', + help='input file(s) or folder(s)', + ) + parser.add_argument( + '-o', '--output_file_name', + action='store', + metavar='PDF', + help='output file name') + + parser.add_argument( + '-u', '--normalize_upm', + action='store_true', + default=False, + help='convert label values to 1000 UPM-equivalent') + + parser.add_argument( + '-s', '--sample_string', + type=str, + default='Hnxphlg', + help='sample string') + + return parser.parse_args() + + +def draw_metrics_page_ufo( + character, fo_list, cmap_list, page_width=1000, normalize_upm=False +): db.newPage(page_width, 250) x_offset = MARGIN for i, font in enumerate(fo_list): - scale_factor = PT_SIZE / font.info.unitsPerEm + upm = font.info.unitsPerEm + scale_factor = PT_SIZE / upm baseline = db.height() / 3 / scale_factor line_y = ( font.info.descender if font.info.descender else -250, @@ -63,22 +96,30 @@ def draw_metrics_page_ufo(character, fo_list, cmap_list, page_width=1000): db.strokeWidth(1) db.line((0, y_value), (glyph.width, y_value)) with db.savedState(): + if normalize_upm and upm != 1000: + conversion_factor = 1000 / upm + label_value = f'{y_value * conversion_factor:.0f}' + else: + label_value = str(y_value) + for y_value in [v for v in line_y if v != 0]: db.font(FONT_MONO) db.fontSize(6 / scale_factor) # db.fill(0, 0.981, 0.574) # Sea Foam db.fill(1, 0.186, 0.573) # Strawberry db.text( - str(y_value), + label_value, (glyph.width / 2, y_value + 2 / scale_factor), align='center') db.text( - font.info.styleName, + get_style_name(font.path), (glyph.width / 2, -baseline + 4 / scale_factor), align='center') -def draw_metrics_page_font(character, font_info_list, page_width=1000): +def draw_metrics_page_font( + character, font_info_list, page_width=1000, normalize_upm=False +): db.newPage(page_width, 250) x_offset = MARGIN @@ -114,16 +155,22 @@ def draw_metrics_page_font(character, font_info_list, page_width=1000): db.line((0, y_value), (glyph_width, y_value)) with db.savedState(): for y_value in [v for v in line_y if v != 0]: + if normalize_upm and upm != 1000: + conversion_factor = 1000 / upm + label_value = f'{y_value * conversion_factor:.0f}' + else: + label_value = str(y_value) + db.font(FONT_MONO) db.fontSize(6 / scale_factor) # db.fill(0, 0.981, 0.574) # Sea Foam db.fill(1, 0.186, 0.573) # Strawberry db.text( - str(y_value), + label_value, (glyph_width / 2, y_value + 2 / scale_factor), align='center') db.text( - f_info.styleName, + get_style_name(f_info.path), (glyph_width / 2, - baseline + 4 / scale_factor), align='center') @@ -150,8 +197,9 @@ def process_font_paths(font_paths, args): f_info.capHeight, f_info.ascender)) - for char in EXAMPLE_CHARS: - draw_metrics_page_font(char, font_info_list, page_width) + for char in args.sample_string: + draw_metrics_page_font( + char, font_info_list, page_width, args.normalize_upm) finish_drawing(doc_name) @@ -190,14 +238,15 @@ def process_ufo_paths(ufo_paths, args): format_dict.get('capHeight'), format_dict.get('ascender'))) - for char in EXAMPLE_CHARS: - draw_metrics_page_ufo(char, fo_list, cmap_list, page_width) + for char in args.sample_string: + draw_metrics_page_ufo( + char, fo_list, cmap_list, page_width, args.normalize_upm) finish_drawing(doc_name) def main(): - args = get_options(description=__doc__) + args = get_args() font_paths = [] ufo_paths = [] for p in args.input: diff --git a/drawbot_proofing/verticalMetricsProof.py b/drawbot_proofing/verticalMetricsProof.py index 3ab12af..e96731a 100644 --- a/drawbot_proofing/verticalMetricsProof.py +++ b/drawbot_proofing/verticalMetricsProof.py @@ -9,7 +9,7 @@ Creates simple view which illustrates all vertical metrics set in the font metadata. Additionally, tallest and lowest glyphs are shown. -Using the -n option, the number of extreme glyphs can be increased. +Using the -e option, the number of reported extreme glyphs can be modified. Input: * font file(s), or folder(s) containing font files @@ -43,13 +43,11 @@ MARGIN_L = 6 * MARGIN -def get_options(args=None, description=__doc__): +def get_args(): + parser = argparse.ArgumentParser( - # this is a deliberate construction, - # so the description can be overridden on import - description=description, - formatter_class=RawDescriptionAndDefaultsFormatter - ) + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( 'input', @@ -64,9 +62,10 @@ def get_options(args=None, description=__doc__): help='output file name') parser.add_argument( - '-n', '--num_extremes', + '-e', '--extremes', type=int, default=1, + metavar='INT', help='number of extreme glyphs') parser.add_argument( @@ -81,7 +80,7 @@ def get_options(args=None, description=__doc__): default='Hxbpg', help='sample string') - return parser.parse_args(args) + return parser.parse_args() class FontInfo(object): @@ -98,7 +97,10 @@ def __init__(self, font_path, args): self.sample_string = args.sample_string self.parse_cmap() self.extract_vertical_metrics() - self.extract_extreme_n_glyphs(n=args.num_extremes) + if hasattr(args, 'extremes'): + # comparison proof does not need extremes + self.extract_extreme_n_glyphs(n=args.extremes) + self.extract_names() self.extract_widths() self.extract_upm() @@ -328,9 +330,10 @@ def process_font_path(font_path, args): fi.capHeight, fi.ascender)) - if args.num_extremes > 1: - print(f'{"":20s} lo {args.num_extremes}: {" ".join(fi.g_ymin)}') - print(f'{"":20s} hi {args.num_extremes}: {" ".join(fi.g_ymax)}') + 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) @@ -349,10 +352,10 @@ def main(): file_or_folder = getFileOrFolder(allowsMultipleSelection=False) input_dir = str(file_or_folder[0]) # would like to get # extremes and sample string here somehow... - args = get_options([input_dir]) + args = get_args([input_dir]) else: - args = get_options() + args = get_args() font_paths = [] for item in args.input: diff --git a/drawbot_proofing/waterfallProof.py b/drawbot_proofing/waterfallProof.py index 737318a..aea3216 100644 --- a/drawbot_proofing/waterfallProof.py +++ b/drawbot_proofing/waterfallProof.py @@ -24,17 +24,22 @@ from .proofing_helpers import fontSorter from .proofing_helpers.files import get_font_paths, read_text_file +from .proofing_helpers.formatter import RawDescriptionAndDefaultsFormatter +from .proofing_helpers.names import get_family_name, get_name_overlap -def get_options(): +def get_args(): + parser = argparse.ArgumentParser( - description=__doc__) + description=__doc__, + formatter_class=RawDescriptionAndDefaultsFormatter) parser.add_argument( 'input', action='store', - metavar='FOLDER', - help='folder to crawl') + metavar='INPUT', + nargs='+', + help='font file(s) or folder(s)') parser.add_argument( '--pointsize', '-p', @@ -64,14 +69,35 @@ def get_options(): return parser.parse_args() -def make_proof(input_dir, args): +def make_output_name(fonts): + ''' + Make a sensible filename for the PDF proof created. + + ''' + chunks = ['waterfall proof'] + all_family_names = sorted(set([get_family_name(font) for font in fonts])) + + family_name_overlap = get_name_overlap(all_family_names) + + if family_name_overlap: + chunks.append(family_name_overlap) + else: + if len(all_family_names) == 1: + chunks.append(all_family_names[0]) + elif len(all_family_names) == 2: + chunks.append(', '.join(all_family_names)) + else: + chunks.append(', '.join(all_family_names[:2]) + ' etc') + + pdf_name = ' '.join(chunks) + '.pdf' + return pdf_name + + +def make_proof(fonts, args): content_dir = Path(__file__).parent / '_content' v_content = read_text_file(content_dir / 'waterfall_vertical.txt') h_content = read_text_file(content_dir / 'waterfall_horizontal.txt') - font_paths = get_font_paths(input_dir) - fonts = fontSorter.sort_fonts(font_paths, alternate_italics=True) - db.newDrawing() pt_size = args.pointsize leading = pt_size * 1.2 @@ -112,22 +138,40 @@ def make_proof(input_dir, args): db.text(fs, (margin, offset)) - output_path = Path( - f'~/Desktop/waterfallProof ({input_dir.name}).pdf').expanduser() + output_name = make_output_name(fonts) + output_path = Path(f'~/Desktop/{output_name}').expanduser() db.saveImage(output_path) db.endDrawing() print(f'saved to {output_path}') subprocess.call(['open', output_path]) -def main(): - args = get_options() - input_dir = Path(args.input) +def collect_fonts(inputs): + + fonts = [] + input_paths = [Path(i) for i in inputs] + if all([p.exists() for p in input_paths]): + if all([ip.is_file() for ip in input_paths]): + # all font files. no sorting + for ip in input_paths: + fonts.extend(get_font_paths(ip)) + else: + # at least some folders. sort per folder + for ip in input_paths: + sorted_fonts = fontSorter.sort_fonts( + get_font_paths(ip), alternate_italics=True) + fonts.extend(sorted_fonts) - if input_dir.is_dir(): - make_proof(input_dir, args) + return fonts + + +def main(): + args = get_args() + fonts = collect_fonts(args.input) + if fonts: + make_proof(fonts, args) else: - print(f'{args.input} is not a directory') + print(f'could not find any fonts') if __name__ == '__main__': diff --git a/pyproject.toml b/pyproject.toml index e85a5f6..6204fd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "drawbot-proofing" -version = "1.0.3" +version = "1.0.4" description = "A collection of font proofing tools using DrawBot" readme = "README.md" requires-python = ">=3.11"