Samstag, 18. Oktober 2014

Python: ListFlowables 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.

Allerdings hat es doch bis zu ReportLab Version 2.6 (erschienen im Oktober 2012) gedauert, bis das direkte Erzeugen von nummerierten und unnummerierten Listen möglich war. Dies war zwar vorher prinzipiell auch möglich, aber nur über Umwege (z.B. bei unnummerierten Listen durch das manuelle hinzufügen eines Bullet Points vor dem Text).

Mit dem seit ReportLab 2.6 vorhandenen Flowable namens "ListFlowable" geht das aber wesentlich einfacher.

Neulich, bei der Überarbeitung eines meiner älteren Python-Skripte, welches auch ReportLab einsetzt, habe ich auch auf diese Flowable umgestellt.

Da die Doku von ReportLab zu diesem Thema - leider - etwas dünn ist, habe ich hier in diesem Blogebeitrag mal ein paar Sachen dazu aufgeschrieben.

Grundsätzlich ist die Sache recht simpel. Nach dem Import der benötigten Module erstellt man ein ListFlowable, welches die Punkte der Liste als ListItems enthält. Der Quelltext für eine Liste mit drei Punkten sieht so aus:

from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.platypus import Paragraph, SimpleDocTemplate, ListFlowable, \
                               ListItem

styles = getSampleStyleSheet()
style = styles['Normal']
story = []

t1 = ListFlowable([ListItem(Paragraph('foo', style)),
                   ListItem(Paragraph('bar', style)),
                   ListItem(Paragraph('spamegg', style))])
story.append(t1)
doc = SimpleDocTemplate('testdoc.pdf', size=A4)
doc.build(story)

So wird eine nummerierte Liste erzeugt, der Standard des ListFlowables, wenn keine weiteren Vorgaben gemacht werden.

Möchte man eine unnummerierte Liste erzeugen, muss dem ListFlowable das Argument bulletType und start mitgegeben werden:

t1 = ListFlowable([ListItem(Paragraph('foo', style)),
                   ListItem(Paragraph('bar', style)),
                   ListItem(Paragraph('spamegg', style))],
                   bulletType='bullet',
                   start='circle')

Möchte man statt Kreisen als Bullet Points z.B. Quadrate haben, dann ersetzt man einfach start='circle' durch start='square'.

Wer aus den obigen Beispielen ein PDF generiert wird feststellen, dass sowohl die Zahl als auch die Bullet Points ziemlich groß sind, größer als der Text, formatiert mit dem Beispiel-Stylesheet aus ReportLab. Fügt man noch das Argument bulletFontSize hinzu, gefolgt von der gewünschten Schriftgröße (also z.B. bulletFontSize=8), dann werden Schrift bzw. Bullet Points in der angegebenen Schriftgröße dargestellt.
Alternativ kann man für unnummerierten Listen auch folgendes angeben: start='bulletchar'. Dann wird als Zeichen standardmäßig ein kleiner Punkt (HTML: ·) verwendet.

Bei nummerierten Listen kann aber ReportLab aber nicht nur mit "normalen" Zahlen zählen, sondern auch:
bulletType='i' # römische Zahlen
bulletType='a' # Zählung: a, b , c, ...
bulletType='A' # Zählung: A, B, C, ...

Mit dem Argument start kann  man den Beginn der Zählung bei nummerierten Listen den Beginn der Zahlung festlegen. bulletType='A', start='10' würde z..B. die Zählung der Liste bei J starten lassen.

Weiterhin werden standardmäßig alle Listen linksbündig ausgerichtet. Möchte man die Listen gegenüber dem Text etwas einrücken, dann muss man die für die Aufzählungszeichen und den zugehörigen Text separat machen, wie z.B.:

p1 = Paragraph('etwas Text', style)
t1 = ListFlowable([ListItem(Paragraph('foo', style), leftIndent=25),
                   ListItem(Paragraph('bar', style), leftIndent=25),
                   ListItem(Paragraph('spamegg', style),leftIndent=25)],
                   bulletType='bullet',
                   start='bulletchar',
                   leftIdent=10)
story.append(p1)
story.append(t1)
story.append(p1)

Und das ListFlowable kann natürlich auch verschachtelt werden, für verschachtelte Liste. Eine Liste mit zwei Ebenen sieht z.B. so aus:

t1 = ListFlowable([ListItem(Paragraph('foo', style)),
                   ListItem(Paragraph('bar', style)),
                   ListFlowable([ListItem(Paragraph('spam', style)),
                                 ListItem(Paragraph('egg', style))],
                                 bulletType='a'),
                   ListItem(Paragraph('more', style))])

Ein paar weiter Tipps zum ListFlowable und dessen Formatierungsmöglichkeiten gibt es auch in einem Thread bei stackoverflow.

Keine Kommentare:

Kommentar veröffentlichen