sábado, 13 de marzo de 2010

Crear documentos PDF en Python, y 2.

Hola a todos. Vamos a seguir en este post con la temática de crear documentos PDF con Python, que empecé hace unas semanas. Os recomiendo que si no habéis leído el primer post sobre ReportLab, lo hagáis antes, para no perderos. En este link.

Anteriormente vimos como insertar imágenes. Ahora toca como insertar texto. Hay varias maneras, pero como no quiero alargar en exceso los artículos, solo veremos una de las formas. Más adelante (en otros post) veremos otras y como juntar todo en una aplicación pequeña con wxPython. Incluso se podría ver con GTK, o TKinter. Estoy abierto a sugerencias, así que si os animáis a participar estaría encantado.

Hay varias formas de escribir texto en PDF con ReportLab. Como hay que ser ordenados, vamos a empezar por la primera. Recordar que es mejor que el lector lea previamente el post número 1 de ReportLab, ya que doy por sentado ciertos conocimientos que ahí vienen descritos.

Utilizando el objeto Text

La interfaz del objeto Text (sus métodos) da un control detallado de la escritura de texto. Un objeto Text mantiene un cursor de texto que se mueve a lo largo de la página cuando el texto comienza a dibujarse.

ReportLab, en principio, no se lleva bien con el español. Esto es, volvemos al problema de siempre con las codificaciones. Para solventar el asunto y que podamos escribir tildes y nuestra "ñ", en latin-1 ó iso-8859-1, hay que utilizar el comando unicode, tal como aparece en el ejemplo. Si no se utiliza, te dará un error de CodeDecodeError: ' utf8' codec can´t decode byes in position... ¿No me crees? Haz la prueba.

En el siguiente ejemplo vamos a utilizar los siguientes métodos:

beginText(): Para instanciar el objeto Text.
setTextOrigin
(x,y): Mueve el cursor a una posición dada por las coordenadas (x,y).
setFont(tipoLetra,tamañoLetra, Leading = None): Cambia el tipo de letra.
textLine(linea_texto): Inserta una de texto en el objeto y baja el cursor a la línea siguiente.
textLines(lineas_texto): Inserta líneas de texto en el objeto y baja el cursor después de la última línea.
setFillGray(g): Ajusta el grado de gris al color del texto (0 <= g <= 1). drawText(): Dibuja el texto del objeto.

Así que tenemos:

from reportlab.pdfgen import canvas

oracion = ['Y en tu ausencia las paredes','se pintarán de tristeza',\
'y enjaularé mi corazón entre tus huesos.']


aux = canvas.Canvas("prueba.pdf")

textobject = aux.beginText()
textobject.setTextOrigin(100, 500)
textobject.setFont("Courier", 14)
for line in oracion:
uniLine = unicode(line, 'latin-1')
textobject.textLine(uniLine)
textobject.setFillGray(0.5)
lineas_texto = '''
Hola a todos. En este post vamos a ver
como escribir texto con Python y ReportLab.
Más información en El Viaje del Navegante.
'''
lineas_texto = unicode(lineas_texto,'latin-1')
textobject.textLines(lineas_texto)
aux.drawText(textobject)

# Salvamos.
aux.showPage()
aux.save()

Darse cuenta del movimiento automático del cursor a la hora de ubicar el texto una vez que el origen ha sido dado. El resultado sería el siguiente:

Como vemos en este ejemplo, instanciamos un objeto, y mediante sus métodos vamos contruyendo el texto que queremos, para luego pasarlo a un fichero PDF. La cuestión aquí es que utilizamos un objeto para realizar todas escrituras, y nos olvidamos de la posición del cursor, pues son los propios métodos vistos los que se encargan de todo.

Tipos de letra. En este caso he ido directo al código de ReportLab, e indagando me he encontrado con un archivo que contiene los tipos de letra. Este fichero se puede encontrar en: Lib\site-packages\reportlab\pdfbase\_fontdata.py.
Esto es lo que he encontrado:

standardFonts = ( 'Courier', 'Courier-Bold', 'Courier-Oblique', 'Courier-BoldOblique', 'Helvetica', 'Helvetica-Bold', 'Helvetica-Oblique', 'Helvetica-BoldOblique', 'Times-Roman', 'Times-Bold', 'Times-Italic', 'Times-BoldItalic', 'Symbol','ZapfDingbats')

