Skip to content

Commit f4c4096

Browse files
committed
Merge branch 'master' into dev
2 parents c8a896c + 42ef3bb commit f4c4096

4 files changed

Lines changed: 112 additions & 26 deletions

File tree

README.rst

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ You can specify all wkhtmltopdf `options <http://wkhtmltopdf.org/usage/wkhtmltop
8888
('Accept-Encoding', 'gzip')
8989
],
9090
'cookie': [
91+
('cookie-empty-value', '""')
9192
('cookie-name1', 'cookie-value1'),
9293
('cookie-name2', 'cookie-value2'),
9394
],
@@ -96,15 +97,11 @@ You can specify all wkhtmltopdf `options <http://wkhtmltopdf.org/usage/wkhtmltop
9697
9798
pdfkit.from_url('http://google.com', 'out.pdf', options=options)
9899
99-
By default, PDFKit will show all ``wkhtmltopdf`` output. If you don't want it, you need to pass ``quiet`` option:
100+
By default, PDFKit will run ``wkhtmltopdf`` with ``quiet`` option turned on, since in most cases output is not needed and can cause excessive memory usage and corrupted results. If need to get ``wkhtmltopdf`` output you should pass ``verbose=True`` to API calls:
100101

101102
.. code-block:: python
102103
103-
options = {
104-
'quiet': ''
105-
}
106-
107-
pdfkit.from_url('google.com', 'out.pdf', options=options)
104+
pdfkit.from_url('google.com', 'out.pdf', verbose=True)
108105
109106
Due to wkhtmltopdf command syntax, **TOC** and **Cover** options must be specified separately. If you need cover before TOC, use ``cover_first`` option:
110107

@@ -178,6 +175,29 @@ Also you can use ``configuration()`` call to check if wkhtmltopdf is present in
178175
Troubleshooting
179176
---------------
180177

178+
Debugging issues with PDF generation
179+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
180+
181+
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:
182+
183+
.. code-block:: python
184+
185+
pdfkit.from_url('http://google.com', 'out.pdf', verbose=True)
186+
187+
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:
188+
189+
.. code-block:: python
190+
191+
import pdfkit
192+
193+
r = pdfkit.PDFKit('html', 'string', verbose=True)
194+
print(' '.join(r.command()))
195+
# try running wkhtmltopdf to create PDF
196+
output = r.to_pdf()
197+
198+
Common errors:
199+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
200+
181201
- ``IOError: 'No wkhtmltopdf executable found'``:
182202

183203
Make sure that you have wkhtmltopdf in your `$PATH` or set via custom configuration (see preceding section). *where wkhtmltopdf* in Windows or *which wkhtmltopdf* on Linux should return actual path to binary.

pdfkit/api.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
def from_url(url, output_path=None, options=None, toc=None, cover=None,
8-
configuration=None, cover_first=False):
8+
configuration=None, cover_first=False, verbose=False):
99
"""
1010
Convert file of files from URLs to PDF document
1111
@@ -16,18 +16,19 @@ def from_url(url, output_path=None, options=None, toc=None, cover=None,
1616
:param cover: (optional) string with url/filename with a cover html page
1717
:param configuration: (optional) instance of pdfkit.configuration.Configuration()
1818
:param cover_first: (optional) if True, cover always precedes TOC
19+
:param verbose: (optional) By default '--quiet' is passed to all calls, set this to False to get wkhtmltopdf output to stdout.
1920
2021
Returns: True on success
2122
"""
2223

2324
r = PDFKit(url, 'url', options=options, toc=toc, cover=cover,
24-
configuration=configuration, cover_first=cover_first)
25+
configuration=configuration, cover_first=cover_first, verbose=verbose)
2526

2627
return r.to_pdf(output_path)
2728

2829

2930
def from_file(input, output_path=None, options=None, toc=None, cover=None, css=None,
30-
configuration=None, cover_first=False):
31+
configuration=None, cover_first=False, verbose=False):
3132
"""
3233
Convert HTML file or files to PDF document
3334
@@ -39,18 +40,19 @@ def from_file(input, output_path=None, options=None, toc=None, cover=None, css=N
3940
:param css: (optional) string with path to css file which will be added to a single input file
4041
:param configuration: (optional) instance of pdfkit.configuration.Configuration()
4142
:param cover_first: (optional) if True, cover always precedes TOC
43+
:param verbose: (optional) By default '--quiet' is passed to all calls, set this to False to get wkhtmltopdf output to stdout.
4244
4345
Returns: True on success
4446
"""
4547

