Files
icnsutil/cli.py
2021-09-27 20:49:28 +02:00

169 lines
5.7 KiB
Python
Executable File

#!/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(
'File "{}" already exists. Force overwrite with -f.'.format(dest),
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 = '''
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:
template, selected, dark
'''
# 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()