Estos 14 tipos de letra estándar siempre están disponibles, y no necesitan guardarse en el PDF, ya que se nos garantiza que están presentes en Acrobat Reader. Investigar está bien, pero ver los tipos de letra con los métodos apropiados está mejor. Podemos ver esta misma información mediante el siguiente ejemplo:


from reportlab.pdfgen import canvas

cadena_texto = "Tipo de letra con ReportLab en El Viaje del Navegante"
aux = canvas.Canvas("tipos-letra.pdf")

textobject = aux.beginText()
textobject.setTextOrigin(20, 500)
for tipo_letra in aux.getAvailableFonts():
textobject.setFont(tipo_letra, 12)
texto = unicode(cadena_texto, 'latin-1')
textobject.textLine(tipo_letra + ": " + texto)
aux.drawText(textobject)

# Salvamos.
aux.showPage()
aux.save()
Y como resultado obtenemos lo siguiente:

En el siguiente ejemplo utilizamos el método moveCursor(dx, dy). De esta forma vamos a poder controlar el movimiento del cursor de forma más explícita. Dicho método mueve el cursor como un desplazamiento (offset) desde el principio de la actual linea, no el cursor actual. Los movimientos de desplazamientos positivos (y) son hacia abajo (en contraposición con la geometría normal donde el movimiento positivo de y mueve hacia arriba). Darse cuenta que el método textOut no mueve hacia abajo la línea, en contraposicion con el método textLine, que sí lo hace.

from reportlab.pdfgen import canvas

oracion = ['Y en tu ausencia las paredes','se pintarán de tristeza',\
'y enjaularé mi corazón entre tus huesos.']


aux = canvas.Canvas("prueba2.pdf")

textobject = aux.beginText()
textobject.setTextOrigin(100, 500)
textobject.setFont("Courier", 16)
for line in oracion:
uniLine = unicode(line, 'latin-1')
textobject.textOut(uniLine)
# "Y" es positivo y mueve el cursor hacia abajo.
textobject.moveCursor(20,15)
textobject.setFillColorRGB(0.2,0,0.6)
aux.drawText(textobject)

# Salvamos.
aux.showPage()
aux.save()


Y el resultado es el siguiente:

Hay dos métodos especiales para separar tanto palabras del texto con espacios como para separar las letras de las palabras que conforman el texto. Ellos son setWordSpace(wordSpace) y setCharSpace(charSpace) respectivamente. Veámoslo con el siguiente ejemplo:

from reportlab.pdfgen import canvas

oracion = ['Y en tu ausencia las paredes','se pintarán de tristeza',\
'y enjaularé mi corazón entre tus huesos.']


aux = canvas.Canvas("prueba3.pdf")

textobject = aux.beginText()
textobject.setTextOrigin(25, 500)
textobject.setFont("Courier", 14)
charspace = 0
for line in oracion:
textobject.setCharSpace(charspace)
uniLine = unicode(line, 'latin-1')
textobject.textLine("Espacio %s : %s " % (charspace,uniLine))
charspace = charspace + 1
lineas_texto = '''
Ejemplo de espaciado de palabras:
Hola a todos. En este post vamos a ver
como escribir texto con Python y ReportLab.
Más información en El Viaje del Navegante.
'''
textobject.setFillGray(0.5)
textobject.setWordSpace(8)
lineas_texto = unicode(lineas_texto,'latin-1')
textobject.textLines(lineas_texto)
aux.drawText(textobject)
# Salvamos.
aux.showPage()
aux.save()

Con el consecuente resultado:


Darse cuenta que las primeras 3 líneas lo que se está haciendo es separar las letras. En el segundo bloque, lo que se hace es separar las palabras. Como no quiero hacer este post demasiado largo, por el momento es todo. En siguientes artículos seguiré presentando varias características de escritura en ReportLab, así como otras formas de hacerlo.

1 comentario:

  1. una consulta, estoy trabajando con tkinter en spyder y necesito imprimir un informe en reportlab, lo que no entiendo y e buscado informacion, es como hacer que un entry(casilla de texto), un listbox o cualquier tipo de objeto se inserte en el informe o la informacion que contenga la muestre ahi...si es que me pueden ayudar....estoy trabajando en ubuntu aunque no creo q el SO influya mi correo es hec2.5@gmail.com por si acaso..

    ResponderEliminar