You are on page 1of 44

Generating PDF with PDFForm

Sébastien Fievet

Djangocong Marseille
April 16, 2011

dimanche 17 avril 2011


Case study:
outputting “simple”
PDFs

dimanche 17 avril 2011


Simple things should
remain simple

dimanche 17 avril 2011


Focus on skills

dimanche 17 avril 2011


Designer ==
templating

dimanche 17 avril 2011


Developper ==
rendering

dimanche 17 avril 2011


The solution ?

dimanche 17 avril 2011


dimanche 17 avril 2011
dimanche 17 avril 2011
1
dimanche 17 avril 2011
pip install fdfgen

dimanche 17 avril 2011


from fdfgen import forge_fdf

def fill_form(fields, src, pdftk_bin):


...
fdf_stream = forge_fdf(fdf_data_strings=fields)
...

dimanche 17 avril 2011


Issue

dimanche 17 avril 2011


Issue

Breaks on accentuated character

dimanche 17 avril 2011


“ Fork it, fix it, contribute it.

dimanche 17 avril 2011



-- a DVCS convinced guy
2
dimanche 17 avril 2011
aptitude install pdftk

dimanche 17 avril 2011


pdftk <PDF input>
dump_data_fields

dimanche 17 avril 2011


pdftk <PDF input>
fill_form <FDF file>
output <PDF output> flatten

dimanche 17 avril 2011


import subprocess
...

def fill_form(fields, src, pdftk_bin):


...
cmd = ' '.join([pdftk_bin, src, 'fill_form', '-',
'output', '-', 'flatten'])
try:
process = subprocess.Popen(cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE, shell=True)
return process.communicate(input=fdf_stream)
except OSError, e:
return None, e

dimanche 17 avril 2011


But...

dimanche 17 avril 2011


But...

utf8 support broken on Ubuntu 10.10


(binary package, v1.41)

dimanche 17 avril 2011


But...

utf8 support broken on Ubuntu 10.10


(binary package, v1.41)
Use the source luke (v1.44)

dimanche 17 avril 2011


But...

utf8 support broken on Ubuntu 10.10


(binary package, v1.41)
Use the source luke (v1.44)
wget && make && make install

dimanche 17 avril 2011


3
dimanche 17 avril 2011
Django template engine
== The challenge

dimanche 17 avril 2011


Template loading

dimanche 17 avril 2011


Template loading

PDFs are binary file

dimanche 17 avril 2011


Template loading

PDFs are binary file


We don't care about the file content

dimanche 17 avril 2011


Template loading

PDFs are binary file


We don't care about the file content
But we ALWAYS need the template
path

dimanche 17 avril 2011


import codecs
from django.template.loader import find_template

def get_template(template_name):

def strict_errors(exception):
raise exception

def fake_strict_errors(exception):
return (u'', -1)

codecs.register_error('strict', fake_strict_errors)
template, origin = find_template(template_name)
codecs.register_error('strict', strict_errors)

return template

dimanche 17 avril 2011


from django.template import loader, Template
...

def get_template_from_string(source, origin=None,


name=None):
if name and name.endswith('.pdf'):
return PdfTemplate('pdf', origin, name)
return Template(source, origin, name)
loader.get_template_from_string = get_template_from_string

dimanche 17 avril 2011


Template origin

* Paranoiac mode

dimanche 17 avril 2011


Template origin

TEMPLATE_DEBUG = True

* Paranoiac mode

dimanche 17 avril 2011


Template origin

TEMPLATE_DEBUG = True
Or monkey-patch make_origin *

* Paranoiac mode

dimanche 17 avril 2011


Template rendering

dimanche 17 avril 2011


Template rendering

Custom rendering method


Leveraging pdftk and FDFGen
Use a dedicated Template class

dimanche 17 avril 2011


from django.template import Template
...

class PdfTemplate(Template):

def __init__(self, template_string, origin=None,


name='<Unknown Template>'):
self.origin = origin

def render(self, context):


context = context.items()
output, err = fill_form(context, self.origin.name)
if err:
raise PdfTemplateError(err)
return output

dimanche 17 avril 2011


https://gist.github.com/918403

dimanche 17 avril 2011


4
dimanche 17 avril 2011
Usage

dimanche 17 avril 2011


from django.http import HttpResponse
from pdf import get_template

def pdf_view(request, template_name='pdf/awesome.pdf'):


context = {
'foo': 'bar',
'bar': 'baz',
'awesome': True,
'user': request.user,
}

response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment;
filename=awesome.pdf'

template = get_template(template_name)
response.write(template.render(context))

return response

dimanche 17 avril 2011


Demo

dimanche 17 avril 2011


Questions?

dimanche 17 avril 2011

You might also like