215 lines
7.4 KiB
Python
215 lines
7.4 KiB
Python
|
|
"""Command-line interface to WeasyPrint."""
|
|||
|
|
|
|||
|
|
import argparse
|
|||
|
|
import logging
|
|||
|
|
import platform
|
|||
|
|
import sys
|
|||
|
|
from warnings import warn
|
|||
|
|
|
|||
|
|
import pydyf
|
|||
|
|
|
|||
|
|
from . import DEFAULT_OPTIONS, HTML, LOGGER, __version__
|
|||
|
|
from .pdf import VARIANTS
|
|||
|
|
from .text.ffi import pango
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PrintInfo(argparse.Action):
|
|||
|
|
def __call__(*_, **__):
|
|||
|
|
uname = platform.uname()
|
|||
|
|
print('System:', uname.system)
|
|||
|
|
print('Machine:', uname.machine)
|
|||
|
|
print('Version:', uname.version)
|
|||
|
|
print('Release:', uname.release)
|
|||
|
|
print()
|
|||
|
|
print('WeasyPrint version:', __version__)
|
|||
|
|
print('Python version:', sys.version.split()[0])
|
|||
|
|
print('Pydyf version:', pydyf.__version__)
|
|||
|
|
print('Pango version:', pango.pango_version())
|
|||
|
|
sys.exit()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Parser(argparse.ArgumentParser):
|
|||
|
|
def __init__(self, *args, **kwargs):
|
|||
|
|
self._arguments = {}
|
|||
|
|
super().__init__(*args, **kwargs)
|
|||
|
|
|
|||
|
|
def add_argument(self, *args, **kwargs):
|
|||
|
|
super().add_argument(*args, **kwargs)
|
|||
|
|
key = args[-1].lstrip('-')
|
|||
|
|
kwargs['flags'] = args
|
|||
|
|
kwargs['positional'] = args[-1][0] != '-'
|
|||
|
|
self._arguments[key] = kwargs
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def docstring(self):
|
|||
|
|
self._arguments['help'] = self._arguments.pop('help')
|
|||
|
|
data = []
|
|||
|
|
for key, args in self._arguments.items():
|
|||
|
|
data.append('.. option:: ')
|
|||
|
|
action = args.get('action', 'store')
|
|||
|
|
for flag in args['flags']:
|
|||
|
|
data.append(flag)
|
|||
|
|
if not args['positional'] and action in ('store', 'append'):
|
|||
|
|
data.append(f' <{key}>')
|
|||
|
|
data.append(', ')
|
|||
|
|
data[-1] = '\n\n'
|
|||
|
|
data.append(f' {args["help"][0].upper()}{args["help"][1:]}.\n\n')
|
|||
|
|
if 'choices' in args:
|
|||
|
|
choices = ", ".join(args['choices'])
|
|||
|
|
data.append(f' Possible choices: {choices}.\n\n')
|
|||
|
|
if action == 'append':
|
|||
|
|
data.append(' This option can be passed multiple times.\n\n')
|
|||
|
|
return ''.join(data)
|
|||
|
|
|
|||
|
|
|
|||
|
|
PARSER = Parser(
|
|||
|
|
prog='weasyprint', description='Render web pages to PDF.')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'input', help='URL or filename of the HTML input, or - for stdin')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'output', help='filename where output is written, or - for stdout')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-e', '--encoding', help='force the input character encoding')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-s', '--stylesheet', action='append', dest='stylesheets',
|
|||
|
|
help='URL or filename for a user CSS stylesheet')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-m', '--media-type',
|
|||
|
|
help='media type to use for @media, defaults to print')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-u', '--base-url',
|
|||
|
|
help='base for relative URLs in the HTML input, defaults to the '
|
|||
|
|
'input’s own filename or URL or the current directory for stdin')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-a', '--attachment', action='append', dest='attachments',
|
|||
|
|
help='URL or filename of a file to attach to the PDF document')
|
|||
|
|
PARSER.add_argument('--pdf-identifier', help='PDF file identifier')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--pdf-variant', choices=VARIANTS, help='PDF variant to generate')
|
|||
|
|
PARSER.add_argument('--pdf-version', help='PDF version number')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--pdf-forms', action='store_true', help='include PDF forms')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--uncompressed-pdf', action='store_true',
|
|||
|
|
help='do not compress PDF content, mainly for debugging purpose')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--custom-metadata', action='store_true',
|
|||
|
|
help='include custom HTML meta tags in PDF metadata')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-p', '--presentational-hints', action='store_true',
|
|||
|
|
help='follow HTML presentational hints')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--optimize-images', action='store_true',
|
|||
|
|
help='optimize size of embedded images with no quality loss')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-j', '--jpeg-quality', type=int,
|
|||
|
|
help='JPEG quality between 0 (worst) to 95 (best)')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--full-fonts', action='store_true',
|
|||
|
|
help='embed unmodified font files when possible')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--hinting', action='store_true',
|
|||
|
|
help='keep hinting information in embedded fonts')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-c', '--cache-folder', dest='cache',
|
|||
|
|
help='store cache on disk instead of memory, folder is '
|
|||
|
|
'created if needed and cleaned after the PDF is generated')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-D', '--dpi', type=int,
|
|||
|
|
help='set maximum resolution of images embedded in the PDF')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-v', '--verbose', action='store_true',
|
|||
|
|
help='show warnings and information messages')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-d', '--debug', action='store_true', help='show debugging messages')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-q', '--quiet', action='store_true', help='hide logging messages')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'--version', action='version',
|
|||
|
|
version=f'WeasyPrint version {__version__}',
|
|||
|
|
help='print WeasyPrint’s version number and exit')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-i', '--info', action=PrintInfo, nargs=0,
|
|||
|
|
help='print system information and exit')
|
|||
|
|
PARSER.add_argument(
|
|||
|
|
'-O', '--optimize-size', action='append',
|
|||
|
|
help='deprecated, use other options instead',
|
|||
|
|
choices=('images', 'fonts', 'hinting', 'pdf', 'all', 'none'))
|
|||
|
|
PARSER.set_defaults(**DEFAULT_OPTIONS)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main(argv=None, stdout=None, stdin=None, HTML=HTML):
|
|||
|
|
"""The ``weasyprint`` program takes at least two arguments:
|
|||
|
|
|
|||
|
|
.. code-block:: sh
|
|||
|
|
|
|||
|
|
weasyprint [options] <input> <output>
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
args = PARSER.parse_args(argv)
|
|||
|
|
|
|||
|
|
if args.input == '-':
|
|||
|
|
source = stdin or sys.stdin.buffer
|
|||
|
|
if args.base_url is None:
|
|||
|
|
args.base_url = '.' # current directory
|
|||
|
|
elif args.base_url == '':
|
|||
|
|
args.base_url = None # no base URL
|
|||
|
|
else:
|
|||
|
|
source = args.input
|
|||
|
|
|
|||
|
|
if args.output == '-':
|
|||
|
|
output = stdout or sys.stdout.buffer
|
|||
|
|
else:
|
|||
|
|
output = args.output
|
|||
|
|
|
|||
|
|
# TODO: to be removed when --optimize-size is removed
|
|||
|
|
optimize_size = {'fonts', 'hinting', 'pdf'}
|
|||
|
|
if args.optimize_size is not None:
|
|||
|
|
warn(
|
|||
|
|
'The --optimize-size option is now deprecated '
|
|||
|
|
'and will be removed in next version. '
|
|||
|
|
'Please use the other options available in --help instead.',
|
|||
|
|
category=FutureWarning)
|
|||
|
|
for arg in args.optimize_size:
|
|||
|
|
if arg == 'none':
|
|||
|
|
optimize_size.clear()
|
|||
|
|
elif arg == 'all':
|
|||
|
|
optimize_size |= {'images', 'fonts', 'hinting', 'pdf'}
|
|||
|
|
else:
|
|||
|
|
optimize_size.add(arg)
|
|||
|
|
del args.optimize_size
|
|||
|
|
|
|||
|
|
options = vars(args)
|
|||
|
|
|
|||
|
|
# TODO: to be removed when --optimize-size is removed
|
|||
|
|
if 'images' in optimize_size:
|
|||
|
|
options['optimize_images'] = True
|
|||
|
|
if 'fonts' not in optimize_size:
|
|||
|
|
options['full_fonts'] = True
|
|||
|
|
if 'hinting' not in optimize_size:
|
|||
|
|
options['hinting'] = True
|
|||
|
|
if 'pdf' not in optimize_size:
|
|||
|
|
options['uncompressed_pdf'] = True
|
|||
|
|
|
|||
|
|
# Default to logging to stderr.
|
|||
|
|
if args.debug:
|
|||
|
|
LOGGER.setLevel(logging.DEBUG)
|
|||
|
|
elif args.verbose:
|
|||
|
|
LOGGER.setLevel(logging.INFO)
|
|||
|
|
if not args.quiet:
|
|||
|
|
handler = logging.StreamHandler()
|
|||
|
|
handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
|
|||
|
|
LOGGER.addHandler(handler)
|
|||
|
|
|
|||
|
|
html = HTML(
|
|||
|
|
source, base_url=args.base_url, encoding=args.encoding,
|
|||
|
|
media_type=args.media_type)
|
|||
|
|
html.write_pdf(output, **options)
|
|||
|
|
|
|||
|
|
|
|||
|
|
main.__doc__ += '\n\n' + PARSER.docstring
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == '__main__': # pragma: no cover
|
|||
|
|
main()
|