pre-release
This commit is contained in:
167
cli.py
Executable file
167
cli.py
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
Export existing icns files or compose new ones.
|
||||
'''
|
||||
import os # path, mkdir
|
||||
import icnsutil
|
||||
from sys import stderr
|
||||
from argparse import ArgumentParser, ArgumentTypeError, RawTextHelpFormatter
|
||||
|
||||
__version__ = icnsutil.__version__
|
||||
|
||||
|
||||
def cli_extract(args):
|
||||
''' Read and extract contents of icns file(s). '''
|
||||
multiple = len(args.file) > 1
|
||||
for i, fname in enumerate(args.file):
|
||||
# PathExist ensures that all files and directories exist
|
||||
out = args.export_dir
|
||||
if out and multiple:
|
||||
out = os.path.join(out, str(i))
|
||||
os.makedirs(out, exist_ok=True)
|
||||
|
||||
pred = 'png' if args.png_only else None
|
||||
icnsutil.IcnsFile(fname).export(
|
||||
out, allowed_ext=pred, recursive=args.recursive,
|
||||
convert_png=args.convert, key_suffix=args.keys)
|
||||
|
||||
|
||||
def cli_compose(args):
|
||||
''' Create new icns file from provided image files. '''
|
||||
dest = args.target
|
||||
if not os.path.splitext(dest)[1]:
|
||||
dest += '.icns' # for the lazy people
|
||||
if not args.force and os.path.exists(dest):
|
||||
print(f'File "{dest}" already exists. Force overwrite with -f.',
|
||||
file=stderr)
|
||||
return 1
|
||||
img = icnsutil.IcnsFile()
|
||||
for x in args.source:
|
||||
img.add_media(file=x)
|
||||
img.write(dest, toc=not args.no_toc)
|
||||
|
||||
|
||||
def cli_print(args):
|
||||
''' Print contents of icns file(s). '''
|
||||
for fname in args.file:
|
||||
print('File:', fname)
|
||||
print(icnsutil.IcnsFile.description(
|
||||
fname, verbose=args.verbose, indent=2))
|
||||
|
||||
|
||||
def cli_verify(args):
|
||||
''' Test if icns file is valid. '''
|
||||
for fname in args.file:
|
||||
is_valid = True
|
||||
if not args.quiet:
|
||||
print('File:', fname)
|
||||
is_valid = None
|
||||
for issue in icnsutil.IcnsFile.verify(fname):
|
||||
if is_valid:
|
||||
print('File:', fname)
|
||||
is_valid = False
|
||||
print(' ', issue)
|
||||
if not args.quiet and is_valid is not False:
|
||||
print('OK')
|
||||
|
||||
|
||||
def main():
|
||||
class PathExist:
|
||||
def __init__(self, kind=None):
|
||||
self.kind = kind
|
||||
|
||||
def __call__(self, path):
|
||||
if not os.path.exists(path) or \
|
||||
self.kind == 'f' and not os.path.isfile(path) or \
|
||||
self.kind == 'd' and not os.path.isdir(path):
|
||||
raise ArgumentTypeError('Does not exist "{}"'.format(path))
|
||||
return path
|
||||
|
||||
# Args Parser
|
||||
parser = ArgumentParser(description=__doc__,
|
||||
formatter_class=RawTextHelpFormatter)
|
||||
parser.set_defaults(func=lambda _: parser.print_help(stderr))
|
||||
parser.add_argument(
|
||||
'-v', '--version', action='version', version='icnsutil ' + __version__)
|
||||
sub_parser = parser.add_subparsers(metavar='command')
|
||||
|
||||
# Extract
|
||||
cmd = sub_parser.add_parser(
|
||||
'extract', aliases=['e'], formatter_class=RawTextHelpFormatter,
|
||||
help=cli_extract.__doc__, description=cli_extract.__doc__.strip())
|
||||
cmd.add_argument(
|
||||
'-r', '--recursive', action='store_true',
|
||||
help='extract nested icns files as well')
|
||||
cmd.add_argument(
|
||||
'-o', '--export-dir', type=PathExist('d'), metavar='DIR',
|
||||
help='set custom export directory')
|
||||
cmd.add_argument(
|
||||
'-k', '--keys', action='store_true',
|
||||
help='use icns key as filenam')
|
||||
cmd.add_argument(
|
||||
'-c', '--convert', action='store_true',
|
||||
help='convert ARGB and RGB images to PNG')
|
||||
cmd.add_argument(
|
||||
'--png-only', action='store_true',
|
||||
help='do not extract ARGB, binary, and meta files')
|
||||
cmd.add_argument(
|
||||
'file', nargs='+', type=PathExist('f'), metavar='FILE',
|
||||
help='One or more .icns files.')
|
||||
cmd.set_defaults(func=cli_extract)
|
||||
|
||||
# Compose
|
||||
cmd = sub_parser.add_parser(
|
||||
'compose', aliases=['c'], formatter_class=RawTextHelpFormatter,
|
||||
help=cli_compose.__doc__, description=cli_compose.__doc__.strip())
|
||||
cmd.add_argument(
|
||||
'-f', '--force', action='store_true',
|
||||
help='force overwrite output file')
|
||||
cmd.add_argument(
|
||||
'--no-toc', action='store_true',
|
||||
help='do not write table of contents to file')
|
||||
cmd.add_argument(
|
||||
'target', type=str, metavar='destination',
|
||||
help='Output file for newly created icns file.')
|
||||
cmd.add_argument(
|
||||
'source', nargs='+', type=PathExist('f'), metavar='src',
|
||||
help='One or more media files: png, argb, plist, icns.')
|
||||
cmd.set_defaults(func=cli_compose)
|
||||
cmd.epilog = f'''
|
||||
Notes:
|
||||
- TOC is optional but only a few bytes long (8b per media entry).
|
||||
- Icon dimensions are read directly from file.
|
||||
- Filename suffix "@2x.png" or "@2x.jp2" sets the retina flag.
|
||||
- Use one of these suffixes to automatically assign icns files:
|
||||
{', '.join(f'{x.name.lower()}.icns' for x in icnsutil.IcnsType.Role)}
|
||||
'''
|
||||
|
||||
# Print
|
||||
cmd = sub_parser.add_parser(
|
||||
'print', aliases=['p'], formatter_class=RawTextHelpFormatter,
|
||||
help=cli_print.__doc__, description=cli_print.__doc__.strip())
|
||||
cmd.add_argument(
|
||||
'-v', '--verbose', action='store_true',
|
||||
help='print all keys with offsets and sizes')
|
||||
cmd.add_argument(
|
||||
'file', nargs='+', type=PathExist('f'), metavar='FILE',
|
||||
help='One or more .icns files.')
|
||||
cmd.set_defaults(func=cli_print)
|
||||
|
||||
# Verify
|
||||
cmd = sub_parser.add_parser(
|
||||
'test', aliases=['t'], formatter_class=RawTextHelpFormatter,
|
||||
help=cli_verify.__doc__, description=cli_verify.__doc__.strip())
|
||||
cmd.add_argument(
|
||||
'-q', '--quiet', action='store_true',
|
||||
help='do not print OK results')
|
||||
cmd.add_argument(
|
||||
'file', nargs='+', type=PathExist('f'), metavar='FILE',
|
||||
help='One or more .icns files.')
|
||||
cmd.set_defaults(func=cli_verify)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user