4648
r = PDFKit(input, 'file', options=options, toc=toc, cover=cover, css=css,
47-
configuration=configuration, cover_first=cover_first)
49+
configuration=configuration, cover_first=cover_first, verbose=verbose)
4850

4951
return r.to_pdf(output_path)
5052

5153

5254
def from_string(input, output_path=None, options=None, toc=None, cover=None, css=None,
53-
configuration=None, cover_first=False):
55+
configuration=None, cover_first=False, verbose=False):
5456
"""
5557
Convert given string or strings to PDF document
5658
@@ -62,12 +64,13 @@ def from_string(input, output_path=None, options=None, toc=None, cover=None, css
6264
:param css: (optional) string with path to css file which will be added to a input string
6365
:param configuration: (optional) instance of pdfkit.configuration.Configuration()
6466
:param cover_first: (optional) if True, cover always precedes TOC
67+
:param verbose: (optional) By default '--quiet' is passed to all calls, set this to False to get wkhtmltopdf output to stdout.
6568
6669
Returns: True on success
6770
"""
6871

6972
r = PDFKit(input, 'string', options=options, toc=toc, cover=cover, css=css,
70-
configuration=configuration, cover_first=cover_first)
73+
configuration=configuration, cover_first=cover_first, verbose=verbose)
7174

7275
return r.to_pdf(output_path)
7376

pdfkit/pdfkit.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import codecs
1010
try:
1111
# Python 2.x and 3.x support for checking string types
12-
assert basestring
13-
assert unicode
12+
basestring
13+
unicode
1414
except NameError:
1515
basestring = str
1616
unicode = str
@@ -39,7 +39,7 @@ def __str__(self):
3939
return self.msg
4040

4141
def __init__(self, url_or_file, type_, options=None, toc=None, cover=None,
42-
css=None, configuration=None, cover_first=False):
42+
css=None, configuration=None, cover_first=False, verbose=False):
4343

