PhantomJS is a scripted, headless browser (a browser without Graphical User Interface) used for automating web page interactions. It provides a JavaScript API enabling automated navigation, screenshots, user behavior, and assertions making it a common tool used to run browser-based unit tests in a headless system like a continuous integration environment. Since PhantomJS is using WebKit, a real layout and rendering engine, it can capture a web page as a screenshot. PhantomJS can render anything on the web page, it can be used to convert contents not only in HTML and CSS, but also SVG and Canvas, so any stuff that can be showed in the web page can be rendered as pdf with the help of PhantomJS.
Install PhantomJS
On OSX use brew to install phantomjs brew install phantomjs
Now let’s take a look at how to use phantomjs inside django view to render html page as pdf. We will start a simple django project namely ‘proj’ and add a reports app in it.
django-admin startproject proj
python manage.py startapp reports
Add reports under INSTALLED_APPS un settings.py
Now copy convertpdf.js into your django project this is the file which will be used by Phantomjs, take screenshots of the html rendered in browser.
#convertpdf.js var page = require('webpage').create(), system = require('system'), address, output, size; page.paperSize = { width: '8in', height: '9.5in', orientation: "portrait" }; if (system.args.length < 3 || system.args.length > 5) { console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]'); console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); phantom.exit(1); } else { address = system.args[1]; output = system.args[2]; page.viewportSize = { width: 1024, height: 770 }; if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { size = system.args[3].split('*'); page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; } if (system.args.length > 4) { page.zoomFactor = system.args[4]; } page.open(address, function (status) { if (status !== 'success') { console.log('Unable to load the address!'); phantom.exit(); } else { window.setTimeout(function () { page.render(output); phantom.exit(); }, 500); } }); }
Create templates directory under reports and add two html file namely
#firstpage.html <html> <head> </head> <body> <p> click the link to download table myreport</p> <br> <a href='/myreport'>get my report</a> </body> </html--> #reportpage.html <!--html> <head> <style> .lorem{ width: 400px;margin:auto; font-size: 15px; } </style> </head> <body> <h3> My Report</h3> <div class="lorem" > <p style="text-align: justify;">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p> </div> <div class="lorem" "> <p style="text-align: justify;">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p> </div> </body> </html-->
The overall project structure should look like this,
proj
├── proj │ ├── __init__.py │ ├── settings.py │ ├── urls.py ├ └── convertpdf.js ├── manage.py ├──reports ├── migrations └── templates ├──firstpage.html ├── reportpage.html ├── __init__.py ├── tests.py ├── views.py └── models.py in views.py file add three views from subprocess import Popen from subprocess import PIPE from subprocess import STDOUT from django.views.generic import TemplateView, View from django.http import StreamingHttpResponse from django.core.servers.basehttp import FileWrapper from proj.settings import BASE_DIR
class MyFirstView(TemplateView): template_name = "firstpage.html" def get(self, request): return self.render_to_response({}) class MyReportView(TemplateView): template_name = 'reportpage.html' def get(self, request): return self.render_to_response({}) class MyPdfView(View): def get(self, request, *args, **kwargs): file_name = BASE_DIR + 'testreport.pdf' filefrom = BASE_DIR + '/convertpdf.js' url = 'http://' + request.get_host() + '/pdfreport' external_process = Popen(["/usr/local/bin/phantomjs", filefrom, url, file_name], stdout=PIPE, stderr=STDOUT) external_process.wait() return_file = FileWrapper(open(file_name, 'r')) download_file_name = 'myreport' response = StreamingHttpResponse(return_file, content_type='application/force-download') response['Content-Disposition'] = 'attachment; filename= %s.pdf' % download_file_name return response
Now inside your urls.py file add this line,
from reports import views
And inside urlpatterns list add
url(r'^$', views.MyFirstView.as_view()), url(r'^pdfreport$', views.MyReportView.as_view()), url(r'^myreport$', views.MyPdfView.as_view()),
And we are done now. Run the project.
python manage.py migrate python manage.py runserver
Go to your browser enter localhost:8000 and click to get ‘my report’ and your report will get downloaded. You can see full project here https://github.com/yaseendar/phantomjs-django.