Skip to content

Instantly share code, notes, and snippets.

@zyegfryed
Created April 13, 2011 21:00
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save zyegfryed/918403 to your computer and use it in GitHub Desktop.
Save zyegfryed/918403 to your computer and use it in GitHub Desktop.
Outputting PDFs with Django and FDFgen
# -*- coding: utf-8 -*-
import codecs
import subprocess
from fdfgen import forge_fdf
from django.template import Template, loader
from django.template.loader import find_template, LoaderOrigin
class PdfTemplateError(Exception):
pass
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 = self.fill_form(context, self.origin.name)
if err:
raise PdfTemplateError(err)
return output
def fill_form(self, fields, src, pdftk_bin=None):
if pdftk_bin is None:
from django.conf import settings
assert hasattr(settings, 'PDFTK_BIN'), "PDF generation requires pdftk (http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/). Edit your PDFTK_BIN settings accordingly."
pdftk_bin = settings.PDFTK_BIN
fdf_stream = forge_fdf(fdf_data_strings=fields)
cmd = [
pdftk_bin,
src,
'fill_form',
'-',
'output',
'-',
'flatten',
]
cmd = ' '.join(cmd)
try:
process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, shell=True)
return process.communicate(input=fdf_stream)
except OSError:
return None
def get_template_from_string(source, origin=None, name=None):
"""
Returns a compiled Template object for the given template code,
handling template inheritance recursively.
"""
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
def make_origin(display_name, loader, name, dirs):
# Always return an Origin object, because PDFTemplate need it to render
# the PDF Form file.
return LoaderOrigin(display_name, loader, name, dirs)
loader.make_origin = make_origin
def get_template(template_name):
"""
Returns a compiled Template object for the given template name,
handling template inheritance recursively.
"""
def strict_errors(exception):
raise exception
def fake_strict_errors(exception):
return (u'', -1)
# Loading hacks
# Ignore UnicodeError, due to PDF file read
codecs.register_error('strict', fake_strict_errors)
# --//--
template, origin = find_template(template_name)
# Loading hacks
codecs.register_error('strict', strict_errors)
# --//--
return template
# -*- coding: utf-8 -*-
from django.http import HttpResponse
from django.utils.translation import ugettext as _
from pdf import get_template
def ticket_print(request, template_name='pdf_form/pdf/foobar.pdf', **kwargs):
context = {
'foo': 'bar',
'bar': _('baz'),
}
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = \
'attachment; filename=foobar.pdf'
template = get_template(template_name)
response.write(template.render(context))
return response
@grantmcconnaughey
Copy link

I have updated this to work with Django 1.8's multiple template engines. Find it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment