From b30c104b4db93d7f12ade018473c57779ac2a77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20D=C3=B6rner?= Date: Sat, 3 Jan 2015 18:08:59 +0100 Subject: [PATCH 01/97] Changed order of TOC and cover so now cover is added to the command args before TOC --- pdfkit/api.py | 18 +++++++++--------- pdfkit/pdfkit.py | 10 +++++----- tests/pdfkit-tests.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) mode change 100644 => 100755 pdfkit/api.py mode change 100644 => 100755 pdfkit/pdfkit.py mode change 100644 => 100755 tests/pdfkit-tests.py diff --git a/pdfkit/api.py b/pdfkit/api.py old mode 100644 new mode 100755 index 5d738b1..31cc7a8 --- a/pdfkit/api.py +++ b/pdfkit/api.py @@ -4,27 +4,27 @@ from .pdfkit import Configuration -def from_url(url, output_path, options=None, toc=None, cover=None, configuration=None): +def from_url(url, output_path, options=None, cover=None, toc=None, configuration=None): """ Convert file of files from URLs to PDF document :param url: URL or list of URLs to be saved :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf global and page options, with or w/o '--' - :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page + :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param configuration: (optional) instance of pdfkit.configuration.Configuration() Returns: True on success """ - r = PDFKit(url, 'url', options=options, toc=toc, cover=cover, + r = PDFKit(url, 'url', options=options, cover=cover, toc=toc, configuration=configuration) return r.to_pdf(output_path) -def from_file(input, output_path, options=None, toc=None, cover=None, css=None, +def from_file(input, output_path, options=None, cover=None, toc=None, css=None, configuration=None): """ Convert HTML file or files to PDF document @@ -32,21 +32,21 @@ def from_file(input, output_path, options=None, toc=None, cover=None, css=None, :param input: path to HTML file or list with paths or file-like object :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' - :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page + :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param css: (optional) string with path to css file which will be added to a single input file :param configuration: (optional) instance of pdfkit.configuration.Configuration() Returns: True on success """ - r = PDFKit(input, 'file', options=options, toc=toc, cover=cover, css=css, + r = PDFKit(input, 'file', options=options, cover=cover, toc=toc, css=css, configuration=configuration) return r.to_pdf(output_path) -def from_string(input, output_path, options=None, toc=None, cover=None, css=None, +def from_string(input, output_path, options=None, cover=None, toc=None, css=None, configuration=None): """ Convert given string or strings to PDF document @@ -54,15 +54,15 @@ def from_string(input, output_path, options=None, toc=None, cover=None, css=None :param input: string with a desired text. Could be a raw text or a html file :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' - :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page + :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param css: (optional) string with path to css file which will be added to a input string :param configuration: (optional) instance of pdfkit.configuration.Configuration() Returns: True on success """ - r = PDFKit(input, 'string', options=options, toc=toc, cover=cover, css=css, + r = PDFKit(input, 'string', options=options, cover=cover, toc=toc, css=css, configuration=configuration) return r.to_pdf(output_path) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py old mode 100644 new mode 100755 index 3591d5b..264cbd8 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -31,7 +31,7 @@ def __init__(self, msg): def __str__(self): return self.msg - def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, + def __init__(self, url_or_file, type_, options=None, cover=None, toc=None, css=None, configuration=None): self.source = Source(url_or_file, type_) @@ -45,9 +45,9 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, if options is not None: self.options.update(options) self.options = self._normalize_options(self.options) + self.cover = cover toc = {} if toc is None else toc self.toc = self._normalize_options(toc) - self.cover = cover self.css = css self.stylesheets = [] @@ -60,12 +60,12 @@ def command(self, path=None): args += list(chain.from_iterable(list(self.options.items()))) args = [_f for _f in args if _f] - if self.toc: - args.append('toc') - args += list(chain.from_iterable(list(self.toc.items()))) if self.cover: args.append('cover') args.append(self.cover) + if self.toc: + args.append('toc') + args += list(chain.from_iterable(list(self.toc.items()))) # If the source is a string then we will pipe it into wkhtmltopdf # If the source is file-like then we will read from it and pipe it in diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py old mode 100644 new mode 100755 index 9b72ade..a012c33 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -190,7 +190,7 @@ def test_cover_and_toc(self): } r = pdfkit.PDFKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}, cover='test.html') command = r.command() - self.assertEqual(command[-7:], ['toc', '--xsl-style-sheet', 'test.xsl', 'cover', 'test.html', '-', '-']) + self.assertEqual(command[-7:], ['cover', 'test.html', 'toc', '--xsl-style-sheet', 'test.xsl', '-', '-']) def test_outline_options(self): options = { From de9f7f3cc0ec7150a6b5bc69cd04f33985c4679a Mon Sep 17 00:00:00 2001 From: Manraj Singh Date: Tue, 6 Oct 2015 19:55:53 +0530 Subject: [PATCH 02/97] Removed extra bracket from documentation --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 96c0cdf..0c49f07 100644 --- a/README.rst +++ b/README.rst @@ -147,7 +147,7 @@ Example - for when ``wkhtmltopdf`` is not on ``$PATH``: .. code-block:: python - config = pdfkit.configuration(wkhtmltopdf='/opt/bin/wkhtmltopdf')) + config = pdfkit.configuration(wkhtmltopdf='/opt/bin/wkhtmltopdf') pdfkit.from_string(html_string, output_file, configuration=config) From 8fca4898f944f30e50c0a4bc8662aad5da6dc5a0 Mon Sep 17 00:00:00 2001 From: Hung Le Date: Sat, 31 Oct 2015 23:36:12 +0700 Subject: [PATCH 03/97] Allow repeatable options and multiple option values --- pdfkit/pdfkit.py | 75 ++++++++++++++++++++++++++++++------------- tests/pdfkit-tests.py | 9 ++++-- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 3591d5b..15dbafd 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -42,49 +42,73 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, self.options = dict() if self.source.isString(): self.options.update(self._find_options_in_meta(url_or_file)) + if options is not None: self.options.update(options) - self.options = self._normalize_options(self.options) - toc = {} if toc is None else toc - self.toc = self._normalize_options(toc) + self.toc = {} if toc is None else toc self.cover = cover self.css = css self.stylesheets = [] - def command(self, path=None): + def _genargs(self, opts): + """ + Generator of args parts based on options specification. + + Note: Empty parts will be filtered out at _command generator + """ + for optkey, optval in self._normalize_options(opts): + yield optkey + + if isinstance(optval, (list, tuple)): + assert len(optval) == 2 and optval[0] and optval[1], 'Option value can only be either a string or a (tuple, list) of 2 items' + yield optval[0] + yield optval[1] + else: + yield optval + + def _command(self, path=None): + """ + Generator of all command parts + """ if self.css: self._prepend_css(self.css) - args = [self.wkhtmltopdf] + yield self.wkhtmltopdf - args += list(chain.from_iterable(list(self.options.items()))) - args = [_f for _f in args if _f] + for argpart in self._genargs(self.options): + if argpart: + yield argpart if self.toc: - args.append('toc') - args += list(chain.from_iterable(list(self.toc.items()))) + yield 'toc' + for argpart in self._genargs(self.toc): + if argpart: + yield argpart + if self.cover: - args.append('cover') - args.append(self.cover) + yield 'cover' + yield self.cover # If the source is a string then we will pipe it into wkhtmltopdf # If the source is file-like then we will read from it and pipe it in if self.source.isString() or self.source.isFileObj(): - args.append('-') + yield '-' else: if isinstance(self.source.source, str): - args.append(self.source.to_s()) + yield self.source.to_s() else: - args += self.source.source + for s in self.source.source: + yield s # If output_path evaluates to False append '-' to end of args # and wkhtmltopdf will pass generated PDF to stdout if path: - args.append(path) + yield path else: - args.append('-') + yield '-' - return args + def command(self, path=None): + return list(self._command(path)) def to_pdf(self, path=None): args = self.command(path) @@ -108,7 +132,7 @@ def to_pdf(self, path=None): if 'cannot connect to X server' in stderr.decode('utf-8'): raise IOError('%s\n' - 'You will need to run whktmltopdf within a "virutal" X server.\n' + 'You will need to run whktmltopdf within a "virtual" X server.\n' 'Go to the link above for more information\n' 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr.decode('utf-8')) @@ -141,13 +165,15 @@ def to_pdf(self, path=None): ' '.join(args)) def _normalize_options(self, options): - """Updates a dict of config options to make then usable on command line + """ Generator of 2-tuples (option-key, option-value). + When options spec is a list, generate a 2-tuples per list item. :param options: dict {option name: value} returns: - dict: {option name: value} - option names lower cased and prepended with - '--' if necessary. Non-empty values cast to str + iterator (option-key, option-value) + - option names lower cased and prepended with + '--' if necessary. Non-empty values cast to str """ normalized_options = {} @@ -156,9 +182,12 @@ def _normalize_options(self, options): normalized_key = '--%s' % self._normalize_arg(key) else: normalized_key = self._normalize_arg(key) - normalized_options[normalized_key] = str(value) if value else value + if isinstance(value, (list, tuple)): + for optval in value: + yield (normalized_key, optval) + + yield (normalized_key, str(value) if value else value) - return normalized_options def _normalize_arg(self, arg): return arg.lower() diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 9b72ade..ee5b7cc 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -42,11 +42,16 @@ def test_file_source_with_path(self): def test_options_parsing(self): r = pdfkit.PDFKit('html', 'string', options={'page-size': 'Letter'}) - self.assertTrue(r.options['--page-size']) + test_command = r.command('test') + idx = test_command.index('--page-size') # Raise exception in case of not found + self.assertTrue(test_command[idx+1] == 'Letter') def test_options_parsing_with_dashes(self): r = pdfkit.PDFKit('html', 'string', options={'--page-size': 'Letter'}) - self.assertTrue(r.options['--page-size']) + + test_command = r.command('test') + idx = test_command.index('--page-size') # Raise exception in case of not found + self.assertTrue(test_command[idx+1] == 'Letter') def test_custom_configuration(self): conf = pdfkit.configuration() From 854f3d8f43842f4828e3d94f6b1c16dcc1341d3e Mon Sep 17 00:00:00 2001 From: Hung Le Date: Sat, 31 Oct 2015 23:49:02 +0700 Subject: [PATCH 04/97] Fixed duplicated options generation for lists and added respective tests --- pdfkit/pdfkit.py | 5 +++-- tests/pdfkit-tests.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 15dbafd..4d8a255 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -182,11 +182,12 @@ def _normalize_options(self, options): normalized_key = '--%s' % self._normalize_arg(key) else: normalized_key = self._normalize_arg(key) + if isinstance(value, (list, tuple)): for optval in value: yield (normalized_key, optval) - - yield (normalized_key, str(value) if value else value) + else: + yield (normalized_key, str(value) if value else value) def _normalize_arg(self, arg): diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index ee5b7cc..31b907a 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -48,11 +48,40 @@ def test_options_parsing(self): def test_options_parsing_with_dashes(self): r = pdfkit.PDFKit('html', 'string', options={'--page-size': 'Letter'}) - + test_command = r.command('test') idx = test_command.index('--page-size') # Raise exception in case of not found self.assertTrue(test_command[idx+1] == 'Letter') + def test_repeatable_options(self): + roptions={ + '--page-size': 'Letter', + 'cookies': [ + ('test_cookie1','cookie_value1'), + ('test_cookie2','cookie_value2'), + ] + } + + r = pdfkit.PDFKit('html', 'string', options=roptions) + + test_command = r.command('test') + + idx1 = test_command.index('--page-size') # Raise exception in case of not found + self.assertTrue(test_command[idx1 + 1] == 'Letter') + + self.assertTrue(test_command.count('--cookies') == 2) + + idx2 = test_command.index('--cookies') + self.assertTrue(test_command[idx2 + 1] == 'test_cookie1') + self.assertTrue(test_command[idx2 + 2] == 'cookie_value1') + + idx3 = test_command.index('--cookies', idx2 + 2) + self.assertTrue(test_command[idx3 + 1] == 'test_cookie2') + self.assertTrue(test_command[idx3 + 2] == 'cookie_value2') + + + + def test_custom_configuration(self): conf = pdfkit.configuration() self.assertEqual('pdfkit-', conf.meta_tag_prefix) From 48dba3d1de5a067f30f6a4a7b779d028837139b7 Mon Sep 17 00:00:00 2001 From: Hung Le Date: Sat, 31 Oct 2015 23:55:08 +0700 Subject: [PATCH 05/97] Added repeatable option documents --- README.rst | 6 +++++- tests/pdfkit-tests.py | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 96c0cdf..978f906 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,7 @@ If you wish to further process generated PDF, you can read it to a variable: # Use False instead of output path to save pdf to a variable pdf = pdfkit.from_url('http://google.com', False) -You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value: +You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookies, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple. .. code-block:: python @@ -78,6 +78,10 @@ You can specify all wkhtmltopdf `options Date: Tue, 16 Feb 2016 17:50:06 -0500 Subject: [PATCH 06/97] Catch AttributeError raised by decode in python3.5 --- pdfkit/pdfkit.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 3591d5b..677519b 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -37,7 +37,10 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, self.source = Source(url_or_file, type_) self.configuration = (Configuration() if configuration is None else configuration) - self.wkhtmltopdf = self.configuration.wkhtmltopdf.decode('utf-8') + try: + self.wkhtmltopdf = self.configuration.wkhtmltopdf.decode('utf-8') + except AttributeError: + self.wkhtmltopdf = self.configuration.wkhtmltopdf self.options = dict() if self.source.isString(): From 399586332505a4351dd947a7133e386a0c37ab8e Mon Sep 17 00:00:00 2001 From: Pietro Delsante Date: Thu, 4 Aug 2016 15:36:30 +0200 Subject: [PATCH 07/97] Adding option to prepend cover to toc --- pdfkit/api.py | 16 ++++++++++------ pdfkit/pdfkit.py | 8 ++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pdfkit/api.py b/pdfkit/api.py index 5d738b1..6520bf8 100644 --- a/pdfkit/api.py +++ b/pdfkit/api.py @@ -4,7 +4,8 @@ from .pdfkit import Configuration -def from_url(url, output_path, options=None, toc=None, cover=None, configuration=None): +def from_url(url, output_path, options=None, toc=None, cover=None, + configuration=None, cover_first=False): """ Convert file of files from URLs to PDF document @@ -14,18 +15,19 @@ def from_url(url, output_path, options=None, toc=None, cover=None, configuration :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page :param configuration: (optional) instance of pdfkit.configuration.Configuration() + :param configuration_first: (optional) if True, cover always precedes TOC Returns: True on success """ r = PDFKit(url, 'url', options=options, toc=toc, cover=cover, - configuration=configuration) + configuration=configuration, cover_first=cover_first) return r.to_pdf(output_path) def from_file(input, output_path, options=None, toc=None, cover=None, css=None, - configuration=None): + configuration=None, cover_first=False): """ Convert HTML file or files to PDF document @@ -36,18 +38,19 @@ def from_file(input, output_path, options=None, toc=None, cover=None, css=None, :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a single input file :param configuration: (optional) instance of pdfkit.configuration.Configuration() + :param configuration_first: (optional) if True, cover always precedes TOC Returns: True on success """ r = PDFKit(input, 'file', options=options, toc=toc, cover=cover, css=css, - configuration=configuration) + configuration=configuration, cover_first=cover_first) return r.to_pdf(output_path) def from_string(input, output_path, options=None, toc=None, cover=None, css=None, - configuration=None): + configuration=None, cover_first=False): """ Convert given string or strings to PDF document @@ -58,12 +61,13 @@ def from_string(input, output_path, options=None, toc=None, cover=None, css=None :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a input string :param configuration: (optional) instance of pdfkit.configuration.Configuration() + :param configuration_first: (optional) if True, cover always precedes TOC Returns: True on success """ r = PDFKit(input, 'string', options=options, toc=toc, cover=cover, css=css, - configuration=configuration) + configuration=configuration, cover_first=cover_first) return r.to_pdf(output_path) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 3591d5b..201dfcd 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -32,7 +32,7 @@ def __str__(self): return self.msg def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, - css=None, configuration=None): + css=None, configuration=None, cover_first=False): self.source = Source(url_or_file, type_) self.configuration = (Configuration() if configuration is None @@ -48,6 +48,7 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, toc = {} if toc is None else toc self.toc = self._normalize_options(toc) self.cover = cover + self.cover_first = cover_first self.css = css self.stylesheets = [] @@ -60,10 +61,13 @@ def command(self, path=None): args += list(chain.from_iterable(list(self.options.items()))) args = [_f for _f in args if _f] + if self.cover and self.cover_first: + args.append('cover') + args.append(self.cover) if self.toc: args.append('toc') args += list(chain.from_iterable(list(self.toc.items()))) - if self.cover: + if self.cover and not self.cover_first: args.append('cover') args.append(self.cover) From bf34e404ef2554dae843cfd8a9856a921b13bfd7 Mon Sep 17 00:00:00 2001 From: Brandon Wood Date: Wed, 31 Aug 2016 10:21:33 -0500 Subject: [PATCH 08/97] Test for passing tuples as arguments for options --- tests/pdfkit-tests.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 9b72ade..e5e300c 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -48,6 +48,19 @@ def test_options_parsing_with_dashes(self): r = pdfkit.PDFKit('html', 'string', options={'--page-size': 'Letter'}) self.assertTrue(r.options['--page-size']) + def test_options_parsing_with_tuple(self): + r = pdfkit.PDFKit('html', 'string', options={'--custom-header': + ('Accept-Encoding', + 'gzip')}) + self.assertTrue(r.options['--custom-header']) + r = pdfkit.PDFKit('html', 'string', options={'custom-header': + ('Accept-Encoding', + 'gzip')}) + self.assertTrue(r.options['--custom-header']) + self.assertTrue("--custom-header" in r.command()) + self.assertTrue("Accept-Encoding" in r.command()) + self.assertTrue("gzip" in r.command()) + def test_custom_configuration(self): conf = pdfkit.configuration() self.assertEqual('pdfkit-', conf.meta_tag_prefix) From bddcf6752064f7fb386016324b862af4a76e8c53 Mon Sep 17 00:00:00 2001 From: Brandon Wood Date: Wed, 31 Aug 2016 10:25:52 -0500 Subject: [PATCH 09/97] Added support for tuple values in options This allows for some of the flags for wkhtmltopdf that require two arguments to be called correctly. --- pdfkit/pdfkit.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 3591d5b..a33a5b0 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -60,6 +60,23 @@ def command(self, path=None): args += list(chain.from_iterable(list(self.options.items()))) args = [_f for _f in args if _f] + # Because certain flags require two separate arguments (e.g. + # ``--custom-header``), we need to flatten those arguments so that they + # are passed correctly to wkhtmltopdf + # + # .. seealso:: + # + # JazzCore/python-pdfkit#45 + # JazzCore/python-pdfkit#53 + # + flat_args = [] + for arg in args: + if isinstance(arg, basestring): + flat_args.append(arg) + else: + flat_args.extend(arg) + args = flat_args + if self.toc: args.append('toc') args += list(chain.from_iterable(list(self.toc.items()))) @@ -156,7 +173,13 @@ def _normalize_options(self, options): normalized_key = '--%s' % self._normalize_arg(key) else: normalized_key = self._normalize_arg(key) - normalized_options[normalized_key] = str(value) if value else value + if not isinstance(value, tuple): + normalized_options[normalized_key] = str(value) if value else value + else: + # Convert tuple values to list of strings to later be + # flattened in :func:`pdfkit.PDFKit.command` + normalized_options[normalized_key] = [str(v) if v else v for v + in value] return normalized_options From 68cf0b93a952c9ac166bab6c76ede1f48aeec600 Mon Sep 17 00:00:00 2001 From: Brandon Wood Date: Wed, 31 Aug 2016 10:27:00 -0500 Subject: [PATCH 10/97] Added cache, egg files to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index c10666e..bd84057 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ .idea *.pyc + +.cache +.eggs +pdfkit.egg-info From ad1f3796e374adafcf5c2e36a6de45445e645942 Mon Sep 17 00:00:00 2001 From: Brandon Wood Date: Wed, 31 Aug 2016 10:41:09 -0500 Subject: [PATCH 11/97] Init tox config for multi-Python version testing --- .gitignore | 5 +++++ tox.ini | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index bd84057..3faf7f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ .idea *.pyc +# Build .cache .eggs pdfkit.egg-info + +# Tests +.tox +.python-version diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..61a0a83 --- /dev/null +++ b/tox.ini @@ -0,0 +1,6 @@ +[tox] +envlist = py27,py33,py34,py35 + +[testenv] +deps = pytest +commands = python setup.py test From aea15ea179000f4ba09cdc068b8cf0ecba61db51 Mon Sep 17 00:00:00 2001 From: Brandon Wood Date: Wed, 31 Aug 2016 10:41:46 -0500 Subject: [PATCH 12/97] Python 3 compatibility for checking of string type --- pdfkit/pdfkit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index a33a5b0..c0670da 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -7,6 +7,11 @@ from itertools import chain import io import codecs +try: + # Python 2.x and 3.x support for checking string types + assert basestring +except NameError: + basestring = str class PDFKit(object): From 53eefe7f18beff518c29464d54b4dfb6ca25d151 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 16:28:28 +0300 Subject: [PATCH 13/97] Revert "Changed order of TOC and cover so now cover is added to the command args before TOC" This reverts commit b30c104b4db93d7f12ade018473c57779ac2a77e. --- pdfkit/api.py | 18 +++++++++--------- pdfkit/pdfkit.py | 10 +++++----- tests/pdfkit-tests.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) mode change 100755 => 100644 pdfkit/api.py mode change 100755 => 100644 pdfkit/pdfkit.py mode change 100755 => 100644 tests/pdfkit-tests.py diff --git a/pdfkit/api.py b/pdfkit/api.py old mode 100755 new mode 100644 index 31cc7a8..5d738b1 --- a/pdfkit/api.py +++ b/pdfkit/api.py @@ -4,27 +4,27 @@ from .pdfkit import Configuration -def from_url(url, output_path, options=None, cover=None, toc=None, configuration=None): +def from_url(url, output_path, options=None, toc=None, cover=None, configuration=None): """ Convert file of files from URLs to PDF document :param url: URL or list of URLs to be saved :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf global and page options, with or w/o '--' - :param cover: (optional) string with url/filename with a cover html page :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' + :param cover: (optional) string with url/filename with a cover html page :param configuration: (optional) instance of pdfkit.configuration.Configuration() Returns: True on success """ - r = PDFKit(url, 'url', options=options, cover=cover, toc=toc, + r = PDFKit(url, 'url', options=options, toc=toc, cover=cover, configuration=configuration) return r.to_pdf(output_path) -def from_file(input, output_path, options=None, cover=None, toc=None, css=None, +def from_file(input, output_path, options=None, toc=None, cover=None, css=None, configuration=None): """ Convert HTML file or files to PDF document @@ -32,21 +32,21 @@ def from_file(input, output_path, options=None, cover=None, toc=None, css=None, :param input: path to HTML file or list with paths or file-like object :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' - :param cover: (optional) string with url/filename with a cover html page :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' + :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a single input file :param configuration: (optional) instance of pdfkit.configuration.Configuration() Returns: True on success """ - r = PDFKit(input, 'file', options=options, cover=cover, toc=toc, css=css, + r = PDFKit(input, 'file', options=options, toc=toc, cover=cover, css=css, configuration=configuration) return r.to_pdf(output_path) -def from_string(input, output_path, options=None, cover=None, toc=None, css=None, +def from_string(input, output_path, options=None, toc=None, cover=None, css=None, configuration=None): """ Convert given string or strings to PDF document @@ -54,15 +54,15 @@ def from_string(input, output_path, options=None, cover=None, toc=None, css=None :param input: string with a desired text. Could be a raw text or a html file :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' - :param cover: (optional) string with url/filename with a cover html page :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' + :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a input string :param configuration: (optional) instance of pdfkit.configuration.Configuration() Returns: True on success """ - r = PDFKit(input, 'string', options=options, cover=cover, toc=toc, css=css, + r = PDFKit(input, 'string', options=options, toc=toc, cover=cover, css=css, configuration=configuration) return r.to_pdf(output_path) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py old mode 100755 new mode 100644 index 264cbd8..3591d5b --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -31,7 +31,7 @@ def __init__(self, msg): def __str__(self): return self.msg - def __init__(self, url_or_file, type_, options=None, cover=None, toc=None, + def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, css=None, configuration=None): self.source = Source(url_or_file, type_) @@ -45,9 +45,9 @@ def __init__(self, url_or_file, type_, options=None, cover=None, toc=None, if options is not None: self.options.update(options) self.options = self._normalize_options(self.options) - self.cover = cover toc = {} if toc is None else toc self.toc = self._normalize_options(toc) + self.cover = cover self.css = css self.stylesheets = [] @@ -60,12 +60,12 @@ def command(self, path=None): args += list(chain.from_iterable(list(self.options.items()))) args = [_f for _f in args if _f] - if self.cover: - args.append('cover') - args.append(self.cover) if self.toc: args.append('toc') args += list(chain.from_iterable(list(self.toc.items()))) + if self.cover: + args.append('cover') + args.append(self.cover) # If the source is a string then we will pipe it into wkhtmltopdf # If the source is file-like then we will read from it and pipe it in diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py old mode 100755 new mode 100644 index a012c33..9b72ade --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -190,7 +190,7 @@ def test_cover_and_toc(self): } r = pdfkit.PDFKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}, cover='test.html') command = r.command() - self.assertEqual(command[-7:], ['cover', 'test.html', 'toc', '--xsl-style-sheet', 'test.xsl', '-', '-']) + self.assertEqual(command[-7:], ['toc', '--xsl-style-sheet', 'test.xsl', 'cover', 'test.html', '-', '-']) def test_outline_options(self): options = { From 6f3ca7cfd40fdaddaa35c91102e26c0dbdaf27d2 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 16:33:44 +0300 Subject: [PATCH 14/97] Add test for cover_first option from #57 --- tests/pdfkit-tests.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 9b72ade..1163cf9 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -192,6 +192,19 @@ def test_cover_and_toc(self): command = r.command() self.assertEqual(command[-7:], ['toc', '--xsl-style-sheet', 'test.xsl', 'cover', 'test.html', '-', '-']) + def test_cover_and_toc_cover_first(self): + options = { + 'page-size': 'Letter', + 'margin-top': '0.75in', + 'margin-right': '0.75in', + 'margin-bottom': '0.75in', + 'margin-left': '0.75in', + 'encoding': "UTF-8" + } + r = pdfkit.PDFKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}, cover='test.html', cover_first=True) + command = r.command() + self.assertEqual(command[-7:], ['cover', 'test.html', 'toc', '--xsl-style-sheet', 'test.xsl', '-', '-']) + def test_outline_options(self): options = { 'outline': None, From 1666682b4a6a5f7ef7b2ba1c555afe24b7b89950 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 16:36:55 +0300 Subject: [PATCH 15/97] Add readme for cover_first option from #57 --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 0c49f07..858c649 100644 --- a/README.rst +++ b/README.rst @@ -93,7 +93,7 @@ By default, PDFKit will show all ``wkhtmltopdf`` output. If you dont want it, yo pdfkit.from_url('google.com', 'out.pdf', options=options) -Due to wkhtmltopdf command syntax, **TOC** and **Cover** options must be specified separately: +Due to wkhtmltopdf command syntax, **TOC** and **Cover** options must be specified separately. If you need cover before TOC, use ``cover_first`` option: .. code-block:: python @@ -104,6 +104,7 @@ Due to wkhtmltopdf command syntax, **TOC** and **Cover** options must be specifi cover = 'cover.html' pdfkit.from_file('file.html', options=options, toc=toc, cover=cover) + pdfkit.from_file('file.html', options=options, toc=toc, cover=cover, cover_first=True) You can specify external CSS files when converting files or strings using *css* option. From 0db6a6084814c7ab6e0827e9aa47b4e2a92c59af Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 18:24:18 +0300 Subject: [PATCH 16/97] Update tests for #40 --- tests/pdfkit-tests.py | 44 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index be5b5da..66e9f8e 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -53,8 +53,32 @@ def test_options_parsing_with_dashes(self): idx = test_command.index('--page-size') # Raise exception in case of not found self.assertTrue(test_command[idx+1] == 'Letter') + def test_options_parsing_with_tuple(self): + options = { + '--custom-header': [ + ('Accept-Encoding','gzip') + ] + } + r = pdfkit.PDFKit('html', 'string', options=options) + command = r.command() + idx1 = command.index('--custom-header') # Raise exception in case of not found + self.assertTrue(command[idx1 + 1] == 'Accept-Encoding') + self.assertTrue(command[idx1 + 2] == 'gzip') + + def test_options_parsing_with_tuple_no_dashes(self): + options = { + 'custom-header': [ + ('Accept-Encoding','gzip') + ] + } + r = pdfkit.PDFKit('html', 'string', options=options) + command = r.command() + idx1 = command.index('--custom-header') # Raise exception in case of not found + self.assertTrue(command[idx1 + 1] == 'Accept-Encoding') + self.assertTrue(command[idx1 + 2] == 'gzip') + def test_repeatable_options(self): - roptions={ + roptions = { '--page-size': 'Letter', 'cookies': [ ('test_cookie1','cookie_value1'), @@ -187,14 +211,18 @@ def test_toc_with_options(self): } r = pdfkit.PDFKit('html', 'string', options=options, toc={'xsl-style-sheet': 'test.xsl'}) - self.assertEqual(r.command()[1 + len(options) * 2], 'toc') - self.assertEqual(r.command()[1 + len(options) * 2 + 1], '--xsl-style-sheet') + command = r.command() + + self.assertEqual(command[1 + len(options) * 2], 'toc') + self.assertEqual(command[1 + len(options) * 2 + 1], '--xsl-style-sheet') def test_cover_without_options(self): r = pdfkit.PDFKit('html', 'string', cover='test.html') - self.assertEqual(r.command()[1], 'cover') - self.assertEqual(r.command()[2], 'test.html') + command = r.command() + + self.assertEqual(command[1], 'cover') + self.assertEqual(command[2], 'test.html') def test_cover_with_options(self): options = { @@ -207,8 +235,10 @@ def test_cover_with_options(self): } r = pdfkit.PDFKit('html', 'string', options=options, cover='test.html') - self.assertEqual(r.command()[1 + len(options) * 2], 'cover') - self.assertEqual(r.command()[1 + len(options) * 2 + 1], 'test.html') + command = r.command() + + self.assertEqual(command[1 + len(options) * 2], 'cover') + self.assertEqual(command[1 + len(options) * 2 + 1], 'test.html') def test_cover_and_toc(self): options = { From 59ce23c37b71ff76535257b4e8108ae80a2bc341 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 18:27:05 +0300 Subject: [PATCH 17/97] Slight README update --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 37dec77..a397161 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,7 @@ If you wish to further process generated PDF, you can read it to a variable: # Use False instead of output path to save pdf to a variable pdf = pdfkit.from_url('http://google.com', False) -You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookies, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple. +You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookies, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple (see example below). .. code-block:: python @@ -78,6 +78,9 @@ You can specify all wkhtmltopdf `options Date: Tue, 6 Sep 2016 18:32:11 +0300 Subject: [PATCH 18/97] remove duplicate test --- tests/pdfkit-tests.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 433ffee..66e9f8e 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -103,19 +103,6 @@ def test_repeatable_options(self): self.assertTrue(test_command[idx3 + 1] == 'test_cookie2') self.assertTrue(test_command[idx3 + 2] == 'cookie_value2') - def test_options_parsing_with_tuple(self): - r = pdfkit.PDFKit('html', 'string', options={'--custom-header': - ('Accept-Encoding', - 'gzip')}) - self.assertTrue(r.options['--custom-header']) - r = pdfkit.PDFKit('html', 'string', options={'custom-header': - ('Accept-Encoding', - 'gzip')}) - self.assertTrue(r.options['--custom-header']) - self.assertTrue("--custom-header" in r.command()) - self.assertTrue("Accept-Encoding" in r.command()) - self.assertTrue("gzip" in r.command()) - def test_custom_configuration(self): conf = pdfkit.configuration() self.assertEqual('pdfkit-', conf.meta_tag_prefix) From 89956729cbbd0164478ad62a31ac94abc1a797fa Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 19:39:46 +0300 Subject: [PATCH 19/97] Fix Travis builds --- travis/before-script.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/travis/before-script.sh b/travis/before-script.sh index bd171e4..026db37 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -1,7 +1,8 @@ #!/bin/sh sudo apt-get install -y openssl build-essential xorg libssl-dev -wget http://wkhtmltopdf.googlecode.com/files/wkhtmltopdf-0.10.0_rc2-static-amd64.tar.bz2 -tar xvjf wkhtmltopdf-0.10.0_rc2-static-amd64.tar.bz2 -sudo chown root:root wkhtmltopdf-amd64 -sudo mv wkhtmltopdf-amd64 /usr/bin/wkhtmltopdf +wget http://download.gna.org/wkhtmltopdf/0.12/0.12.3/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz +tar -xJf wkhtmltox-0.12.3_linux-generic-amd64.tar.xz +cd wkhtmltox +sudo chown root:root bin/wkhtmltopdf +sudo cp -r * /usr/ From 452b9ea1183bc10b45beef7be75d51ed3c4c7e01 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 6 Sep 2016 20:04:10 +0300 Subject: [PATCH 20/97] More Travis fixes --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 15dc4fe..0c5007c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ language: python python: - 2.7 - - 3.2 + - 3.2 - 3.3 + - 3.4 + - 3.5 before_script: "./travis/before-script.sh" -script: python setup.py test +script: nosetests -w tests notifications: email: false From 59bc64da55cb15a0e28c21c7898eeb0f26c7ae79 Mon Sep 17 00:00:00 2001 From: Fasih Ahmad Fakhri Date: Wed, 26 Oct 2016 23:58:43 +0530 Subject: [PATCH 21/97] Appeding IOError messages & updating stderr with proper output --- pdfkit/pdfkit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index a6ca36e..62c175b 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -137,7 +137,7 @@ def to_pdf(self, path=None): else: input = None stdout, stderr = result.communicate(input=input) - + stderr = stderr or stdout exit_code = result.returncode if 'cannot connect to X server' in stderr.decode('utf-8'): @@ -169,10 +169,10 @@ def to_pdf(self, path=None): 'Check whhtmltopdf output without \'quiet\' ' 'option' % ' '.join(args)) return True - except IOError: + except IOError,e: raise IOError('Command failed: %s\n' - 'Check whhtmltopdf output without \'quiet\' option' % - ' '.join(args)) + 'Check whhtmltopdf output without \'quiet\' option\n' + '%s ' %(' '.join(args)),e) def _normalize_options(self, options): """ Generator of 2-tuples (option-key, option-value). From 3096d0bb8c0c54fe12f22b0c6874545852e6a640 Mon Sep 17 00:00:00 2001 From: Fasih Ahmad Fakhri Date: Mon, 19 Dec 2016 19:02:01 +0530 Subject: [PATCH 22/97] Updated Exception syntax for python3 --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 62c175b..1e7774d 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -169,7 +169,7 @@ def to_pdf(self, path=None): 'Check whhtmltopdf output without \'quiet\' ' 'option' % ' '.join(args)) return True - except IOError,e: + except IOError as e: raise IOError('Command failed: %s\n' 'Check whhtmltopdf output without \'quiet\' option\n' '%s ' %(' '.join(args)),e) From f3a10c705dae2df657155a6ad007fd2d7fdf7e1d Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Mon, 19 Dec 2016 16:42:47 +0300 Subject: [PATCH 23/97] Fix some typos Fixes #54 --- pdfkit/pdfkit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index efed176..03ce1a9 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -145,8 +145,8 @@ def to_pdf(self, path=None): if 'cannot connect to X server' in stderr.decode('utf-8'): raise IOError('%s\n' - 'You will need to run whktmltopdf within a "virtual" X server.\n' - 'Go to the link above for more information\n' + 'You will need to run wkhtmltopdf within a "virtual" X server.\n' + 'Go to the link below for more information\n' 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr.decode('utf-8')) if 'Error' in stderr.decode('utf-8'): From ac822de4a5cf7ec7baae424f52a2dbc1d7a67f0a Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Mon, 19 Dec 2016 16:46:46 +0300 Subject: [PATCH 24/97] Replace cookies with cookie in README Fixes #64 --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a397161..65cb8b5 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,7 @@ If you wish to further process generated PDF, you can read it to a variable: # Use False instead of output path to save pdf to a variable pdf = pdfkit.from_url('http://google.com', False) -You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookies, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple (see example below). +You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookie, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple (see example below). .. code-block:: python @@ -81,7 +81,7 @@ You can specify all wkhtmltopdf `options Date: Mon, 19 Dec 2016 16:54:53 +0300 Subject: [PATCH 25/97] Replace str with basestring in isinstance call Fixes #69 --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 03ce1a9..275a2c6 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -107,7 +107,7 @@ def _command(self, path=None): if self.source.isString() or self.source.isFileObj(): yield '-' else: - if isinstance(self.source.source, str): + if isinstance(self.source.source, basestring): yield self.source.to_s() else: for s in self.source.source: From 8740aa7457f061b43804234a0a636aafd2a06d39 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Mon, 19 Dec 2016 17:10:11 +0300 Subject: [PATCH 26/97] Version bump to 0.6.0 --- AUTHORS.rst | 6 ++++++ HISTORY.rst | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index b7b6746..cd12c2f 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -11,3 +11,9 @@ Contributors * `Tomscytale `_ * `Matheus Marchini `_ * `Rory McCann `_ +* `Matheus Marchini `_ +* `signalkraft `_ +* `Pietro Delsante `_ +* `Hung Le `_ +* `Zachary Kazanski `_ +* `Fasih Ahmad Fakhri `_ diff --git a/HISTORY.rst b/HISTORY.rst index 62240fb..ad0c233 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,11 @@ Changelog --------- +* `0.6.0` + * Support repeatable options + * Support multiple values for some options + * Fix some corner cases when specific argument order is required + * Some Python 3+ compatibility fixes + * Update README * `0.5.0` * Allow passing multiple css files * Fix problems with external file encodings From 695758ffdc07c68ed1c18f6b7b112e05ea598d21 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Mon, 19 Dec 2016 17:16:55 +0300 Subject: [PATCH 27/97] Really bump version to 0.6.0 --- pdfkit/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/__init__.py b/pdfkit/__init__.py index 47ff517..c007b35 100644 --- a/pdfkit/__init__.py +++ b/pdfkit/__init__.py @@ -4,7 +4,7 @@ """ __author__ = 'Golovanov Stanislav' -__version__ = '0.5.0' +__version__ = '0.6.0' __license__ = 'MIT' from .pdfkit import PDFKit From 22e8462354b5082a55d7b993ea81d459a7198a37 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Sun, 8 Jan 2017 18:16:56 -0800 Subject: [PATCH 28/97] prevent UnicodeDecodeError when trying to decode pdf output as stderr --- pdfkit/pdfkit.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 275a2c6..dc56174 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -4,7 +4,6 @@ import sys from .source import Source from .configuration import Configuration -from itertools import chain import io import codecs try: @@ -141,24 +140,28 @@ def to_pdf(self, path=None): input = None stdout, stderr = result.communicate(input=input) stderr = stderr or stdout + try: + stderr = stderr.decode('utf-8') + except UnicodeDecodeError: + stderr = '' exit_code = result.returncode - if 'cannot connect to X server' in stderr.decode('utf-8'): + if 'cannot connect to X server' in stderr: raise IOError('%s\n' 'You will need to run wkhtmltopdf within a "virtual" X server.\n' 'Go to the link below for more information\n' - 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr.decode('utf-8')) + 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) - if 'Error' in stderr.decode('utf-8'): - raise IOError('wkhtmltopdf reported an error:\n' + stderr.decode('utf-8')) + if 'Error' in stderr: + raise IOError('wkhtmltopdf reported an error:\n' + stderr) if exit_code != 0: - raise IOError("wkhtmltopdf exited with non-zero code {0}. error:\n{1}".format(exit_code, stderr.decode("utf-8"))) + raise IOError("wkhtmltopdf exited with non-zero code {0}. error:\n{1}".format(exit_code, stderr)) # Since wkhtmltopdf sends its output to stderr we will capture it # and properly send to stdout if '--quiet' not in args: - sys.stdout.write(stderr.decode('utf-8')) + sys.stdout.write(stderr) if not path: return stdout @@ -184,18 +187,17 @@ def _normalize_options(self, options): :param options: dict {option name: value} returns: - iterator (option-key, option-value) + iterator (option-key, option-value) - option names lower cased and prepended with '--' if necessary. Non-empty values cast to str """ - normalized_options = {} for key, value in list(options.items()): if not '--' in key: normalized_key = '--%s' % self._normalize_arg(key) else: normalized_key = self._normalize_arg(key) - + if isinstance(value, (list, tuple)): for optval in value: yield (normalized_key, optval) From c8c1030bf63a41f7d22ffc40bd346417202466ee Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Mon, 9 Jan 2017 18:41:22 +0300 Subject: [PATCH 29/97] bump verison to 0.6.1 --- AUTHORS.rst | 1 + HISTORY.rst | 2 ++ pdfkit/__init__.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index cd12c2f..62bdf43 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -17,3 +17,4 @@ Contributors * `Hung Le `_ * `Zachary Kazanski `_ * `Fasih Ahmad Fakhri `_ +* `Alan Hamlett `_ diff --git a/HISTORY.rst b/HISTORY.rst index ad0c233..4c8715d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,7 @@ Changelog --------- +* `0.6.1` + * Fix regression on python 3+ when trying to decode pdf output * `0.6.0` * Support repeatable options * Support multiple values for some options diff --git a/pdfkit/__init__.py b/pdfkit/__init__.py index c007b35..4d064d9 100644 --- a/pdfkit/__init__.py +++ b/pdfkit/__init__.py @@ -4,7 +4,7 @@ """ __author__ = 'Golovanov Stanislav' -__version__ = '0.6.0' +__version__ = '0.6.1' __license__ = 'MIT' from .pdfkit import PDFKit From bac0816fa90b8eab949453a5abf34db2e6d4883c Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Thu, 4 May 2017 00:06:00 -0700 Subject: [PATCH 30/97] only treat stderr as text after getting non-zero exit code, otherwise it should be pdf binary data --- pdfkit/pdfkit.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index dc56174..65c457f 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -140,23 +140,25 @@ def to_pdf(self, path=None): input = None stdout, stderr = result.communicate(input=input) stderr = stderr or stdout - try: - stderr = stderr.decode('utf-8') - except UnicodeDecodeError: - stderr = '' + exit_code = result.returncode + if exit_code != 0: + try: + stderr = stderr.decode('utf-8') + except UnicodeDecodeError: + stderr = '' - if 'cannot connect to X server' in stderr: - raise IOError('%s\n' - 'You will need to run wkhtmltopdf within a "virtual" X server.\n' - 'Go to the link below for more information\n' - 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) + if 'cannot connect to X server' in stderr: + raise IOError('%s\n' + 'You will need to run wkhtmltopdf within a "virtual" X server.\n' + 'Go to the link below for more information\n' + 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) - if 'Error' in stderr: - raise IOError('wkhtmltopdf reported an error:\n' + stderr) + if 'Error' in stderr: + raise IOError('wkhtmltopdf reported an error:\n' + stderr) - if exit_code != 0: - raise IOError("wkhtmltopdf exited with non-zero code {0}. error:\n{1}".format(exit_code, stderr)) + error_msg = stderr or 'Unknown Error' + raise IOError("wkhtmltopdf exited with non-zero code {0}. error:\n{1}".format(exit_code, error_msg)) # Since wkhtmltopdf sends its output to stderr we will capture it # and properly send to stdout From d667a337428c61144bd85cfdf92926d717c3b6c8 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Thu, 4 May 2017 00:08:27 -0700 Subject: [PATCH 31/97] decode stderr before writing to stdout --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 65c457f..1d0aada 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -163,7 +163,7 @@ def to_pdf(self, path=None): # Since wkhtmltopdf sends its output to stderr we will capture it # and properly send to stdout if '--quiet' not in args: - sys.stdout.write(stderr) + sys.stdout.write(stderr.decode('utf-8')) if not path: return stdout From 438b9b7abcb2045df2b5dc71e6e0a9f5601c7253 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Thu, 4 May 2017 00:19:08 -0700 Subject: [PATCH 32/97] catch OSError in case output file does not exist --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 1d0aada..9aec4f4 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -177,7 +177,7 @@ def to_pdf(self, path=None): 'Check whhtmltopdf output without \'quiet\' ' 'option' % ' '.join(args)) return True - except IOError as e: + except (IOError, OSError) as e: raise IOError('Command failed: %s\n' 'Check whhtmltopdf output without \'quiet\' option\n' '%s ' %(' '.join(args)),e) From 736b697a77b03a971b367855302aaef617fa3a6c Mon Sep 17 00:00:00 2001 From: NestorTejero Date: Mon, 19 Jun 2017 14:57:39 +0200 Subject: [PATCH 33/97] Correct placing of arguments for IOError message --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 9aec4f4..a4cf7e7 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -180,7 +180,7 @@ def to_pdf(self, path=None): except (IOError, OSError) as e: raise IOError('Command failed: %s\n' 'Check whhtmltopdf output without \'quiet\' option\n' - '%s ' %(' '.join(args)),e) + '%s ' % (' '.join(args), e)) def _normalize_options(self, options): """ Generator of 2-tuples (option-key, option-value). From 810d3d86e954215e963ec5304be36032542dda65 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 1 Sep 2017 10:18:09 +0100 Subject: [PATCH 34/97] correct spelling mistake --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 65cb8b5..cc0e28e 100644 --- a/README.rst +++ b/README.rst @@ -90,7 +90,7 @@ You can specify all wkhtmltopdf `options Date: Fri, 15 Sep 2017 19:00:39 +0200 Subject: [PATCH 35/97] assertRegexpMatches deprecated ../tests/pdfkit-tests.py:408: DeprecationWarning: Please use assertRegex instead. --- tests/pdfkit-tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 66e9f8e..fe6e30f 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -402,7 +402,7 @@ def test_raise_error_if_bad_wkhtmltopdf_option(self): r.to_pdf() raised_exception = cm.exception - self.assertRegexpMatches(str(raised_exception), '^wkhtmltopdf exited with non-zero code 1. error:\nUnknown long argument --bad-option\r?\n') + self.assertRegex(str(raised_exception), '^wkhtmltopdf exited with non-zero code 1. error:\nUnknown long argument --bad-option\r?\n') if __name__ == "__main__": unittest.main() From 397ed72ef540a38be55dc431ede6f0c9b130c7d0 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Fri, 29 Dec 2017 10:35:50 -0800 Subject: [PATCH 36/97] fix wkhtml2pdf install on travis --- travis/before-script.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/travis/before-script.sh b/travis/before-script.sh index 026db37..984d40a 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -1,8 +1,10 @@ -#!/bin/sh +#!/usr/bin/env sh + +WKHTML2PDF_VERSION='0.12.4' sudo apt-get install -y openssl build-essential xorg libssl-dev -wget http://download.gna.org/wkhtmltopdf/0.12/0.12.3/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz -tar -xJf wkhtmltox-0.12.3_linux-generic-amd64.tar.xz +wget "https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox-${WKHTML2PDF_VERSION}_linux-generic-amd64.tar.xz" +tar -xJf "wkhtmltox-${WKHTML2PDF_VERSION}_linux-generic-amd64.tar.xz" cd wkhtmltox sudo chown root:root bin/wkhtmltopdf sudo cp -r * /usr/ From 6f1077dbae22863390915b6f69c8ec77f7c4a83f Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Fri, 29 Dec 2017 10:43:23 -0800 Subject: [PATCH 37/97] patch unittest.TestCase.assertRegex for py27 --- tests/pdfkit-tests.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index fe6e30f..78fc329 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -6,6 +6,10 @@ import unittest +if sys.version_info[0] == 2 and sys.version_info[1] == 7: + unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches + + #Prepend ../ to PYTHONPATH so that we can import PDFKIT form there. TESTS_ROOT = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, os.path.realpath(os.path.join(TESTS_ROOT, '..'))) @@ -80,9 +84,9 @@ def test_options_parsing_with_tuple_no_dashes(self): def test_repeatable_options(self): roptions = { '--page-size': 'Letter', - 'cookies': [ + 'cookies': [ ('test_cookie1','cookie_value1'), - ('test_cookie2','cookie_value2'), + ('test_cookie2','cookie_value2'), ] } From de4c38f4f2457afe72956fc8dcfe1321143c5c3d Mon Sep 17 00:00:00 2001 From: fbataill Date: Thu, 11 Jan 2018 22:51:00 +0100 Subject: [PATCH 38/97] Update Readme for python3 I had to search on internet to understand why I was not able to import pdfkit in python3. I just did not know that I had to use pip3 instead of pip. Indicating this here could same hours of search or premature abandon :) --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index cc0e28e..da7fbe6 100644 --- a/README.rst +++ b/README.rst @@ -19,7 +19,7 @@ Installation .. code-block:: bash - $ pip install pdfkit + $ pip install pdfkit (or pip3 for python3) 2. Install wkhtmltopdf: From 5b81171fd4e84ce6de56fdb2066fbfeb99e5ce58 Mon Sep 17 00:00:00 2001 From: Mathieu Zonzon Date: Tue, 6 Feb 2018 14:20:48 +0100 Subject: [PATCH 39/97] pdfkit/pdfkit: Keeping the options order for python < 3.6 Options mixup may result in wkhtmltopdf failing example: OSError: wkhtmltopdf exited with non-zero code 1. error: --margin-bottom specified in incorrect location --- pdfkit/pdfkit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index a4cf7e7..fc01793 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -2,6 +2,7 @@ import re import subprocess import sys +from collections import OrderedDict from .source import Source from .configuration import Configuration import io @@ -46,7 +47,7 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, except AttributeError: self.wkhtmltopdf = self.configuration.wkhtmltopdf - self.options = dict() + self.options = OrderedDict() if self.source.isString(): self.options.update(self._find_options_in_meta(url_or_file)) From 5f2d3714bc49959102e06bc474602ce5532dff29 Mon Sep 17 00:00:00 2001 From: Mathieu Zonzon Date: Wed, 7 Feb 2018 17:59:20 +0100 Subject: [PATCH 40/97] pdfkit/pdfkit: Improved stdout/stderr decoding (errors handling) --- pdfkit/pdfkit.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index fc01793..7e59373 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -144,10 +144,7 @@ def to_pdf(self, path=None): exit_code = result.returncode if exit_code != 0: - try: - stderr = stderr.decode('utf-8') - except UnicodeDecodeError: - stderr = '' + stderr = stderr.decode('utf-8', errors='replace') if 'cannot connect to X server' in stderr: raise IOError('%s\n' @@ -164,7 +161,7 @@ def to_pdf(self, path=None): # Since wkhtmltopdf sends its output to stderr we will capture it # and properly send to stdout if '--quiet' not in args: - sys.stdout.write(stderr.decode('utf-8')) + sys.stdout.write(stderr.decode('utf-8', errors='replace')) if not path: return stdout From 1b58db090885b8269cc68f45597aab49ba89b043 Mon Sep 17 00:00:00 2001 From: Martin Rehr Date: Thu, 22 Mar 2018 08:28:50 +0100 Subject: [PATCH 41/97] Support for passing custom OS environment to Popen --- pdfkit/configuration.py | 12 +++++++++++- pdfkit/pdfkit.py | 6 ++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index 95228f3..b086995 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- +import os import subprocess import sys class Configuration(object): - def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-'): + def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): self.meta_tag_prefix = meta_tag_prefix self.wkhtmltopdf = wkhtmltopdf @@ -25,3 +26,12 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-'): 'If this file exists please check that this process can ' 'read it. Otherwise please install wkhtmltopdf - ' 'https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf' % self.wkhtmltopdf) + + self.environ = environ + + if not self.environ: + self.environ = os.environ + + for key in self.environ.keys(): + if not isinstance(self.environ[key], str): + self.environ[key] = str(self.environ[key]) \ No newline at end of file diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 7e59373..6de7f5e 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -51,6 +51,8 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, if self.source.isString(): self.options.update(self._find_options_in_meta(url_or_file)) + self.environ = self.configuration.environ + if options is not None: self.options.update(options) self.toc = {} if toc is None else toc @@ -125,9 +127,9 @@ def command(self, path=None): def to_pdf(self, path=None): args = self.command(path) - + result = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, env=self.environ) # If the source is a string then we will pipe it into wkhtmltopdf. # If we want to add custom CSS to file then we read input file to From 86fd2e88ccc517f65da924cfc35e9ea1e00a8f3c Mon Sep 17 00:00:00 2001 From: Martin Joly Date: Fri, 30 Mar 2018 10:06:19 +0200 Subject: [PATCH 42/97] Added wkhtmltopdf installation command for macOS --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index cc0e28e..db0d19a 100644 --- a/README.rst +++ b/README.rst @@ -28,6 +28,12 @@ Installation .. code-block:: bash $ sudo apt-get install wkhtmltopdf + +* macOS: + +.. code-block:: bash + + $ brew install caskroom/cask/wkhtmltopdf **Warning!** Version in debian/ubuntu repos have reduced functionality (because it compiled without the wkhtmltopdf QT patches), such as adding outlines, headers, footers, TOC etc. To use this options you should install static binary from `wkhtmltopdf `_ site or you can use `this script `_. From 479d65646ab422bc095ed1c72d05f46dc834c83b Mon Sep 17 00:00:00 2001 From: Michael Kim Date: Sat, 4 Aug 2018 16:09:28 -0700 Subject: [PATCH 43/97] [CLEANUP] reformat code --- pdfkit/pdfkit.py | 54 ++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 6de7f5e..8153b3d 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -127,9 +127,14 @@ def command(self, path=None): def to_pdf(self, path=None): args = self.command(path) - - result = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env=self.environ) + + result = subprocess.Popen( + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.environ + ) # If the source is a string then we will pipe it into wkhtmltopdf. # If we want to add custom CSS to file then we read input file to @@ -141,18 +146,18 @@ def to_pdf(self, path=None): input = self.source.source.read().encode('utf-8') else: input = None + stdout, stderr = result.communicate(input=input) stderr = stderr or stdout - + stderr = stderr.decode('utf-8', errors='replace') exit_code = result.returncode - if exit_code != 0: - stderr = stderr.decode('utf-8', errors='replace') + if exit_code != 0: if 'cannot connect to X server' in stderr: raise IOError('%s\n' - 'You will need to run wkhtmltopdf within a "virtual" X server.\n' - 'Go to the link below for more information\n' - 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) + 'You will need to run wkhtmltopdf within a "virtual" X server.\n' + 'Go to the link below for more information\n' + 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) if 'Error' in stderr: raise IOError('wkhtmltopdf reported an error:\n' + stderr) @@ -163,24 +168,24 @@ def to_pdf(self, path=None): # Since wkhtmltopdf sends its output to stderr we will capture it # and properly send to stdout if '--quiet' not in args: - sys.stdout.write(stderr.decode('utf-8', errors='replace')) + sys.stdout.write(stderr) if not path: return stdout - else: - try: - with codecs.open(path, encoding='utf-8') as f: - # read 4 bytes to get PDF signature '%PDF' - text = f.read(4) - if text == '': - raise IOError('Command failed: %s\n' - 'Check whhtmltopdf output without \'quiet\' ' - 'option' % ' '.join(args)) - return True - except (IOError, OSError) as e: - raise IOError('Command failed: %s\n' - 'Check whhtmltopdf output without \'quiet\' option\n' - '%s ' % (' '.join(args), e)) + + try: + with codecs.open(path, encoding='utf-8') as f: + # read 4 bytes to get PDF signature '%PDF' + text = f.read(4) + if text == '': + raise IOError('Command failed: %s\n' + 'Check whhtmltopdf output without \'quiet\' ' + 'option' % ' '.join(args)) + return True + except (IOError, OSError) as e: + raise IOError('Command failed: %s\n' + 'Check whhtmltopdf output without \'quiet\' option\n' + '%s ' % (' '.join(args), e)) def _normalize_options(self, options): """ Generator of 2-tuples (option-key, option-value). @@ -206,7 +211,6 @@ def _normalize_options(self, options): else: yield (normalized_key, str(value) if value else value) - def _normalize_arg(self, arg): return arg.lower() From ba42969e4e9456498cab7a589245ad6720e1cbd5 Mon Sep 17 00:00:00 2001 From: Michael Kim Date: Sat, 4 Aug 2018 16:10:48 -0700 Subject: [PATCH 44/97] handle_error method --- pdfkit/pdfkit.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 8153b3d..178a13a 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -125,6 +125,23 @@ def _command(self, path=None): def command(self, path=None): return list(self._command(path)) + @staticmethod + def handle_error(exit_code, stderr): + if exit_code == 0: + return + + if 'cannot connect to X server' in stderr: + raise IOError('%s\n' + 'You will need to run wkhtmltopdf within a "virtual" X server.\n' + 'Go to the link below for more information\n' + 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) + + if 'Error' in stderr: + raise IOError('wkhtmltopdf reported an error:\n' + stderr) + + error_msg = stderr or 'Unknown Error' + raise IOError("wkhtmltopdf exited with non-zero code {0}. error:\n{1}".format(exit_code, error_msg)) + def to_pdf(self, path=None): args = self.command(path) @@ -151,19 +168,7 @@ def to_pdf(self, path=None): stderr = stderr or stdout stderr = stderr.decode('utf-8', errors='replace') exit_code = result.returncode - - if exit_code != 0: - if 'cannot connect to X server' in stderr: - raise IOError('%s\n' - 'You will need to run wkhtmltopdf within a "virtual" X server.\n' - 'Go to the link below for more information\n' - 'https://github.com/JazzCore/python-pdfkit/wiki/Using-wkhtmltopdf-without-X-server' % stderr) - - if 'Error' in stderr: - raise IOError('wkhtmltopdf reported an error:\n' + stderr) - - error_msg = stderr or 'Unknown Error' - raise IOError("wkhtmltopdf exited with non-zero code {0}. error:\n{1}".format(exit_code, error_msg)) + self.handle_error(exit_code, stderr) # Since wkhtmltopdf sends its output to stderr we will capture it # and properly send to stdout From 440596e0772c57fc9ea0d11411c5550959fda6de Mon Sep 17 00:00:00 2001 From: Michael Kim Date: Sat, 4 Aug 2018 16:17:25 -0700 Subject: [PATCH 45/97] [FIX] look for 'Done' in stderr Sometimes wkhtmltopdf will exit with non-zero even if the generation is successful. --- pdfkit/pdfkit.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 178a13a..7613b1d 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -130,6 +130,12 @@ def handle_error(exit_code, stderr): if exit_code == 0: return + # Sometimes wkhtmltopdf will exit with non-zero + # even if it finishes generation. + # If will display 'Done' in the second last line + if stderr.splitlines()[-2].strip() == 'Done': + return + if 'cannot connect to X server' in stderr: raise IOError('%s\n' 'You will need to run wkhtmltopdf within a "virtual" X server.\n' From 2b9a87c03ca96a456a506392dc72f493d5416337 Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Tue, 28 Aug 2018 05:00:35 -0400 Subject: [PATCH 46/97] Allow creation of pdfs containing unicode characters --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 6de7f5e..6f137dc 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -204,7 +204,7 @@ def _normalize_options(self, options): for optval in value: yield (normalized_key, optval) else: - yield (normalized_key, str(value) if value else value) + yield (normalized_key, unicode(value) if value else value) def _normalize_arg(self, arg): From 0aa6afe9c9f04d0fe15c1bd1f298b63f0eb8d744 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Mon, 22 Oct 2018 08:50:35 -0700 Subject: [PATCH 47/97] support unicode keyword on Python 3 --- pdfkit/pdfkit.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index e54ddee..15d1d76 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -10,8 +10,10 @@ try: # Python 2.x and 3.x support for checking string types assert basestring + assert unicode except NameError: basestring = str + unicode = str class PDFKit(object): @@ -53,7 +55,8 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None, self.environ = self.configuration.environ - if options is not None: self.options.update(options) + if options is not None: + self.options.update(options) self.toc = {} if toc is None else toc self.cover = cover @@ -211,7 +214,7 @@ def _normalize_options(self, options): """ for key, value in list(options.items()): - if not '--' in key: + if '--' not in key: normalized_key = '--%s' % self._normalize_arg(key) else: normalized_key = self._normalize_arg(key) From 844b0bb9c5f89db88ed0be9bd439dd7bff7e797f Mon Sep 17 00:00:00 2001 From: Niklas Baumstark Date: Mon, 9 Dec 2019 14:48:54 +0100 Subject: [PATCH 48/97] Update source.py --- pdfkit/source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/source.py b/pdfkit/source.py index 44ca1bf..28e9659 100644 --- a/pdfkit/source.py +++ b/pdfkit/source.py @@ -8,7 +8,7 @@ def __init__(self, url_or_file, type_): self.source = url_or_file self.type = type_ - if self.type is 'file': + if self.type == 'file': self.checkFiles() def isUrl(self): From 704087d9a204f05ebdfe22da26375d10aba49131 Mon Sep 17 00:00:00 2001 From: Cong Zhang Date: Mon, 23 Mar 2020 19:06:38 +0800 Subject: [PATCH 49/97] Update README.rst fix example --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index b565263..6e7f074 100644 --- a/README.rst +++ b/README.rst @@ -84,9 +84,9 @@ You can specify all wkhtmltopdf `options Date: Thu, 23 Apr 2020 19:20:52 +0100 Subject: [PATCH 50/97] Fix SyntaxWarning with literal equality check `.venv/lib/python3.8/site-packages/pdfkit/source.py:11: SyntaxWarning: "is" with a literal. Did you mean "=="?` --- pdfkit/source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/source.py b/pdfkit/source.py index 44ca1bf..28e9659 100644 --- a/pdfkit/source.py +++ b/pdfkit/source.py @@ -8,7 +8,7 @@ def __init__(self, url_or_file, type_): self.source = url_or_file self.type = type_ - if self.type is 'file': + if self.type == 'file': self.checkFiles() def isUrl(self): From 2247e1ce00bc30c16c998b4a0c880814b5a0f05e Mon Sep 17 00:00:00 2001 From: Marcello Dalponte Date: Thu, 23 Apr 2020 19:28:21 +0100 Subject: [PATCH 51/97] Remove python 3.2 and 3.3 and add 3.6, 3.7, 3.8 It looks like 3.2 and 3.3 are not available on travis anymore https://travis-ci.org/github/JazzCore/python-pdfkit/jobs/665811141 --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c5007c..5404159 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ language: python python: - 2.7 - - 3.2 - - 3.3 - 3.4 - 3.5 + - 3.6 + - 3.7 + - 3.8 before_script: "./travis/before-script.sh" script: nosetests -w tests notifications: From 517c05cbb843042fd2ccf6b6df70cc28f27af0dd Mon Sep 17 00:00:00 2001 From: sudharshansudhu Date: Thu, 2 Jul 2020 18:52:37 +0530 Subject: [PATCH 52/97] Update api.py Corrected the function docstring. --- pdfkit/api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pdfkit/api.py b/pdfkit/api.py index 6520bf8..d5495d8 100644 --- a/pdfkit/api.py +++ b/pdfkit/api.py @@ -15,7 +15,7 @@ def from_url(url, output_path, options=None, toc=None, cover=None, :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page :param configuration: (optional) instance of pdfkit.configuration.Configuration() - :param configuration_first: (optional) if True, cover always precedes TOC + :param cover_first: (optional) if True, cover always precedes TOC Returns: True on success """ @@ -38,7 +38,7 @@ def from_file(input, output_path, options=None, toc=None, cover=None, css=None, :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a single input file :param configuration: (optional) instance of pdfkit.configuration.Configuration() - :param configuration_first: (optional) if True, cover always precedes TOC + :param cover_first: (optional) if True, cover always precedes TOC Returns: True on success """ @@ -61,7 +61,7 @@ def from_string(input, output_path, options=None, toc=None, cover=None, css=None :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a input string :param configuration: (optional) instance of pdfkit.configuration.Configuration() - :param configuration_first: (optional) if True, cover always precedes TOC + :param cover_first: (optional) if True, cover always precedes TOC Returns: True on success """ From 9a8f74d00a5d0c6da6b32db75162b00a4005f14f Mon Sep 17 00:00:00 2001 From: Fasih Ahmad Fakhri Date: Fri, 7 Aug 2020 13:16:26 +0530 Subject: [PATCH 53/97] Update AUTHORS.rst --- AUTHORS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 62bdf43..07d9fd0 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -16,5 +16,5 @@ Contributors * `Pietro Delsante `_ * `Hung Le `_ * `Zachary Kazanski `_ -* `Fasih Ahmad Fakhri `_ +* `Fasih Ahmad Fakhri `_ * `Alan Hamlett `_ From 3977d3b7ac48d77c9497323088e014e6a2248be2 Mon Sep 17 00:00:00 2001 From: Sammy Taylor Date: Thu, 24 Sep 2020 10:01:33 -0500 Subject: [PATCH 54/97] Update README.rst Update `brew install` command for macOS. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b565263..5c4785c 100644 --- a/README.rst +++ b/README.rst @@ -33,7 +33,7 @@ Installation .. code-block:: bash - $ brew install caskroom/cask/wkhtmltopdf + $ brew install homebrew/cask/wkhtmltopdf **Warning!** Version in debian/ubuntu repos have reduced functionality (because it compiled without the wkhtmltopdf QT patches), such as adding outlines, headers, footers, TOC etc. To use this options you should install static binary from `wkhtmltopdf `_ site or you can use `this script `_. From 1d6145fd2acbd0c3dc6a67616e7529ccfaca7084 Mon Sep 17 00:00:00 2001 From: Jan-Frederik Konopka Date: Fri, 13 Nov 2020 01:03:59 +0100 Subject: [PATCH 55/97] fix: prevent configuration failing if multiple paths available If "where wkhtmltopdf" returns 2 or more lines, the configuration currently fails with "No wkhtmltopdf executable found". --- pdfkit/configuration.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index b086995..e3e3990 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -13,10 +13,10 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): if not self.wkhtmltopdf: if sys.platform == 'win32': self.wkhtmltopdf = subprocess.Popen( - ['where', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip() + ['where', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].split()[0].strip() else: self.wkhtmltopdf = subprocess.Popen( - ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip() + ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].split()[0].strip() try: with open(self.wkhtmltopdf) as f: @@ -34,4 +34,4 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): for key in self.environ.keys(): if not isinstance(self.environ[key], str): - self.environ[key] = str(self.environ[key]) \ No newline at end of file + self.environ[key] = str(self.environ[key]) From d866477d2b5d46239613d41f33dbd86da886ebb6 Mon Sep 17 00:00:00 2001 From: Osama Gadit Date: Tue, 24 Nov 2020 02:58:50 +0500 Subject: [PATCH 56/97] Changed where to where.exe Changed where to where.exe as Powershell has a native where command which was messing up the script if run through a Powershell console on Windows --- pdfkit/configuration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index b086995..9a73c02 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -13,7 +13,7 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): if not self.wkhtmltopdf: if sys.platform == 'win32': self.wkhtmltopdf = subprocess.Popen( - ['where', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip() + ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip() else: self.wkhtmltopdf = subprocess.Popen( ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip() @@ -34,4 +34,4 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): for key in self.environ.keys(): if not isinstance(self.environ[key], str): - self.environ[key] = str(self.environ[key]) \ No newline at end of file + self.environ[key] = str(self.environ[key]) From 81ce7fe63926660a0075751e699eea3d03c24459 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 4 Feb 2021 18:35:22 +0100 Subject: [PATCH 57/97] Make from_url/from_file/from_string output_path parameter optional --- README.rst | 6 +++--- pdfkit/api.py | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index b565263..afb4210 100644 --- a/README.rst +++ b/README.rst @@ -28,7 +28,7 @@ Installation .. code-block:: bash $ sudo apt-get install wkhtmltopdf - + * macOS: .. code-block:: bash @@ -70,8 +70,8 @@ If you wish to further process generated PDF, you can read it to a variable: .. code-block:: python - # Use False instead of output path to save pdf to a variable - pdf = pdfkit.from_url('http://google.com', False) + # Without output_path, PDF is returned for assigning to a variable + pdf = pdfkit.from_url('http://google.com') You can specify all wkhtmltopdf `options `_. You can drop '--' in option name. If option without value, use *None, False* or *''* for dict value:. For repeatable options (incl. allow, cookie, custom-header, post, postfile, run-script, replace) you may use a list or a tuple. With option that need multiple values (e.g. --custom-header Authorization secret) we may use a 2-tuple (see example below). diff --git a/pdfkit/api.py b/pdfkit/api.py index 6520bf8..8932970 100644 --- a/pdfkit/api.py +++ b/pdfkit/api.py @@ -4,13 +4,13 @@ from .pdfkit import Configuration -def from_url(url, output_path, options=None, toc=None, cover=None, +def from_url(url, output_path=None, options=None, toc=None, cover=None, configuration=None, cover_first=False): """ Convert file of files from URLs to PDF document :param url: URL or list of URLs to be saved - :param output_path: path to output PDF file. False means file will be returned as string. + :param output_path: (optional) path to output PDF file. By default, PDF will be returned for assigning to a variable. :param options: (optional) dict with wkhtmltopdf global and page options, with or w/o '--' :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page @@ -26,13 +26,13 @@ def from_url(url, output_path, options=None, toc=None, cover=None, return r.to_pdf(output_path) -def from_file(input, output_path, options=None, toc=None, cover=None, css=None, +def from_file(input, output_path=None, options=None, toc=None, cover=None, css=None, configuration=None, cover_first=False): """ Convert HTML file or files to PDF document :param input: path to HTML file or list with paths or file-like object - :param output_path: path to output PDF file. False means file will be returned as string. + :param output_path: (optional) path to output PDF file. By default, PDF will be returned for assigning to a variable. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page @@ -49,13 +49,13 @@ def from_file(input, output_path, options=None, toc=None, cover=None, css=None, return r.to_pdf(output_path) -def from_string(input, output_path, options=None, toc=None, cover=None, css=None, +def from_string(input, output_path=None, options=None, toc=None, cover=None, css=None, configuration=None, cover_first=False): """ Convert given string or strings to PDF document :param input: string with a desired text. Could be a raw text or a html file - :param output_path: path to output PDF file. False means file will be returned as string. + :param output_path: (optional) path to output PDF file. By default, PDF will be returned for assigning to a variable. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page From ac092fec86f713200bb874412b470574e2107154 Mon Sep 17 00:00:00 2001 From: Ajin Abraham Date: Sat, 25 Apr 2020 06:15:28 +0530 Subject: [PATCH 58/97] Support Python3.8 Python 3.8 throws warning ``` source.py:11: SyntaxWarning: "is" with a literal. Did you mean "=="? if self.type is 'file': ``` --- pdfkit/source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/source.py b/pdfkit/source.py index 44ca1bf..28e9659 100644 --- a/pdfkit/source.py +++ b/pdfkit/source.py @@ -8,7 +8,7 @@ def __init__(self, url_or_file, type_): self.source = url_or_file self.type = type_ - if self.type is 'file': + if self.type == 'file': self.checkFiles() def isUrl(self): From c18af15ac404439fb769b04cb2ad6c5092a84e53 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Fri, 19 Mar 2021 18:56:46 +0300 Subject: [PATCH 59/97] add test for #181 --- tests/pdfkit-tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 78fc329..82da788 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -309,6 +309,11 @@ def test_pdf_generation(self): pdf = r.to_pdf('out.pdf') self.assertTrue(pdf) + def test_pdf_generation_into_variable(self): + r = pdfkit.PDFKit('html', 'string', options={'page-size': 'Letter'}) + pdf = r.to_pdf() + self.assertTrue(pdf[0:4].decode('ascii').startswith('%PDF')) + def test_raise_error_with_invalid_url(self): r = pdfkit.PDFKit('wrongurl', 'url') with self.assertRaises(IOError): From afa1efcf239480deb75cb1a3f018e9f428ceb60e Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Fri, 19 Mar 2021 21:01:31 +0300 Subject: [PATCH 60/97] Add test for #42 --- tests/fixtures/issue_42_bad_char_page.html | 6 ++++++ tests/pdfkit-tests.py | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/fixtures/issue_42_bad_char_page.html diff --git a/tests/fixtures/issue_42_bad_char_page.html b/tests/fixtures/issue_42_bad_char_page.html new file mode 100644 index 0000000..a6044d5 --- /dev/null +++ b/tests/fixtures/issue_42_bad_char_page.html @@ -0,0 +1,6 @@ + + + + This is a bad character -->   <-- + + diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 82da788..f3e0520 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -413,5 +413,12 @@ def test_raise_error_if_bad_wkhtmltopdf_option(self): raised_exception = cm.exception self.assertRegex(str(raised_exception), '^wkhtmltopdf exited with non-zero code 1. error:\nUnknown long argument --bad-option\r?\n') + def test_issue_42_encode_file_with_unicode_char(self): + with open('fixtures/issue_42_bad_char_page.html', 'r') as f: + data = f.read() + r = pdfkit.PDFKit(data, 'string') + output = r.to_pdf() + self.assertEqual(output[:4].decode('utf-8'), '%PDF') + if __name__ == "__main__": unittest.main() From 9c27001a6845bb9f30f63d88f8574cc5a854abf0 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Fri, 19 Mar 2021 21:01:48 +0300 Subject: [PATCH 61/97] Force decode input string to unicode on python 2 Fixes #42 --- pdfkit/source.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pdfkit/source.py b/pdfkit/source.py index 28e9659..7b115b6 100644 --- a/pdfkit/source.py +++ b/pdfkit/source.py @@ -1,6 +1,14 @@ # -*- coding: utf-8 -*- import os import io +try: + # Python 2.x and 3.x support for checking string types + assert basestring + assert unicode +except NameError: + basestring = str + unicode = str + class Source(object): @@ -38,4 +46,12 @@ def isFileObj(self): return hasattr(self.source, 'read') def to_s(self): - return self.source + # String should be in unicode(python2)/str(python3) type since we will + # later encode it to utf-8 bytes array to pipe into subprocess + # With some charachters on python 2 it sets to str type (bytes) which is wrong + # and cant later encode properly, this is a workaround for this. + # See issue #42 + if isinstance(self.source, unicode): + return self.source + else: + return unicode(self.source, 'utf-8') From ac0406a707ba54ac7a20264b0847c6c50e523254 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Fri, 19 Mar 2021 23:48:45 +0300 Subject: [PATCH 62/97] update README --- README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.rst b/README.rst index 8b27754..ad49c8c 100644 --- a/README.rst +++ b/README.rst @@ -164,6 +164,16 @@ Example - for when ``wkhtmltopdf`` is not on ``$PATH``: config = pdfkit.configuration(wkhtmltopdf='/opt/bin/wkhtmltopdf') pdfkit.from_string(html_string, output_file, configuration=config) +Also you can use ``configuration()`` call to check if wkhtmltopdf is present in ``$PATH``: + +.. code-block:: python + + try: + config = pdfkit.configuration() + pdfkit.from_string(html_string, output_file) + except OSError: + #not present in PATH + Troubleshooting --------------- From ea99799eaa5751348888ee59233db1ca95917c8a Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Fri, 19 Mar 2021 23:58:21 +0300 Subject: [PATCH 63/97] update travis wkhtmltopdf version to 0.12.6-1 --- travis/before-script.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/travis/before-script.sh b/travis/before-script.sh index 984d40a..3e2194e 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -1,10 +1,8 @@ #!/usr/bin/env sh -WKHTML2PDF_VERSION='0.12.4' +WKHTML2PDF_VERSION='0.12.6-1' sudo apt-get install -y openssl build-essential xorg libssl-dev -wget "https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox-${WKHTML2PDF_VERSION}_linux-generic-amd64.tar.xz" -tar -xJf "wkhtmltox-${WKHTML2PDF_VERSION}_linux-generic-amd64.tar.xz" -cd wkhtmltox -sudo chown root:root bin/wkhtmltopdf -sudo cp -r * /usr/ +https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb +wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.focal_amd64.deb" +sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.focal_amd64.deb From a86cbe54cd805072318e87555b4d9ddb2dd9ea88 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:09:08 +0300 Subject: [PATCH 64/97] More travis build deps --- travis/before-script.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/travis/before-script.sh b/travis/before-script.sh index 3e2194e..d9a307c 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -2,7 +2,8 @@ WKHTML2PDF_VERSION='0.12.6-1' -sudo apt-get install -y openssl build-essential xorg libssl-dev +sudo apt-get install -y openssl build-essential xorg libssl-dev libxrender1 libfontconfig1 libx11-dev libjpeg62 libxtst6 fontconfig xfonts-75dpi xfonts-base libpng12-0 https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.focal_amd64.deb" sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.focal_amd64.deb +sudo apt-get -f install \ No newline at end of file From e852d274b78b1afea4033d3cd7af0579a7d03227 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:26:10 +0300 Subject: [PATCH 65/97] update error handling in Configuration class --- pdfkit/configuration.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index bec847d..4555fca 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -2,6 +2,10 @@ import os import subprocess import sys +try: + FileNotFoundError +except NameError: + FileNotFoundError = IOError class Configuration(object): @@ -10,23 +14,29 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): self.wkhtmltopdf = wkhtmltopdf - if not self.wkhtmltopdf: - if sys.platform == 'win32': - self.wkhtmltopdf = subprocess.Popen( - ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].split()[0].strip() - else: - self.wkhtmltopdf = subprocess.Popen( - ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].split()[0].strip() - try: + if not self.wkhtmltopdf: + if sys.platform == 'win32': + self.wkhtmltopdf = subprocess.Popen( + ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] + else: + self.wkhtmltopdf = subprocess.Popen( + ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] + + lines = self.wkhtmltopdf.splitlines() + if len(lines) > 0: + self.wkhtmltopdf = lines[0].strip() + with open(self.wkhtmltopdf) as f: pass - except IOError: + except (IOError, FileNotFoundError) as e: raise IOError('No wkhtmltopdf executable found: "%s"\n' 'If this file exists please check that this process can ' - 'read it. Otherwise please install wkhtmltopdf - ' + 'read it or you can pass path to it manually in method call, ' + 'check README. Otherwise please install wkhtmltopdf - ' 'https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf' % self.wkhtmltopdf) + self.environ = environ if not self.environ: From 214255312922c9448a28300dd725596c18e83aff Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:33:37 +0300 Subject: [PATCH 66/97] More Travis updates --- .travis.yml | 1 + travis/before-script.sh | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5404159..06ee64d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,5 +8,6 @@ python: - 3.8 before_script: "./travis/before-script.sh" script: nosetests -w tests +dist: xenial notifications: email: false diff --git a/travis/before-script.sh b/travis/before-script.sh index d9a307c..f7c361b 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -2,8 +2,7 @@ WKHTML2PDF_VERSION='0.12.6-1' -sudo apt-get install -y openssl build-essential xorg libssl-dev libxrender1 libfontconfig1 libx11-dev libjpeg62 libxtst6 fontconfig xfonts-75dpi xfonts-base libpng12-0 -https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb -wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.focal_amd64.deb" -sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.focal_amd64.deb +sudo apt-get install -y openssl build-essential libssl-dev libpthread-stubs0-dev libxau-dev xorg-sgml-doctools libxdmcp-dev x11proto-core-dev x11proto-input-dev x11proto-kb-dev xtrans-dev libx11-dev libxcb1-dev libjpeg62 libxtst6 libfontenc1 libxfont1 x11-common xfonts-encodings xfonts-utils fontconfig xfonts-base xfonts-75dpi +wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.xenial_amd64.deb" +sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.xenial_amd64.deb sudo apt-get -f install \ No newline at end of file From 30e0d3b5395b03cc0e6f47aa78499133edb406c0 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:55:16 +0300 Subject: [PATCH 67/97] Switch Travis to Bionic --- .travis.yml | 2 +- travis/before-script.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06ee64d..2df813d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,6 @@ python: - 3.8 before_script: "./travis/before-script.sh" script: nosetests -w tests -dist: xenial +dist: bionic notifications: email: false diff --git a/travis/before-script.sh b/travis/before-script.sh index f7c361b..58d7286 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -2,7 +2,7 @@ WKHTML2PDF_VERSION='0.12.6-1' -sudo apt-get install -y openssl build-essential libssl-dev libpthread-stubs0-dev libxau-dev xorg-sgml-doctools libxdmcp-dev x11proto-core-dev x11proto-input-dev x11proto-kb-dev xtrans-dev libx11-dev libxcb1-dev libjpeg62 libxtst6 libfontenc1 libxfont1 x11-common xfonts-encodings xfonts-utils fontconfig xfonts-base xfonts-75dpi -wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.xenial_amd64.deb" -sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.xenial_amd64.deb +sudo apt-get install -y openssl build-essential libssl-dev libxrender1 libfontconfig1 libx11-dev fontconfig xfonts-75dpi xfonts-base +wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb" +sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb sudo apt-get -f install \ No newline at end of file From 044210d188e91bd29216908de0731c50c3a7d117 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:57:03 +0300 Subject: [PATCH 68/97] Add test for #140 --- tests/pdfkit-tests.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index f3e0520..896efd5 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -107,6 +107,21 @@ def test_repeatable_options(self): self.assertTrue(test_command[idx3 + 1] == 'test_cookie2') self.assertTrue(test_command[idx3 + 2] == 'cookie_value2') + def test_empty_cookie_value(self): + roptions = { + '--page-size': 'Letter', + 'cookies': [ + ('test_cookie1',''), + ('test_cookie2','cookie_value2'), + ] + } + + r = pdfkit.PDFKit('html', 'string', options=roptions) + + test_command = r.command('test') + + self.assertTrue(test_command[idx1 + 1] == 'Letter') + def test_custom_configuration(self): conf = pdfkit.configuration() self.assertEqual('pdfkit-', conf.meta_tag_prefix) From ec22673d4e31f317ff50aa4664024f3afc23bb7e Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 01:05:15 +0300 Subject: [PATCH 69/97] Travis still fails --- travis/before-script.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/travis/before-script.sh b/travis/before-script.sh index 58d7286..81fda49 100755 --- a/travis/before-script.sh +++ b/travis/before-script.sh @@ -2,7 +2,6 @@ WKHTML2PDF_VERSION='0.12.6-1' -sudo apt-get install -y openssl build-essential libssl-dev libxrender1 libfontconfig1 libx11-dev fontconfig xfonts-75dpi xfonts-base +sudo apt-get install -y build-essential xorg libssl-dev libxrender-dev wget gdebi wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb" -sudo dpkg -i wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb -sudo apt-get -f install \ No newline at end of file +sudo gdebi --n wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb From c8a896c7ee34d152caf1b8feee456d2b171e5538 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 10:36:55 +0300 Subject: [PATCH 70/97] hide cmd window on Windows when running subprocess --- pdfkit/configuration.py | 7 ++++++- pdfkit/pdfkit.py | 29 ++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index 4555fca..11785e1 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -17,8 +17,13 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): try: if not self.wkhtmltopdf: if sys.platform == 'win32': + #hide cmd window + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + self.wkhtmltopdf = subprocess.Popen( - ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] + ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE, startupinfo=startupinfo).communicate()[0] else: self.wkhtmltopdf = subprocess.Popen( ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 15d1d76..64c9951 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -154,13 +154,28 @@ def handle_error(exit_code, stderr): def to_pdf(self, path=None): args = self.command(path) - result = subprocess.Popen( - args, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=self.environ - ) + if sys.platform == 'win32': + #hide cmd window + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + + result = subprocess.Popen( + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.environ, + startupinfo=startupinfo + ) + else: + result = subprocess.Popen( + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.environ + ) # If the source is a string then we will pipe it into wkhtmltopdf. # If we want to add custom CSS to file then we read input file to From 5bd73d2f84cd77464f97526a306766dc8a666df3 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:26:10 +0300 Subject: [PATCH 71/97] update error handling in Configuration class --- pdfkit/configuration.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index bec847d..4555fca 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -2,6 +2,10 @@ import os import subprocess import sys +try: + FileNotFoundError +except NameError: + FileNotFoundError = IOError class Configuration(object): @@ -10,23 +14,29 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): self.wkhtmltopdf = wkhtmltopdf - if not self.wkhtmltopdf: - if sys.platform == 'win32': - self.wkhtmltopdf = subprocess.Popen( - ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].split()[0].strip() - else: - self.wkhtmltopdf = subprocess.Popen( - ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].split()[0].strip() - try: + if not self.wkhtmltopdf: + if sys.platform == 'win32': + self.wkhtmltopdf = subprocess.Popen( + ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] + else: + self.wkhtmltopdf = subprocess.Popen( + ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] + + lines = self.wkhtmltopdf.splitlines() + if len(lines) > 0: + self.wkhtmltopdf = lines[0].strip() + with open(self.wkhtmltopdf) as f: pass - except IOError: + except (IOError, FileNotFoundError) as e: raise IOError('No wkhtmltopdf executable found: "%s"\n' 'If this file exists please check that this process can ' - 'read it. Otherwise please install wkhtmltopdf - ' + 'read it or you can pass path to it manually in method call, ' + 'check README. Otherwise please install wkhtmltopdf - ' 'https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf' % self.wkhtmltopdf) + self.environ = environ if not self.environ: From 5077924e311f02afef8b523637e26337a6c66660 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 00:57:03 +0300 Subject: [PATCH 72/97] Add test for #140 --- tests/pdfkit-tests.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index f3e0520..896efd5 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -107,6 +107,21 @@ def test_repeatable_options(self): self.assertTrue(test_command[idx3 + 1] == 'test_cookie2') self.assertTrue(test_command[idx3 + 2] == 'cookie_value2') + def test_empty_cookie_value(self): + roptions = { + '--page-size': 'Letter', + 'cookies': [ + ('test_cookie1',''), + ('test_cookie2','cookie_value2'), + ] + } + + r = pdfkit.PDFKit('html', 'string', options=roptions) + + test_command = r.command('test') + + self.assertTrue(test_command[idx1 + 1] == 'Letter') + def test_custom_configuration(self): conf = pdfkit.configuration() self.assertEqual('pdfkit-', conf.meta_tag_prefix) From ee1a7bc1953e12465395582c330a78115206bd15 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 10:36:55 +0300 Subject: [PATCH 73/97] hide cmd window on Windows when running subprocess Fixes #144 --- pdfkit/configuration.py | 7 ++++++- pdfkit/pdfkit.py | 29 ++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/pdfkit/configuration.py b/pdfkit/configuration.py index 4555fca..11785e1 100644 --- a/pdfkit/configuration.py +++ b/pdfkit/configuration.py @@ -17,8 +17,13 @@ def __init__(self, wkhtmltopdf='', meta_tag_prefix='pdfkit-', environ=''): try: if not self.wkhtmltopdf: if sys.platform == 'win32': + #hide cmd window + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + self.wkhtmltopdf = subprocess.Popen( - ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] + ['where.exe', 'wkhtmltopdf'], stdout=subprocess.PIPE, startupinfo=startupinfo).communicate()[0] else: self.wkhtmltopdf = subprocess.Popen( ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0] diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 15d1d76..64c9951 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -154,13 +154,28 @@ def handle_error(exit_code, stderr): def to_pdf(self, path=None): args = self.command(path) - result = subprocess.Popen( - args, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=self.environ - ) + if sys.platform == 'win32': + #hide cmd window + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + + result = subprocess.Popen( + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.environ, + startupinfo=startupinfo + ) + else: + result = subprocess.Popen( + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=self.environ + ) # If the source is a string then we will pipe it into wkhtmltopdf. # If we want to add custom CSS to file then we read input file to From a84800103cd23b02dc68f6c965a7627645172bcf Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 15:22:24 +0300 Subject: [PATCH 74/97] update test for issue 140 --- tests/pdfkit-tests.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 896efd5..4be9d92 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -107,21 +107,6 @@ def test_repeatable_options(self): self.assertTrue(test_command[idx3 + 1] == 'test_cookie2') self.assertTrue(test_command[idx3 + 2] == 'cookie_value2') - def test_empty_cookie_value(self): - roptions = { - '--page-size': 'Letter', - 'cookies': [ - ('test_cookie1',''), - ('test_cookie2','cookie_value2'), - ] - } - - r = pdfkit.PDFKit('html', 'string', options=roptions) - - test_command = r.command('test') - - self.assertTrue(test_command[idx1 + 1] == 'Letter') - def test_custom_configuration(self): conf = pdfkit.configuration() self.assertEqual('pdfkit-', conf.meta_tag_prefix) @@ -435,5 +420,31 @@ def test_issue_42_encode_file_with_unicode_char(self): output = r.to_pdf() self.assertEqual(output[:4].decode('utf-8'), '%PDF') + def test_issue_140_empty_cookie_value(self): + roptions_bad = { + '--page-size': 'Letter', + 'cookie': [ + ('test_cookie1',''), + ('test_cookie2','cookie_value2'), + ] + } + + roptions_good = { + '--page-size': 'Letter', + 'cookie': [ + ('test_cookie1','""'), + ('test_cookie2','cookie_value2'), + ] + } + + r1 = pdfkit.PDFKit('html', 'string', options=roptions_bad) + + self.assertRaises(AssertionError, r1.to_pdf) + + r2 = pdfkit.PDFKit('html', 'string', options=roptions_good) + output2 = r2.to_pdf() + + self.assertEqual(output2[:4].decode('utf-8'), '%PDF') + if __name__ == "__main__": unittest.main() From 1417a96202043f8da41e96b368ad41ad7f3ac8bc Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 15:23:29 +0300 Subject: [PATCH 75/97] update README with empty cookie value example --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index ad49c8c..52f0559 100644 --- a/README.rst +++ b/README.rst @@ -88,6 +88,7 @@ You can specify all wkhtmltopdf `options Date: Sat, 20 Mar 2021 15:29:01 +0300 Subject: [PATCH 76/97] Remove assert in check for basestring Fixes #157 --- pdfkit/pdfkit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 64c9951..c3807b2 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -9,8 +9,8 @@ import codecs try: # Python 2.x and 3.x support for checking string types - assert basestring - assert unicode + basestring + unicode except NameError: basestring = str unicode = str From bc7e71dc1ed2828f204a91b58e4e846cc310c16e Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 16:01:23 +0300 Subject: [PATCH 77/97] Normalize Boolean argument values in options to empty string Fixes #169 --- pdfkit/pdfkit.py | 3 ++- tests/pdfkit-tests.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index c3807b2..c30880f 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -238,7 +238,8 @@ def _normalize_options(self, options): for optval in value: yield (normalized_key, optval) else: - yield (normalized_key, unicode(value) if value else value) + normalized_value = '' if isinstance(value,bool) else value + yield (normalized_key, unicode(normalized_value) if value else value) def _normalize_arg(self, arg): return arg.lower() diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 4be9d92..2593f0c 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -446,5 +446,16 @@ def test_issue_140_empty_cookie_value(self): self.assertEqual(output2[:4].decode('utf-8'), '%PDF') + def test_issue_169_quiet_boolean_True(self): + options = { + 'outline': '', + 'footer-line': None, + 'quiet': True + } + + r = pdfkit.PDFKit('html', 'string', options=options) + output = r.to_pdf() + self.assertEqual(output[:4].decode('utf-8'), '%PDF') + if __name__ == "__main__": unittest.main() From 5a992b7eb550619f6ec2d6404002bbac0e357b60 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 18:11:36 +0300 Subject: [PATCH 78/97] Switch to quiet output by default, Add verbose parameter to API calls Fixes #84 --- README.rst | 31 +++++++++++++++++++++++++------ pdfkit/api.py | 15 +++++++++------ pdfkit/pdfkit.py | 10 ++++++++-- tests/pdfkit-tests.py | 37 ++++++++++++++++++++++++++++--------- 4 files changed, 70 insertions(+), 23 deletions(-) diff --git a/README.rst b/README.rst index 52f0559..a225c26 100644 --- a/README.rst +++ b/README.rst @@ -97,15 +97,11 @@ You can specify all wkhtmltopdf `options 1 and stderr.splitlines()[-2].strip() == 'Done': return if 'cannot connect to X server' in stderr: diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index 2593f0c..b183a7a 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -201,8 +201,9 @@ def test_skip_nonpdfkit_tags(self): def test_toc_handling_without_options(self): r = pdfkit.PDFKit('hmtl', 'string', toc={'xsl-style-sheet': 'test.xsl'}) - self.assertEqual(r.command()[1], 'toc') - self.assertEqual(r.command()[2], '--xsl-style-sheet') + self.assertEqual(r.command()[1], '--quiet') + self.assertEqual(r.command()[2], 'toc') + self.assertEqual(r.command()[3], '--xsl-style-sheet') def test_toc_with_options(self): options = { @@ -217,16 +218,18 @@ def test_toc_with_options(self): command = r.command() - self.assertEqual(command[1 + len(options) * 2], 'toc') - self.assertEqual(command[1 + len(options) * 2 + 1], '--xsl-style-sheet') + self.assertEqual(command[1 + len(options) * 2], '--quiet') + self.assertEqual(command[2 + len(options) * 2], 'toc') + self.assertEqual(command[2 + len(options) * 2 + 1], '--xsl-style-sheet') def test_cover_without_options(self): r = pdfkit.PDFKit('html', 'string', cover='test.html') command = r.command() - self.assertEqual(command[1], 'cover') - self.assertEqual(command[2], 'test.html') + self.assertEqual(command[1], '--quiet') + self.assertEqual(command[2], 'cover') + self.assertEqual(command[3], 'test.html') def test_cover_with_options(self): options = { @@ -241,8 +244,9 @@ def test_cover_with_options(self): command = r.command() - self.assertEqual(command[1 + len(options) * 2], 'cover') - self.assertEqual(command[1 + len(options) * 2 + 1], 'test.html') + self.assertEqual(command[1 + len(options) * 2], '--quiet') + self.assertEqual(command[2 + len(options) * 2], 'cover') + self.assertEqual(command[2 + len(options) * 2 + 1], 'test.html') def test_cover_and_toc(self): options = { @@ -289,10 +293,25 @@ def test_filter_empty_and_none_values_in_opts(self): 'quiet': False } - r = pdfkit.PDFKit('html', 'string', options=options) + r = pdfkit.PDFKit('html', 'string', options=options, verbose=True) cmd = r.command() self.assertEqual(len(cmd), 6) + def test_verbose_option(self): + options = { + 'outline': '', + 'footer-line': None, + 'quiet': True + } + + r = pdfkit.PDFKit('html', 'string') + cmd = r.command() + self.assertTrue('--quiet' in cmd) + + r = pdfkit.PDFKit('html', 'string', options=options) + cmd = r.command() + self.assertTrue('--quiet' in cmd) + class TestPDFKitGeneration(unittest.TestCase): """Test to_pdf() method""" From 42ef3bb73914291994883346e166a944cba1878c Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 18:16:36 +0300 Subject: [PATCH 79/97] Fix README typos --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a225c26..6a8c13a 100644 --- a/README.rst +++ b/README.rst @@ -178,13 +178,13 @@ Troubleshooting Debugging issues with PDF generation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you struggling to generate correct PDF firstly you should check wkhtmltopdf output for some clues, you can get it by passing ``verbose=True`` to API calls: +If you struggling to generate correct PDF firstly you should check ``wkhtmltopdf`` output for some clues, you can get it by passing ``verbose=True`` to API calls: .. code-block:: python pdfkit.from_url('http://google.com', 'out.pdf', verbose=True) -If you are getting strage results in PDF or some option looks like its ignored you should try to run ``wkhtmltopdf`` directly to see if it produces the same result. You can get CLI command by creating ``pdfkit.PDFKit`` class directly and then calling its ``command()`` method: +If you are getting strange results in PDF or some option looks like its ignored you should try to run ``wkhtmltopdf`` directly to see if it produces the same result. You can get CLI command by creating ``pdfkit.PDFKit`` class directly and then calling its ``command()`` method: .. code-block:: python From 0d6c4a106621f0f07829bc5a1c020d6426f6defc Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sat, 20 Mar 2021 18:56:30 +0300 Subject: [PATCH 80/97] Update changelog --- HISTORY.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 4c8715d..7e009bf 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,13 @@ Changelog --------- +* `1.0.0` + * By default PDFKit now passes "quiet" option to wkhtmltopdf. Now if you need to get output you should pass "verbose=True" to API calls + * Fix different issues with searching for wkhtmltopdf binary + * Update error handling for wkhtmltopdf + * Fix different issues with options handling + * Better handling of unicode input + * Actualize Travis build scripts + * Update README * `0.6.1` * Fix regression on python 3+ when trying to decode pdf output * `0.6.0` From d364d6d8d52698f50007caf309730d4dd14db5e9 Mon Sep 17 00:00:00 2001 From: Camilo Nova Date: Wed, 4 Aug 2021 09:52:26 -0500 Subject: [PATCH 81/97] Fixes typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6a8c13a..c26fae2 100644 --- a/README.rst +++ b/README.rst @@ -149,7 +149,7 @@ You can also pass any options through meta tags in your HTML: Configuration ------------- -Each API call takes an optional configuration paramater. This should be an instance of ``pdfkit.configuration()`` API call. It takes the configuration options as initial paramaters. The available options are: +Each API call takes an optional configuration parameter. This should be an instance of ``pdfkit.configuration()`` API call. It takes the configuration options as initial parameters. The available options are: * ``wkhtmltopdf`` - the location of the ``wkhtmltopdf`` binary. By default ``pdfkit`` will attempt to locate this using ``which`` (on UNIX type systems) or ``where`` (on Windows). * ``meta_tag_prefix`` - the prefix for ``pdfkit`` specific meta tags - by default this is ``pdfkit-`` From 640d10b52dd5a2a8d971ae6f761ff01dc016b0b9 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 19:47:57 +0300 Subject: [PATCH 82/97] fix test for empty cookie value --- tests/pdfkit-tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pdfkit-tests.py b/tests/pdfkit-tests.py index da408bd..969afbd 100644 --- a/tests/pdfkit-tests.py +++ b/tests/pdfkit-tests.py @@ -111,7 +111,7 @@ def test_empty_cookie_value(self): roptions = { '--page-size': 'Letter', 'cookies': [ - ('test_cookie1',''), + ('test_cookie1','""'), ('test_cookie2','cookie_value2'), ] } @@ -120,6 +120,7 @@ def test_empty_cookie_value(self): test_command = r.command('test') + idx1 = test_command.index('--page-size') # Raise exception in case of not found self.assertTrue(test_command[idx1 + 1] == 'Letter') def test_custom_configuration(self): From cdc54a4979a88ba69d48c7b7cd871ce1e3bcdacf Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 19:59:20 +0300 Subject: [PATCH 83/97] Handle None stderr and stdout returns from communicate in to_pdf Fixes #187 --- pdfkit/pdfkit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 2078a59..784aad0 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -195,7 +195,7 @@ def to_pdf(self, path=None): input = None stdout, stderr = result.communicate(input=input) - stderr = stderr or stdout + stderr = stderr or stdout or b"" stderr = stderr.decode('utf-8', errors='replace') exit_code = result.returncode self.handle_error(exit_code, stderr) From 142c4a578659dc94dc37084065d42072fd310f2e Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 20:41:48 +0300 Subject: [PATCH 84/97] add github actions CI --- .github/workflows/main.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/main.yaml diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..0232039 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,28 @@ +name: Run Tests + +on: [push, pull_request] + +jobs: + testing: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + + - name: Install dependencies + run: | + ./travis/before-script.sh + python -m pip install --upgrade pip + pip install nose + + - name: Run tests + run: nosetests -w tests \ No newline at end of file From 1c44826c9f4ffa5b90e83859ca6e6c2ae61ae7e1 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 20:47:32 +0300 Subject: [PATCH 85/97] remove 3.4 from ci --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0232039..cf59521 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] fail-fast: false steps: From 403e1b205a8962a3361656a92c55fb8ce22e9d57 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 20:50:17 +0300 Subject: [PATCH 86/97] remove travis files --- .github/workflows/main.yaml | 4 ++-- .travis.yml | 13 ------------- 2 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index cf59521..4de9cfc 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,4 +1,4 @@ -name: Run Tests +name: Build on: [push, pull_request] @@ -20,7 +20,7 @@ jobs: - name: Install dependencies run: | - ./travis/before-script.sh + ./ci/before-script.sh python -m pip install --upgrade pip pip install nose diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2df813d..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: python -python: - - 2.7 - - 3.4 - - 3.5 - - 3.6 - - 3.7 - - 3.8 -before_script: "./travis/before-script.sh" -script: nosetests -w tests -dist: bionic -notifications: - email: false From 630c66377b55fc28e4139a28c8efac58436172e9 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 20:52:34 +0300 Subject: [PATCH 87/97] change readme from travis to gh actions --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 6a8c13a..57bc700 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,8 @@ Python-PDFKit: HTML to PDF wrapper ================================== -.. image:: https://travis-ci.org/JazzCore/python-pdfkit.png?branch=master - :target: https://travis-ci.org/JazzCore/python-pdfkit +.. image:: https://github.com/JazzCore/python-pdfkit/actions/workflows/main.yaml/badge.svg?branch=master + :target: https://github.com/JazzCore/python-pdfkit/actions/workflows/main.yaml .. image:: https://badge.fury.io/py/pdfkit.svg :target: http://badge.fury.io/py/pdfkit From 677532df4b64ae6eacf0c8231cd27bc06500b628 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 20:54:21 +0300 Subject: [PATCH 88/97] rename travis dir to ci --- {travis => ci}/before-script.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {travis => ci}/before-script.sh (100%) mode change 100755 => 100644 diff --git a/travis/before-script.sh b/ci/before-script.sh old mode 100755 new mode 100644 similarity index 100% rename from travis/before-script.sh rename to ci/before-script.sh From 7f5c68e9e76c6828e81e3adfeb7ed60fba96dd08 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 20:57:55 +0300 Subject: [PATCH 89/97] fix gh actions --- .github/workflows/main.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4de9cfc..451b8b5 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -20,6 +20,7 @@ jobs: - name: Install dependencies run: | + chmod +x ./ci/before-script.sh ./ci/before-script.sh python -m pip install --upgrade pip pip install nose From d3b92e767266561e2237ede3af98847717b14236 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 21:18:57 +0300 Subject: [PATCH 90/97] update changelog --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 7e009bf..7e1809d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,7 +6,7 @@ Changelog * Update error handling for wkhtmltopdf * Fix different issues with options handling * Better handling of unicode input - * Actualize Travis build scripts + * Switch from Travis to GitHub Actions * Update README * `0.6.1` * Fix regression on python 3+ when trying to decode pdf output From 80956064ac71e517da4a45e1b7fca3d751637b6e Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 21:23:58 +0300 Subject: [PATCH 91/97] bump version to 1.0.0 --- pdfkit/__init__.py | 2 +- setup.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pdfkit/__init__.py b/pdfkit/__init__.py index 4d064d9..a54653a 100644 --- a/pdfkit/__init__.py +++ b/pdfkit/__init__.py @@ -4,7 +4,7 @@ """ __author__ = 'Golovanov Stanislav' -__version__ = '0.6.1' +__version__ = '1.0.0' __license__ = 'MIT' from .pdfkit import PDFKit diff --git a/setup.py b/setup.py index 251c3ab..ce454fa 100644 --- a/setup.py +++ b/setup.py @@ -43,6 +43,12 @@ def long_description(): 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Text Processing', 'Topic :: Text Processing :: General', 'Topic :: Text Processing :: Markup', From c7c609f46512f40e3bd6009bcb17bc1201316753 Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Sun, 14 Nov 2021 22:16:09 +0300 Subject: [PATCH 92/97] fix setup.py for pypi packaging --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ce454fa..5aadd99 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -import codecs from distutils.core import setup from setuptools.command.test import test as TestCommand import re @@ -20,7 +19,7 @@ def run_tests(self): def long_description(): """Pre-process the README so that PyPi can render it properly.""" - with codecs.open('README.rst', encoding='utf8') as f: + with open('README.rst') as f: rst = f.read() code_block = '(:\n\n)?\.\. code-block::.*' rst = re.sub(code_block, '::', rst) From c83fa250f85b210f4a0f05ca613ec0fb9580732e Mon Sep 17 00:00:00 2001 From: Stanislav Golovanov Date: Tue, 30 Nov 2021 12:40:41 +0300 Subject: [PATCH 93/97] Fix README link to debian/ubuntu build script Fixes #211 #213 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9119ea3..602d2e2 100644 --- a/README.rst +++ b/README.rst @@ -35,7 +35,7 @@ Installation $ brew install homebrew/cask/wkhtmltopdf -**Warning!** Version in debian/ubuntu repos have reduced functionality (because it compiled without the wkhtmltopdf QT patches), such as adding outlines, headers, footers, TOC etc. To use this options you should install static binary from `wkhtmltopdf `_ site or you can use `this script `_. +**Warning!** Version in debian/ubuntu repos have reduced functionality (because it compiled without the wkhtmltopdf QT patches), such as adding outlines, headers, footers, TOC etc. To use this options you should install static binary from `wkhtmltopdf `_ site or you can use `this script `_ (written for CI servers with Ubuntu 18.04 Bionic, but it could work on other Ubuntu/Debian versions). * Windows and other options: check wkhtmltopdf `homepage `_ for binary installers From 49b4550a6baac721a2e5c6efd65a2d5c869d27c6 Mon Sep 17 00:00:00 2001 From: Greg Klupar Date: Tue, 22 Feb 2022 09:29:45 -0900 Subject: [PATCH 94/97] Fix for issue #218 --- pdfkit/pdfkit.py | 11 ++++------- pdfkit/source.py | 10 +++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index 784aad0..cde58d1 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -7,13 +7,10 @@ from .configuration import Configuration import io import codecs -try: - # Python 2.x and 3.x support for checking string types - basestring - unicode -except NameError: - basestring = str - unicode = str + +# Python 2.x and 3.x support for checking string types +basestring = str.__mro__[-2] +unicode = type(u'') class PDFKit(object): diff --git a/pdfkit/source.py b/pdfkit/source.py index 7b115b6..3005bbd 100644 --- a/pdfkit/source.py +++ b/pdfkit/source.py @@ -1,14 +1,10 @@ # -*- coding: utf-8 -*- import os import io -try: - # Python 2.x and 3.x support for checking string types - assert basestring - assert unicode -except NameError: - basestring = str - unicode = str +# Python 2.x and 3.x support for checking string types +basestring = str.__mro__[-2] +unicode = type(u'') class Source(object): From 86cbaa88958cf869c14edcd1abe3ad226f947831 Mon Sep 17 00:00:00 2001 From: Keegan Date: Mon, 30 Jan 2023 10:46:49 +0200 Subject: [PATCH 95/97] Add support for Python 3.10 and Python 3.11 --- .github/workflows/main.yaml | 2 +- HISTORY.rst | 2 ++ pdfkit/__init__.py | 2 +- setup.py | 2 ++ tox.ini | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 451b8b5..72b597e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11] fail-fast: false steps: diff --git a/HISTORY.rst b/HISTORY.rst index 7e1809d..f0abb06 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,7 @@ Changelog --------- +* `1.0.1` + * Add support for Python 3.10 and Python 3.11 * `1.0.0` * By default PDFKit now passes "quiet" option to wkhtmltopdf. Now if you need to get output you should pass "verbose=True" to API calls * Fix different issues with searching for wkhtmltopdf binary diff --git a/pdfkit/__init__.py b/pdfkit/__init__.py index a54653a..31c052e 100644 --- a/pdfkit/__init__.py +++ b/pdfkit/__init__.py @@ -4,7 +4,7 @@ """ __author__ = 'Golovanov Stanislav' -__version__ = '1.0.0' +__version__ = '1.0.1' __license__ = 'MIT' from .pdfkit import PDFKit diff --git a/setup.py b/setup.py index 5aadd99..99262c4 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,8 @@ def long_description(): 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Topic :: Text Processing', 'Topic :: Text Processing :: General', 'Topic :: Text Processing :: Markup', diff --git a/tox.ini b/tox.ini index 61a0a83..a193343 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py33,py34,py35 +envlist = py27,py33,py34,py35,py36,py37,py38,py39,py310,py311 [testenv] deps = pytest From b7bf798b946fa5655f8e82f0d80dec6b6b13d414 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Thu, 14 Sep 2023 10:02:50 +0300 Subject: [PATCH 96/97] Drop support for Python <= 3.7 --- .github/workflows/main.yaml | 8 ++++---- HISTORY.rst | 4 ++-- README.rst | 4 ++-- ci/before-script.sh | 4 ++-- pdfkit/__init__.py | 2 +- pdfkit/pdfkit.py | 12 ++++-------- setup.py | 7 ------- tox.ini | 2 +- 8 files changed, 16 insertions(+), 27 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 72b597e..ed3ed7e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -7,13 +7,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11] + python-version: [3.8, 3.9, '3.10', 3.11] fail-fast: false steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -23,7 +23,7 @@ jobs: chmod +x ./ci/before-script.sh ./ci/before-script.sh python -m pip install --upgrade pip - pip install nose + pip install pynose - name: Run tests - run: nosetests -w tests \ No newline at end of file + run: nosetests -w tests diff --git a/HISTORY.rst b/HISTORY.rst index f0abb06..7470f24 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,7 +1,7 @@ Changelog --------- -* `1.0.1` - * Add support for Python 3.10 and Python 3.11 +* `2.0.0` + * Drop support for Python <= 3.7 * `1.0.0` * By default PDFKit now passes "quiet" option to wkhtmltopdf. Now if you need to get output you should pass "verbose=True" to API calls * Fix different issues with searching for wkhtmltopdf binary diff --git a/README.rst b/README.rst index 602d2e2..f0c9bd3 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ Python-PDFKit: HTML to PDF wrapper .. image:: https://badge.fury.io/py/pdfkit.svg :target: http://badge.fury.io/py/pdfkit -Python 2 and 3 wrapper for wkhtmltopdf utility to convert HTML to PDF using Webkit. +Python 3 wrapper for wkhtmltopdf utility to convert HTML to PDF using Webkit. This is adapted version of `ruby PDFKit `_ library, so big thanks to them! @@ -19,7 +19,7 @@ Installation .. code-block:: bash - $ pip install pdfkit (or pip3 for python3) + $ pip install pdfkit 2. Install wkhtmltopdf: diff --git a/ci/before-script.sh b/ci/before-script.sh index 81fda49..92ae41b 100644 --- a/ci/before-script.sh +++ b/ci/before-script.sh @@ -2,6 +2,6 @@ WKHTML2PDF_VERSION='0.12.6-1' -sudo apt-get install -y build-essential xorg libssl-dev libxrender-dev wget gdebi +sudo apt install -y build-essential xorg libssl-dev libxrender-dev wget wget "https://github.com/wkhtmltopdf/packaging/releases/download/${WKHTML2PDF_VERSION}/wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb" -sudo gdebi --n wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb +sudo apt install -y ./wkhtmltox_${WKHTML2PDF_VERSION}.bionic_amd64.deb diff --git a/pdfkit/__init__.py b/pdfkit/__init__.py index 31c052e..027c95c 100644 --- a/pdfkit/__init__.py +++ b/pdfkit/__init__.py @@ -4,7 +4,7 @@ """ __author__ = 'Golovanov Stanislav' -__version__ = '1.0.1' +__version__ = '2.0.0' __license__ = 'MIT' from .pdfkit import PDFKit diff --git a/pdfkit/pdfkit.py b/pdfkit/pdfkit.py index cde58d1..88b3c92 100644 --- a/pdfkit/pdfkit.py +++ b/pdfkit/pdfkit.py @@ -8,10 +8,6 @@ import io import codecs -# Python 2.x and 3.x support for checking string types -basestring = str.__mro__[-2] -unicode = type(u'') - class PDFKit(object): """ @@ -113,7 +109,7 @@ def _command(self, path=None): if self.source.isString() or self.source.isFileObj(): yield '-' else: - if isinstance(self.source.source, basestring): + if isinstance(self.source.source, str): yield self.source.to_s() else: for s in self.source.source: @@ -158,7 +154,7 @@ def to_pdf(self, path=None): args = self.command(path) if sys.platform == 'win32': - #hide cmd window + # hide cmd window startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.wShowWindow = subprocess.SW_HIDE @@ -241,8 +237,8 @@ def _normalize_options(self, options): for optval in value: yield (normalized_key, optval) else: - normalized_value = '' if isinstance(value,bool) else value - yield (normalized_key, unicode(normalized_value) if value else value) + normalized_value = '' if isinstance(value, bool) else value + yield (normalized_key, str(normalized_value) if value else value) def _normalize_arg(self, arg): return arg.lower() diff --git a/setup.py b/setup.py index 99262c4..4006a34 100644 --- a/setup.py +++ b/setup.py @@ -39,13 +39,6 @@ def long_description(): author_email='stgolovanov@gmail.com', classifiers=[ 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', diff --git a/tox.ini b/tox.ini index a193343..2ccafd3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py33,py34,py35,py36,py37,py38,py39,py310,py311 +envlist = py38,py39,py310,py311 [testenv] deps = pytest From 9962afa17c958869a8d33144044703d1746cf588 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Thu, 14 Sep 2023 10:15:01 +0300 Subject: [PATCH 97/97] deprecation warning --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index f0c9bd3..2822f0c 100644 --- a/README.rst +++ b/README.rst @@ -12,6 +12,11 @@ Python 3 wrapper for wkhtmltopdf utility to convert HTML to PDF using Webkit. This is adapted version of `ruby PDFKit `_ library, so big thanks to them! +Deprecation Warning +------------------- + +This library has been deprecated to match the `wkhtmltopdf project status `_. + Installation ------------