Django es el entorno de desarrollo web para perfeccionistas con límites de tiempo

Generar archivos PDF con Django y Pisa

Pisa es un conversor de HTML/XHTML a PDF escrito en Python. Vamos a ver cómo utilizar Pisa en nuestras vistas para convertir nuestras plantillas HTML a PDF.

Lo primero que tenemos que hacer es descargar e instalar Pisa. Pisa requiere ReportLab Toolkit, HTML5lib, pyPdf y PIL (útil: Cómo instalar PIL en Mac OSX). La forma más sencilla de instalar Pisa es mediante easy_install:

easy_install pisa

También podemos bajar el código fuente e instalarlo mediante el siguiente comando (en Linux y Mac Osx puedes necesitar incluir sudo delante):

python setup.py install

Una vez instalado Pisa añadiremos a views.py una función que nos permita convertir nuestras plantillas HTML a PDF. Utilizaremos una vista y una plantilla HTML que la función se encargará de convertir a formato PDF y devolverlo en la petición HTTP. La función recibirá la plantilla HTML renderizada previamente en nuestra vista mediante render_to_string.

Vamos a ver cómo quedaría nuestro views.py para una vista que reciba el id de un hipotético modelo Libro y devuelva un PDF con la información del mismo:

# -*- coding: utf-8 -*-

import ho.pisa as pisa
import cStringIO as StringIO
import cgi
from django.template import RequestContext
from django.template.loader import render_to_string
from django.http import HttpResponse

def generar_pdf(html):
    # Función para generar el archivo PDF y devolverlo mediante HttpResponse
    result = StringIO.StringIO()
    pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")), result)
    if not pdf.err:
        return HttpResponse(result.getvalue(), mimetype='application/pdf')
    return HttpResponse('Error al generar el PDF: %s' % cgi.escape(html))

def libro_pdf(request, id):
    # vista de ejemplo con un hipotético modelo Libro
    libro=get_object_or_404(Libro, id=id)
    html = render_to_string('libro_pdf.html', {'pagesize':'A4', 'libro':libro}, context_instance=RequestContext(request))
    return generar_pdf(html)

Ahora sólo nos falta crear la plantilla libro_pdf.html para mostrar la información del libro:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>    
<body>
    <h1>{{ libro.titulo }}</h1>
    <p>{{ libro.descripcion }}</p>
</body>
</html>

Incluir imágenes en los PDFs generados con Pisa

Si queremos incluir imágenes en nuestro PDF deberemos utilizar su ruta local en la plantilla en vez de su URL. Imaginando que nuestro modelo Libro tuviera un campo imagen de tipo ImageField podríamos hacerlo de la siguiente forma:

<img src="{{ libro.imagen.path }}" />

Las imágenes suelen quedar más grandes de lo normal debido al mapeo de píxeles a puntos que hace Pisa. Para que las imágenes salgan con un tamaño adecuado conviene incluirles el estilo CSS zoom:

<img src="{{ libro.imagen.path }}" style="zoom: 80%;" />

Etiquetas HTML especiales de Pisa

Hay varias etiquetas especiales que aporta Pisa y que podemos utilizar en nuestras plantillas. Las más útiles son: pdf:pagenumber y pdf:nextpage. La primera nos permite incluir número de página en cada una de las páginas del documento PDF y la segunda nos permite definir dónde comienza otra página. Su uso en las plantillas HTML es muy sencillo:

<pdf:pagenumber>
<pdf:nextpage>

Como véis, las posibilidades son múltiples a la hora de generar PDFs dinámicamente con Python. Aquí tenéis un ejemplo de PDF que se genera dinámicamente mediante este método: Receta de dátiles con bacon

Publicado por Antonio Melé el Miércoles 4 de Agosto de 2010 Compártelo: Facebook: Twitter: | Categorías: plantillas, snippets, tutorial

Entradas similares

Templatetag {% if %} con más comparaciones

Este snippet reemplaza la funcionalidad del templatetag {% if %} permitiendo realizar comparaciones con operadores >, <, >=, <=, != además de las comparaciones que permite hacer {% if %} por defecto. Por ...


Crear gráficas estadísticas con Django y Google Visualization API

Vamos a ver cómo combinar django-qsstats para generar estadísticas y Google Visualization API para representarlas gráficamente.

django-qsstats permite obtener estadísticas agregadas sobre querysets de ...


 
Idiomas en nuestras URLs gracias a django-localeurl

django-localeurl es una aplicación que permite el uso de códigos de idiomas en las URLs de nuestro proyecto. Esto trae la ventaja de que ...


Subdominios con Django

En ocasiones nos interesa trabajar con subdominios en nuestros proyectos Django. Para ello podemos utilizar un sencillo middleware para subdominios que podemos encontrar en ...


 
 

4 comentarios:

El Miércoles 4 de Agosto de 2010 Fran Lucena dijo:
Gracias Antonio !! Me va a ser de gran ayuda, la semana pasada estuve peleándome con Reportlab "a pelo" para maquetar un pdf. Un Saludo desde Granada !
El Viernes 27 de Agosto de 2010 Esau Rodriguez dijo:
Yo probé hace bastante pisa, pero tuvimos bastantes problemas. El soporte de css era bastante limitado y el rendimiento bastante malo. Tras estar utilizando pisa por un tiempo migramos a OpenOffice con pyuno. Este es un poco mejor, el rendimiento es flojito, pero puedes maquetar fácilmente con OpenOffice. Finalmente utilicé http://code.google.com/p/wkhtmltopdf/. Esto no es una librería de python, pero es fácil utilizarlo. Ese software utiliza khtml (webwit) para renderizar la página y luego la convierte a PDF. Además soporta cabeceras, y pies de página y hace los saltos de página adecuadamente. El rendimiento es muy muy bueno. Si los documentos que quieres generar son grandes esta es la opción.
El Jueves 11 de Noviembre de 2010 Orlando dijo:
Añado una guía por si se quiere sacar el pdf en chino mandarin: http://edisonlz.javaeye.com/blog/635745 está en chino pero el translator funciona bien :) y aquí el link para descargar de las fuentes (.ttf) http://www.vdisk.cn/down/index/3657112A4247 Salud!
El Miércoles 11 de Enero de 2012 Julian Ernesto dijo:
muy bueno, pero tengo una pregunta, cuales tamaños acepta 'pagesize'? se puede dar el tamaño que uno quiera?

Escribe un comentario: