Skip to content

Commit

Permalink
Ensure all fonttools CLI tools have help documentation (#1948)
Browse files Browse the repository at this point in the history
Note UI change : `fonttools varLib.models` now takes prefixed options `-d` or `-l` instead of guessing the intended feature from the number of arguments.

We have a number of command line tools which are somewhat opaque. (varLib.models in particular was very confusing.) This ensures that they all use argparse to have a consistent interface, and all have --help documentation which at least details their parameters, and hopefully therefore gives more of a clue about what they do. Those which use logging have had a command-line logging parameter added.
  • Loading branch information
simoncozens committed May 12, 2020
1 parent a114ec2 commit 089f24d
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 47 deletions.
24 changes: 19 additions & 5 deletions Lib/fontTools/cffLib/width.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,30 @@ def optimizeWidths(widths):

return default, nominal

def main():
def main(args=None):
"""Calculate optimum defaultWidthX/nominalWidthX values"""
for fontfile in sys.argv[1:]:

import argparse
parser = argparse.ArgumentParser(
"fonttools cffLib.width",
description=main.__doc__,
)
parser.add_argument('inputs', metavar='FILE', type=str, nargs='+',
help="Input TTF files")
parser.add_argument('-b', '--brute-force', dest="brute", action="store_true",
help="Use brute-force approach (VERY slow)")

args = parser.parse_args(args)

for fontfile in args.inputs:
font = TTFont(fontfile)
hmtx = font['hmtx']
widths = [m[0] for m in hmtx.metrics.values()]
default, nominal = optimizeWidths(widths)
if args.brute:
default, nominal = optimizeWidthsBruteforce(widths)
else:
default, nominal = optimizeWidths(widths)
print("glyphs=%d default=%d nominal=%d byteCost=%d" % (len(widths), default, nominal, byteCost(widths, default, nominal)))
#default, nominal = optimizeWidthsBruteforce(widths)
#print("glyphs=%d default=%d nominal=%d byteCost=%d" % (len(widths), default, nominal, byteCost(widths, default, nominal)))

if __name__ == '__main__':
import sys
Expand Down
28 changes: 21 additions & 7 deletions Lib/fontTools/mtiLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,17 +1164,31 @@ def main(args=None, font=None):
# comment this out to enable debug messages from mtiLib's logger
# log.setLevel(logging.DEBUG)

import argparse
parser = argparse.ArgumentParser(
"fonttools mtiLib",
description=main.__doc__,
)

parser.add_argument('--font', '-f', metavar='FILE', dest="font",
help="Input TTF files (used for glyph classes and sorting coverage tables)")
parser.add_argument('--table', '-t', metavar='TABLE', dest="tableTag",
help="Table to fill (sniffed from input file if not provided)")
parser.add_argument('inputs', metavar='FILE', type=str, nargs='+',
help="Input FontDame .txt files")

args = parser.parse_args(args)

if font is None:
font = MockFont()
if args.font:
font = ttLib.TTFont(args.font)
else:
font = MockFont()

tableTag = None
if args[0].startswith('-t'):
tableTag = args[0][2:]
del args[0]
for f in args:
for f in args.inputs:
log.debug("Processing %s", f)
with open(f, 'rt', encoding="utf-8") as f:
table = build(f, font, tableTag=tableTag)
table = build(f, font, tableTag=args.tableTag)
blob = table.compile(font) # Make sure it compiles
decompiled = table.__class__()
decompiled.decompile(blob, font) # Make sure it decompiles!
Expand Down
23 changes: 20 additions & 3 deletions Lib/fontTools/ttLib/woff2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,17 @@ def main(args=None):
from fontTools import configLogger
from fontTools.ttx import makeOutputFileName

class _HelpAction(argparse._HelpAction):

def __call__(self, parser, namespace, values, option_string=None):
subparsers_actions = [
action for action in parser._actions
if isinstance(action, argparse._SubParsersAction)]
for subparsers_action in subparsers_actions:
for choice, subparser in subparsers_action.choices.items():
print(subparser.format_help())
parser.exit()

class _NoGlyfTransformAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
namespace.transform_tables.difference_update({"glyf", "loca"})
Expand All @@ -1405,12 +1416,18 @@ def __call__(self, parser, namespace, values, option_string=None):

parser = argparse.ArgumentParser(
prog="fonttools ttLib.woff2",
description=main.__doc__
description=main.__doc__,
add_help = False
)

parser.add_argument('-h', '--help', action=_HelpAction,
help='show this help message and exit')

parser_group = parser.add_subparsers(title="sub-commands")
parser_compress = parser_group.add_parser("compress")
parser_decompress = parser_group.add_parser("decompress")
parser_compress = parser_group.add_parser("compress",
description = "Compress a TTF or OTF font to WOFF2")
parser_decompress = parser_group.add_parser("decompress",
description = "Decompress a WOFF2 font to OTF")

for subparser in (parser_compress, parser_decompress):
group = subparser.add_mutually_exclusive_group(required=False)
Expand Down
2 changes: 1 addition & 1 deletion Lib/fontTools/varLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ def main(args=None):
from argparse import ArgumentParser
from fontTools import configLogger

parser = ArgumentParser(prog='varLib')
parser = ArgumentParser(prog='varLib', description = main.__doc__)
parser.add_argument('designspace')
parser.add_argument(
'-o',
Expand Down
18 changes: 13 additions & 5 deletions Lib/fontTools/varLib/interpolatable.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,23 +157,31 @@ def test(glyphsets, glyphs=None, names=None):
#for x in hist:
# print(x)

def main(args):
def main(args=None):
"""Test for interpolatability issues between fonts"""
filenames = args
import argparse
parser = argparse.ArgumentParser(
"fonttools varLib.interpolatable",
description=main.__doc__,
)
parser.add_argument('inputs', metavar='FILE', type=str, nargs='+',
help="Input TTF files")

args = parser.parse_args(args)
glyphs = None
#glyphs = ['uni08DB', 'uniFD76']
#glyphs = ['uni08DE', 'uni0034']
#glyphs = ['uni08DE', 'uni0034', 'uni0751', 'uni0753', 'uni0754', 'uni08A4', 'uni08A4.fina', 'uni08A5.fina']

from os.path import basename
names = [basename(filename).rsplit('.', 1)[0] for filename in filenames]
names = [basename(filename).rsplit('.', 1)[0] for filename in args.inputs]

from fontTools.ttLib import TTFont
fonts = [TTFont(filename) for filename in filenames]
fonts = [TTFont(filename) for filename in args.inputs]

glyphsets = [font.getGlyphSet() for font in fonts]
test(glyphsets, glyphs=glyphs, names=names)

if __name__ == '__main__':
import sys
main(sys.argv[1:])
main()
36 changes: 24 additions & 12 deletions Lib/fontTools/varLib/interpolate_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,40 @@ def interpolate_layout(designspace, loc, master_finder=lambda s:s, mapped=False)
def main(args=None):
"""Interpolate GDEF/GPOS/GSUB tables for a point on a designspace"""
from fontTools import configLogger

import argparse
import sys
if args is None:
args = sys.argv[1:]

designspace_filename = args[0]
locargs = args[1:]
outfile = os.path.splitext(designspace_filename)[0] + '-instance.ttf'
parser = argparse.ArgumentParser(
"fonttools varLib.interpolate_layout",
description=main.__doc__,
)
parser.add_argument('designspace_filename', metavar='DESIGNSPACE',
help="Input TTF files")
parser.add_argument('locations', metavar='LOCATION', type=str, nargs='+',
help="Axis locations (e.g. wdth=120")
parser.add_argument('-o', '--output', metavar='OUTPUT',
help="Output font file (defaults to <designspacename>-instance.ttf)")
parser.add_argument('-l', '--loglevel', metavar='LEVEL', default="INFO",
help="Logging level (defaults to INFO)")


args = parser.parse_args(args)

if not args.output:
args.output = os.path.splitext(args.designspace_filename)[0] + '-instance.ttf'

# TODO: allow user to configure logging via command-line options
configLogger(level="INFO")
configLogger(level=args.loglevel)

finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf')

loc = {}
for arg in locargs:
for arg in args.locations:
tag,val = arg.split('=')
loc[tag] = float(val)

font = interpolate_layout(designspace_filename, loc, finder)
log.info("Saving font %s", outfile)
font.save(outfile)
font = interpolate_layout(args.designspace_filename, loc, finder)
log.info("Saving font %s", args.output)
font.save(args.output)


if __name__ == "__main__":
Expand Down
31 changes: 18 additions & 13 deletions Lib/fontTools/varLib/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,27 +422,32 @@ def piecewiseLinearMap(v, mapping):
return va + (vb - va) * (v - a) / (b - a)


def main(args):
def main(args=None):
"""Normalize locations on a given designspace"""
from fontTools import configLogger
import argparse

args = args[1:]
parser = argparse.ArgumentParser(
"fonttools varLib.models",
description=main.__doc__,
)
parser.add_argument('--loglevel', metavar='LEVEL', default="INFO",
help="Logging level (defaults to INFO)")

# TODO: allow user to configure logging via command-line options
configLogger(level="INFO")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-d', '--designspace',metavar="DESIGNSPACE",type=str)
group.add_argument('-l', '--locations', metavar='LOCATION', nargs='+',
help="Master locations as comma-separate coordinates. One must be all zeros.")

if len(args) < 1:
print("usage: fonttools varLib.models source.designspace", file=sys.stderr)
print(" or")
print("usage: fonttools varLib.models location1 location2 ...", file=sys.stderr)
sys.exit(1)
args = parser.parse_args(args)

configLogger(level=args.loglevel)
from pprint import pprint

if len(args) == 1 and args[0].endswith('.designspace'):
if args.designspacefile:
from fontTools.designspaceLib import DesignSpaceDocument
doc = DesignSpaceDocument()
doc.read(args[0])
doc.read(args.designspacefile)
locs = [s.location for s in doc.sources]
print("Original locations:")
pprint(locs)
Expand All @@ -452,7 +457,7 @@ def main(args):
pprint(locs)
else:
axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args.locations]

model = VariationModel(locs)
print("Sorted locations:")
Expand All @@ -464,6 +469,6 @@ def main(args):
import doctest, sys

if len(sys.argv) > 1:
sys.exit(main(sys.argv))
sys.exit(main())

sys.exit(doctest.testmod().failed)
2 changes: 1 addition & 1 deletion Lib/fontTools/varLib/varStore.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ def main(args=None):
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables.otBase import OTTableWriter

parser = ArgumentParser(prog='varLib.varStore')
parser = ArgumentParser(prog='varLib.varStore', description= main.__doc__)
parser.add_argument('fontfile')
parser.add_argument('outfile', nargs='?')
options = parser.parse_args(args)
Expand Down

0 comments on commit 089f24d

Please sign in to comment.