├── Dockerfile ├── LICENSE ├── README.md └── app.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openlabs/docker-wkhtmltopdf:latest 2 | MAINTAINER Sharoon Thomas 3 | 4 | # Install dependencies for running web service 5 | RUN apt-get install -y python-pip 6 | RUN pip install werkzeug executor gunicorn 7 | 8 | ADD app.py /app.py 9 | EXPOSE 80 10 | 11 | ENTRYPOINT ["usr/local/bin/gunicorn"] 12 | 13 | # Show the extended help 14 | CMD ["-b", "0.0.0.0:80", "--log-file", "-", "app:application"] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Openlabs Technologies & Consulting (P) Limited 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-wkhtmltopdf-aas 2 | 3 | wkhtmltopdf in a docker container as a web service. 4 | 5 | This image is based on the 6 | [wkhtmltopdf container](https://registry.hub.docker.com/u/openlabs/docker-wkhtmltopdf/). 7 | 8 | ## Running the service 9 | 10 | Run the container with docker run and binding the ports to the host. 11 | The web service is exposed on port 80 in the container. 12 | 13 | ```sh 14 | docker run -d -P openlabs/docker-wkhtmltopdf-aas 15 | ``` 16 | 17 | The container now runs as a daemon. 18 | 19 | Find the port that the container is bound to: 20 | 21 | ```sh 22 | docker port 071599a1373e 80 23 | ``` 24 | 25 | where 071599a1373e is the container SHA that docker assigned when 26 | `docker run` was executed in the previous command. 27 | 28 | Take a note of the public port number where docker binds to. 29 | 30 | ## Using the webservice 31 | 32 | There are multiple ways to generate a PDF of HTML using the 33 | service. 34 | 35 | ### Uploading a HTML file 36 | 37 | This is a convenient way to use the service from command line 38 | utilities like curl. 39 | 40 | ```sh 41 | curl -X POST -vv -F 'file=@path/to/local/file.html' http://:/ -o path/to/output/file.pdf 42 | ``` 43 | 44 | where: 45 | 46 | * docker-host is the hostname or address of the docker host running the container 47 | * port is the public port to which the container is bound to. 48 | 49 | ### JSON API 50 | 51 | If you are planning on using this service in your application, 52 | it might be more convenient to use the JSON API that the service 53 | uses. 54 | 55 | Here is an example using python requests: 56 | 57 | ```python 58 | import json 59 | import requests 60 | 61 | url = 'http://:/' 62 | data = { 63 | 'contents': open('/file/to/convert.html').read().encode('base64'), 64 | } 65 | headers = { 66 | 'Content-Type': 'application/json', # This is important 67 | } 68 | response = requests.post(url, data=json.dumps(data), headers=headers) 69 | 70 | # Save the response contents to a file 71 | with open('/path/to/local/file.pdf', 'wb') as f: 72 | f.write(response.content) 73 | ``` 74 | 75 | Here is another example in python, but this time we pass options to wkhtmltopdf. 76 | When passing our settings we omit the double dash "--" at the start of the option. 77 | For documentation on what options are available, visit http://wkhtmltopdf.org/usage/wkhtmltopdf.txt 78 | 79 | ```python 80 | import json 81 | import requests 82 | 83 | url = 'http://:/' 84 | data = { 85 | 'contents': open('/file/to/convert.html').read().encode('base64'), 86 | 'options': { 87 | #Omitting the "--" at the start of the option 88 | 'margin-top': '6', 89 | 'margin-left': '6', 90 | 'margin-right': '6', 91 | 'margin-bottom': '6', 92 | 'page-width': '105mm', 93 | 'page-height': '40mm' 94 | } 95 | } 96 | headers = { 97 | 'Content-Type': 'application/json', # This is important 98 | } 99 | response = requests.post(url, data=json.dumps(data), headers=headers) 100 | 101 | # Save the response contents to a file 102 | with open('/path/to/local/file.pdf', 'wb') as f: 103 | f.write(response.content) 104 | ``` 105 | 106 | ## TODO 107 | 108 | * Implement conversion of URLs to PDF 109 | * Add documentation on passing options to the service 110 | * Add `curl` example for JSON api 111 | * Explain more gunicorn options 112 | 113 | ## Bugs and questions 114 | 115 | The development of the container takes place on 116 | [Github](https://github.com/openlabs/docker-wkhtmltopdf-aas). If you 117 | have a question or a bug report to file, you can report as a github issue. 118 | 119 | 120 | ## Authors and Contributors 121 | 122 | This image was built at [Openlabs](http://www.openlabs.co.in). 123 | 124 | ## Professional Support 125 | 126 | This image is professionally supported by [Openlabs](http://www.openlabs.co.in). 127 | If you are looking for on-site teaching or consulting support, contact our 128 | [sales](mailto:sales@openlabs.co.in) and [support](mailto:support@openlabs.co.in) teams. 129 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | WSGI APP to convert wkhtmltopdf As a webservice 4 | 5 | :copyright: (c) 2013 by Openlabs Technologies & Consulting (P) Limited 6 | :license: BSD, see LICENSE for more details. 7 | """ 8 | import json 9 | import tempfile 10 | 11 | from werkzeug.wsgi import wrap_file 12 | from werkzeug.wrappers import Request, Response 13 | from executor import execute 14 | 15 | 16 | @Request.application 17 | def application(request): 18 | """ 19 | To use this application, the user must send a POST request with 20 | base64 or form encoded encoded HTML content and the wkhtmltopdf Options in 21 | request data, with keys 'base64_html' and 'options'. 22 | The application will return a response with the PDF file. 23 | """ 24 | if request.method != 'POST': 25 | return 26 | 27 | request_is_json = request.content_type.endswith('json') 28 | 29 | with tempfile.NamedTemporaryFile(suffix='.html') as source_file: 30 | 31 | if request_is_json: 32 | # If a JSON payload is there, all data is in the payload 33 | payload = json.loads(request.data) 34 | source_file.write(payload['contents'].decode('base64')) 35 | options = payload.get('options', {}) 36 | elif request.files: 37 | # First check if any files were uploaded 38 | source_file.write(request.files['file'].read()) 39 | # Load any options that may have been provided in options 40 | options = json.loads(request.form.get('options', '{}')) 41 | 42 | source_file.flush() 43 | 44 | # Evaluate argument to run with subprocess 45 | args = ['wkhtmltopdf'] 46 | 47 | # Add Global Options 48 | if options: 49 | for option, value in options.items(): 50 | args.append('--%s' % option) 51 | if value: 52 | args.append('"%s"' % value) 53 | 54 | # Add source file name and output file name 55 | file_name = source_file.name 56 | args += [file_name, file_name + ".pdf"] 57 | 58 | # Execute the command using executor 59 | execute(' '.join(args)) 60 | 61 | return Response( 62 | wrap_file(request.environ, open(file_name + '.pdf')), 63 | mimetype='application/pdf', 64 | ) 65 | 66 | 67 | if __name__ == '__main__': 68 | from werkzeug.serving import run_simple 69 | run_simple( 70 | '127.0.0.1', 5000, application, use_debugger=True, use_reloader=True 71 | ) 72 | --------------------------------------------------------------------------------