4444
self.source = Source(url_or_file, type_)
4545
self.configuration = (Configuration() if configuration is None
@@ -61,6 +61,7 @@ def __init__(self, url_or_file, type_, options=None, toc=None, cover=None,
6161
self.toc = {} if toc is None else toc
6262
self.cover = cover
6363
self.cover_first = cover_first
64+
self.verbose = verbose
6465
self.css = css
6566
self.stylesheets = []
6667

@@ -89,6 +90,9 @@ def _command(self, path=None):
8990

9091
yield self.wkhtmltopdf
9192

93+
if not self.verbose:
94+
self.options.update({'--quiet': ''})
95+
9296
for argpart in self._genargs(self.options):
9397
if argpart:
9498
yield argpart
@@ -133,10 +137,12 @@ def handle_error(exit_code, stderr):
133137
if exit_code == 0:
134138
return
135139

140+
stderr_lines = stderr.splitlines()
141+
136142
# Sometimes wkhtmltopdf will exit with non-zero
137143
# even if it finishes generation.
138144
# If will display 'Done' in the second last line
139-
if stderr.splitlines()[-2].strip() == 'Done':
145+
if len(stderr_lines) > 1 and stderr.splitlines()[-2].strip() == 'Done':
140146
return
141147

142148
if 'cannot connect to X server' in stderr:
@@ -238,7 +244,8 @@ def _normalize_options(self, options):
238244
for optval in value:
239245
yield (normalized_key, optval)
240246
else:
241-
yield (normalized_key, unicode(value) if value else value)
247+
normalized_value = '' if isinstance(value,bool) else value
248+
yield (normalized_key, unicode(normalized_value) if value else value)
242249

243250
def _normalize_arg(self, arg):
244251
return arg.lower()

tests/pdfkit-tests.py

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,9 @@ def test_skip_nonpdfkit_tags(self):
216216

217217
def test_toc_handling_without_options(self):
218218
r = pdfkit.PDFKit('hmtl', 'string', toc={'xsl-style-sheet': 'test.xsl'})
219-
self.assertEqual(r.command()[1], 'toc')
220-
self.assertEqual(r.command()[2], '--xsl-style-sheet')
219+
self.assertEqual(r.command()[1], '--quiet')
220+
self.assertEqual(r.command()[2], 'toc')
221+
self.assertEqual(r.command()[3], '--xsl-style-sheet')
221222

222223
def test_toc_with_options(self):
223224
options = {
@@ -232,16 +233,18 @@ def test_toc_with_options(self):
232233

233234
command = r.command()
234235

235-
self.assertEqual(command[1 + len(options) * 2], 'toc')
236-
self.assertEqual(command[1 + len(options) * 2 + 1], '--xsl-style-sheet')
236+
self.assertEqual(command[1 + len(options) * 2], '--quiet')
237+
self.assertEqual(command[2 + len(options) * 2], 'toc')
238+
self.assertEqual(command[2 + len(options) * 2 + 1], '--xsl-style-sheet')
237239

238240
def test_cover_without_options(self):
239241
r = pdfkit.PDFKit('html', 'string', cover='test.html')
240242

241243
command = r.command()
242244

243-
self.assertEqual(command[1], 'cover')
244-
self.assertEqual(command[2], 'test.html')
245+
self.assertEqual(command[1], '--quiet')
246+
self.assertEqual(command[2], 'cover')
247+
self.assertEqual(command[3], 'test.html')
245248

246249
def test_cover_with_options(self):
247250
options = {
@@ -256,8 +259,9 @@ def test_cover_with_options(self):
256259

257260
command = r.command()
258261

259-
self.assertEqual(command[1 + len(options) * 2], 'cover')
260-
self.assertEqual(command[1 + len(options) * 2 + 1], 'test.html')
262+
self.assertEqual(command[1 + len(options) * 2], '--quiet')
263+
self.assertEqual(command[2 + len(options) * 2], 'cover')
264+
self.assertEqual(command[2 + len(options) * 2 + 1], 'test.html')
261265

262266
def test_cover_and_toc(self):
263267
options = {
@@ -304,10 +308,25 @@ def test_filter_empty_and_none_values_in_opts(self):
304308
'quiet': False
305309
}
306310

307-
r = pdfkit.PDFKit('html', 'string', options=options)
311+
r = pdfkit.PDFKit('html', 'string', options=options, verbose=True)
308312
cmd = r.command()
309313
self.assertEqual(len(cmd), 6)
310314

315+
def test_verbose_option(self):
316+
options = {
317+
'outline': '',
318+
'footer-line': None,
319+
'quiet': True
320+
}
321+
322+
r = pdfkit.PDFKit('html', 'string')
323+
cmd = r.command()
324+
self.assertTrue('--quiet' in cmd)
325+
326+
r = pdfkit.PDFKit('html', 'string', options=options)
327+
cmd = r.command()
328+
self.assertTrue('--quiet' in cmd)
329+
311330

312331
class TestPDFKitGeneration(unittest.TestCase):
313332
"""Test to_pdf() method"""
@@ -435,5 +454,42 @@ def test_issue_42_encode_file_with_unicode_char(self):
435454
output = r.to_pdf()
436455
self.assertEqual(output[:4].decode('utf-8'), '%PDF')
437456

457+
def test_issue_140_empty_cookie_value(self):
458+
roptions_bad = {
459+
'--page-size': 'Letter',
460+
'cookie': [
461+
('test_cookie1',''),
462+
('test_cookie2','cookie_value2'),
463+
]
464+
}
465+
466+
roptions_good = {
467+
'--page-size': 'Letter',
468+
'cookie': [
469+
('test_cookie1','""'),
470+
('test_cookie2','cookie_value2'),
471+
]
472+
}
473+
474+
r1 = pdfkit.PDFKit('html', 'string', options=roptions_bad)
475+
476+
self.assertRaises(AssertionError, r1.to_pdf)
477+
478+
r2 = pdfkit.PDFKit('html', 'string', options=roptions_good)
479+
output2 = r2.to_pdf()
480+
481+
self.assertEqual(output2[:4].decode('utf-8'), '%PDF')
482+
483+
def test_issue_169_quiet_boolean_True(self):
484+
options = {
485+
'outline': '',
486+
'footer-line': None,
487+
'quiet': True
488+
}
489+
490+
r = pdfkit.PDFKit('html', 'string', options=options)
491+
output = r.to_pdf()
492+
self.assertEqual(output[:4].decode('utf-8'), '%PDF')
493+
438494
if __name__ == "__main__":
439495
unittest.main()

0 commit comments

Comments
 (0)