Sonntag, 26. Juli 2015

Python: Tabellen in ReportLab

ReportLab ist die wohl leistungsfähigste Bibliothek für Python, wenn es um das Erzeugen von PDF-Dateien geht. Von High-Level Zugriffen via Platypus (=Page Layout and Typography Using Scripts) bis zu Low-Level mit direktem Zugriff auf den Canvas bietet ReportLab alles.

Tabellen unterstützt Platypus mit Hilfe der Table-Klasse (welche ein Flowable darstellt) direkt und die Verwendung ist auch erst Mal nicht weiter schwierig.

Es soll ein einfaches PDF-Dokument mit Hilfe von ReportLab gebaut werden. Das Dokument besteht aus einem Absatz Text, einer Tabelle und einem Absatz Text nach der Tabelle.

Der Code dafür sieht im einfachsten Fall so aus:

from reportlab.platypus import Paragraph, Table, SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4


my_data = [['foo', 'bar', 'spam'],
           ['egg', 123, 'python'],
           ['a bit longer', 666, '9B']]
          
styles = getSampleStyleSheet()
doc = SimpleDocTemplate('table_test.pdf', pagesize=A4)
story = []
story.append(Paragraph('Text vor Tabelle', styles['Normal']))
story.append(Table(my_data))
story.append(Paragraph('Text nach Tabelle', styles['Normal']))
doc.build(story)


my_data enthält die Daten für die Tabelle, der Rest ist weitestgehend selbsterklärend.

Nur das das Dokument nicht gerade "hübsch". Das Ergebnis ist im folgenden Screenshot zu sehen:

Standardmäßig werden für die Tabelle keine Rahmen gezeichnet und die Tabelle ist horizontal auf der Seite zentriert. Außerdem ist der Text sehr nah an der Tabelle.

Das ist im folgenden Code korrigiert:

from reportlab.platypus import Paragraph, Table, TableStyle, SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors

my_data = [['foo', 'bar', 'spam'],
           ['egg', 123, 'python'],
           ['a bit longer', 666, '9B']]
          
styles = getSampleStyleSheet()
doc = SimpleDocTemplate('table_test.pdf', pagesize=A4)
story = []
story.append(Paragraph('Text vor Tabelle', styles['Normal']))
t = Table(my_data)
t.hAlign = 'LEFT'
t.spaceBefore =  10
t.spaceAfter = 10
t.setStyle(TableStyle(

    [('BOX', (0,0), (-1,-1), 0.5, colors.black),
     ('INNERGRID', (0,0), (-1,-1), 0.5, colors.black)]))
story.append(t)
story.append(Paragraph('Text nach Tabelle', styles['Normal']))
doc.build(story)


Das ganze liefert als Ergebnis folgendes:
Über das Attribute hAlign wird die Tabelle die Ausrichtung der Tabelle bestimmt, der Wert LEFT richtet sie linksbündig aus. Mit den Attributen spaceBefore und SpaceAfter wird zusätzlicher Leerraum (hier: 10 px) eingefügt.

Die Tabelle an sich wird über die setStyle-Methode formatiert, welche als Wert die TableStyle-Klasse übergeben bekommt. Diese erlaubt einen detaillierten Eingriff in die Formatierung der Tabelle.

Die Werte für TableStyle werden als Liste von Tupeln übergeben. Im obigen Beispiel wird mit BOX ein Rahmen um die Tabelle gezeichnet und INNERGRID zeichnet den inneren Rahmen, beide in schwarz.
Die Tupel (0, 0), (-1, -1) definieren den Geltungsbereich der Formatierung. (0,0) ist dabei die Zelle links oben, (-1, -1) die Zelle rechts unten - somit ist also die ganze Tabelle abgedeckt.

Möchte man z.B. das innere Gitter mur für einen Teil der Zellen zeichnen und in der mittleren Zelle soll die Schrift rot sein, so ist die Codezeile wie folgt anzupassen:

t.setStyle(TableStyle(
    [('BOX', (0,0), (-1,-1), 0.5, colors.black
     ('INNERGRID', (0,0), (1,1), 0.5, colors.black),
     ('TEXTCOLOR', (1,1), (1,1), colors.red)]))

Und das Ergebnis sieht dann so aus:


TableStyle bietet eine Vielzahl weiterer Möglichkeiten, dass Aussehen der Tabelle zu beeinflussen. Eine komplette Übersicht ist im Kapitel 7 der ReportLab Dokumentation zu finden.

So ist es z.B. auch möglich, Zellen innerhalb einer Spalte oder Zeile zusammenzufassen. Möchte man z.B. die drei Zellen der ersten Zeile zusammenfassen, so ist der obiger Code wie folgt zu ändern:

...
my_data = [['Überschrift', '', ''],
           ['egg', 123, 'python'],
           ['a bit longer', 666, '9B']]

....
t.setStyle(TableStyle(
    [('BOX', (0,0), (-1,-1), 0.5, colors.black),
     ('INNERGRID', (0,0), (-1,-1), 0.5, colors.black),
     ('SPAN', (0, 0), (-1, 0))]))


Das sieht im Ergebnis so aus:

Die bisher generierten Tabellen ermitteln die Breite der Spalten und Höhe der Zeilen automatisch, aber auch das kann natürlich beeinflusst werden. Möchte man z.B. eine fixe Spaltenbreite  von 100 px haben, so ist im Code die Zeile wie folgt zu ändern:

t = Table(my_data, colWidths=100)

Weitere Möglichkeit sind ebenfalls im Kapitel 7 der Dokumentation zu finden.

In den obigen Beispielen wird der Table-Klasse einfach eine Liste von Strings bzw. Integern übergeben. Man kann aber auch eine Liste von Flowables übergeben, was sich anbieten, wenn man z.B. längeren Text oder Grafiken in der Tabelle unterbringen möchte.

Des Weiteren gibt es noch die Klasse LongTable, welche anstellen von Table verwendet werden kann. Das Ergebnis ist laut Dokumentation das gleiche, allerdings soll LongTable bei sehr langen Tabelle etwas performanter sein.

Wie hier gezeigt wird, ist das Generieren einer Tabelle in einem mit Platypus gebauten PDF-Dokuments in ReportLab nicht weiter schwierig. Wie bei ReportLab üblich kann man auf Wunsch aber auch sehr detailliert in die Gestaltung und das Layout der Tabelle eingreifen.

Alle Beispiele aus diesem Blogbeitrag sind unter Python 3.4 und ReportLab 3.0 erstellt.

Keine Kommentare:

Kommentar veröffentlichen