-
-
Save grantmcconnaughey/ce90a689050c07c61c96 to your computer and use it in GitHub Desktop.
| # -*- coding: utf-8 -*- | |
| import codecs | |
| import subprocess | |
| from fdfgen import forge_fdf | |
| from django.core.exceptions import ImproperlyConfigured | |
| from django.template import engines | |
| from django.template.backends.base import BaseEngine | |
| from django.template.engine import Engine, _dirs_undefined | |
| class PdfTemplateError(Exception): | |
| pass | |
| class PdftkEngine(BaseEngine): | |
| # Going ahead and defining this, but really PDFs should still be placed | |
| # in the templates directory of an app because the loader checks templates | |
| app_dirname = 'pdfs' | |
| def __init__(self, params): | |
| params = params.copy() | |
| options = params.pop('OPTIONS').copy() | |
| super(PdftkEngine, self).__init__(params) | |
| self.engine = self._Engine(self.dirs, self.app_dirs, **options) | |
| def get_template(self, template_name, dirs=_dirs_undefined): | |
| return PdfTemplate(self.engine.get_template(template_name, dirs)) | |
| class _Engine(Engine): | |
| def make_origin(self, display_name, loader, name, dirs): | |
| # Always return an Origin object, because PDFTemplate need it to | |
| # render the PDF Form file. | |
| from django.template.loader import LoaderOrigin | |
| return LoaderOrigin(display_name, loader, name, dirs) | |
| class PdfTemplate(object): | |
| pdftk_bin = None | |
| def __init__(self, template): | |
| self.template = template | |
| self.set_pdftk_bin() | |
| @property | |
| def origin(self): | |
| return self.template.origin | |
| def render(self, context=None, request=None): | |
| if context is None: | |
| 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): | |
| fdf_stream = forge_fdf(fdf_data_strings=fields) | |
| cmd = [self.pdftk_bin, src, 'fill_form', '-', 'output', '-', 'flatten'] | |
| cmd = ' '.join(cmd) | |
| return self.run_cmd(cmd, fdf_stream) | |
| def dump_data_fields(self): | |
| cmd = [self.pdftk_bin, self.origin.name, 'dump_data_fields'] | |
| cmd = ' '.join(cmd) | |
| output, err = self.run_cmd(cmd, None) | |
| if err: | |
| raise PdfTemplateError(err) | |
| return output | |
| def run_cmd(self, cmd, input_data): | |
| try: | |
| process = subprocess.Popen(cmd, stdin=subprocess.PIPE, | |
| stdout=subprocess.PIPE, shell=True) | |
| if input_data: | |
| return process.communicate(input=input_data) | |
| else: | |
| return process.communicate() | |
| except OSError, e: | |
| return None, e | |
| def set_pdftk_bin(self): | |
| if self.pdftk_bin is None: | |
| from django.conf import settings | |
| if not hasattr(settings, 'PDFTK_BIN'): | |
| msg = "PDF generation requires pdftk " \ | |
| "(http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit). " \ | |
| "Edit your PDFTK_BIN settings accordingly." | |
| raise ImproperlyConfigured(msg) | |
| self.pdftk_bin = settings.PDFTK_BIN | |
| return self.pdftk_bin | |
| def version(self): | |
| cmd = [self.pdftk_bin, '--version'] | |
| cmd = ' '.join(cmd) | |
| output, err = self.run_cmd(cmd, None) | |
| if err: | |
| raise PdfTemplateError(err) | |
| return output | |
| 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) | |
| if template_name.endswith('.pdf'): | |
| template = engines['pdf'].get_template(template_name) | |
| else: | |
| template = engines['django'].get_template(template_name) | |
| # Loading hacks | |
| codecs.register_error('strict', strict_errors) | |
| return template |
| TEMPLATES = [ | |
| { | |
| 'BACKEND': 'django.template.backends.django.DjangoTemplates', | |
| 'APP_DIRS': True, | |
| }, | |
| { | |
| 'BACKEND': 'my_app.pdf.PdftkEngine', | |
| 'APP_DIRS': True, | |
| }, | |
| ] |
| # -*- 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 |
This would be great as a released app.
This is extremely useful grantmcconnaughey! I'm trying to implement this solution and I've run into two roadblocks.
-
I'm having trouble specifying the correct path for 'BACKEND': 'my_app.pdf.PdftkEngine'. Is the "my_app" path the series of folders leading to the pdf.py file?
-
How do you pass dynamic content into the "context" array of ticket_print? For example, if the user puts in his or her name in the front end and I want that to be passed in automatically.
@mandrews1989 - https://docs.djangoproject.com/en/2.0/ref/settings/#templates
You can use a template backend that doesn’t ship with Django by setting BACKEND to a fully-qualified path (i.e. 'mypackage.whatever.Backend').
This is not working anymore in the latest version of django
How would I go about filling checkboxes with this library? Otherwise this works great! Thank you for this.