Hallöle zusammen,
ich schlage mich schon seit einiger Zeit mit ReportLab rum, leider erfolglos.
Die Dokumentation von ReportLab online finde ich persönlich sehr einfach gehalten und bietet mir keine Hilfe, wenn es um komplexere PDFs geht.
Ich versuche seit 2 Wochen eine gescheite, mehrseitige Rechnung zu generieren (Erstmal mit Dummy Variablen und später mit User Input)...
Mein Ziel ist folgendes: Ich möchte eine Rechnung erstellen, die sich an die DIN 5008 Form B hält. D.h. fixe Positionen für Absender, Empfänger, Rechnungsdetails, Footer und "Mainblock" (Textfeld). Das sollte auch bei längeren Rechnungen/Lieferscheinen/Dokumenten klappen.
Mein Problem: Ich schaffe es einfach nicht die Positionierung von den o.g. Positionen bei BaseDocTemplate, oder SimpleDocTemplate einzuhalten. Mehrseitige Dokumente zersprengen mir meine PDF. Ich habe schon versucht über Frames die Geschichte zu lösen, was auch nicht richtig klappt und stehe nun richtig auf dem Schlauch. Ich bräuchte ein Template oder eine Vorgehensweise von euch, wie ich es schaffe fixe ("statische) Elemente auf der ersten Seite, wie Absender, Empfänger, Rechnungsdetails und Footer exakt positioniert zu erstellen und da zwischen eine Tabelle generiere, die automatisch Zeilenumbrüche und PageBreaks macht. Wobei der nächsten Seite dann der Mainblock weitergeht mit der Tabelle und die Footer + evtl. Seitenanzahl. Für Tipps, Snippets oder Hinweise auf Ressourcen wäre ich euch sehr dankbar.
Beste Grüße
Tekkadan
Reportlab Rechnungs-/Dokumentenerstellung
Mojn,
ich kann zwar nicht wirklich helfen, werde aber interessiert mitlesen...
Dieses
ich kann zwar nicht wirklich helfen, werde aber interessiert mitlesen...
Dieses
kann ich nämlich voll und ganz unterschreiben!tekkadan hat geschrieben: Dienstag 4. Juni 2024, 16:55 Die Dokumentation von ReportLab online finde ich persönlich sehr einfach gehalten und bietet mir keine Hilfe, wenn es um komplexere PDFs geht.
_______________________________________________________________________________
https://www.python-kurs.eu/index.php
https://learnxinyminutes.com/docs/python/ https://learnxinyminutes.com/docs/de-de/python-de/
https://quickref.me/python https://docs.python-guide.org/
Das hier ist speziell für Fedex und auf englisch, aber bestimmt kannst du dich am Code orientieren für etwas Eigenes.
https://github.com/radzhome/fedex-commercial-invoice
Dort findest du im "sample"-Ordner auch ein PDF mit einer Beispiel-Ausgabe.
Ich habe allerdings auf die Schnelle nicht nachgeschaut, inwieweit intelligente Seitenumbrüche unterstützt werden.
Und der Code ist halt 8 Jahre alt. Möglich dass er auf einem aktuellen System nicht auf Anhieb laufen wird...
https://github.com/radzhome/fedex-commercial-invoice
Dort findest du im "sample"-Ordner auch ein PDF mit einer Beispiel-Ausgabe.
Ich habe allerdings auf die Schnelle nicht nachgeschaut, inwieweit intelligente Seitenumbrüche unterstützt werden.
Und der Code ist halt 8 Jahre alt. Möglich dass er auf einem aktuellen System nicht auf Anhieb laufen wird...
Muss es denn reportlab sein? Mit weasyprint kann man das Seitenlayout einfach per CSS definieren.
Hier zum Beispiel: https://github.com/Xiphe/din-5008-css
Hier zum Beispiel: https://github.com/Xiphe/din-5008-css
Vielen Dank euch erstmal für die Antworten. Weasyprint hatte ich auch so einige Probleme mit fontconfig und die Installation auf Windowsmaschinen ist nicht so einfach..Sirius3 hat geschrieben: Mittwoch 5. Juni 2024, 05:40 Muss es denn reportlab sein? Mit weasyprint kann man das Seitenlayout einfach per CSS definieren.
Hier zum Beispiel: https://github.com/Xiphe/din-5008-css
Ich habe mit FPDF rumhantiert und bin schon viel weiter gekommen. Ich schaue mir das FEDEX Beispiel mal an und gebe nochmal Rückmeldung.
EDIT: Das ist schonmal ein guter Ansatz für weasyprint. schaue ich mir auch an. vielen dank

Zuletzt geändert von tekkadan am Mittwoch 5. Juni 2024, 13:45, insgesamt 1-mal geändert.
Das sieht doch auf den ersten Blick sehr hilfreich aus. Vielen Dank! Ich checke es ab und melde mich zurücksnafu hat geschrieben: Mittwoch 5. Juni 2024, 04:53 Das hier ist speziell für Fedex und auf englisch, aber bestimmt kannst du dich am Code orientieren für etwas Eigenes.
https://github.com/radzhome/fedex-commercial-invoice
Dort findest du im "sample"-Ordner auch ein PDF mit einer Beispiel-Ausgabe.
Ich habe allerdings auf die Schnelle nicht nachgeschaut, inwieweit intelligente Seitenumbrüche unterstützt werden.
Und der Code ist halt 8 Jahre alt. Möglich dass er auf einem aktuellen System nicht auf Anhieb laufen wird...

Nach rumprobieren belasse ich es die reportlab library zu nutzen. Ich versuche mich jetzt an fpdf2. Ich bin auch ein gutes Stück weitergekommen, nur habe ich folgendes Problem.
Diese Methode ist für die Rechnungstabelle zuständig. Wie man auf meinem Bild sehen kann, wird sobald ein Seitenumbruch stattfindet, der Rest der Zeile ab der letzten y-Position fortgesetzt, aber auf der NEUEN Seite. Das möchte ich natürlich nicht, da es nicht hineinpasst. Der Seitenumbruch erfolgt, nachdem die y-Position einen Schwellenwert erreicht hat. Ich weiß nicht, wie man Folgendes implementieren kann:
Zeile 1 Spalte 1 erreicht den Schwellenwert nicht.
Zeile 1 Spalte 2 erreicht ihn -> Seitenumbruch, setzt die multicell mit bestehenden Logik auf der nächsten Seite fort. (korrekt)
Zeile 1 Spalte 3 prüft den maximalen y-Wert -> erreicht nicht den Schwellenwert -> kein Seitenumbruch erforderlich. Die Zelle sollte auf der VORHERIGEN Seite gedruckt werden (funktioniert nicht)
Zeile 1 Spalte 4 erreicht wieder den Schwellenwert -> KEIN neuer Seitenumbruch (es sei denn, er ist so lang, dass wir nach der nächsten Seite einen weiteren Seitenumbruch brauchen), da wir bereits einen haben. Mit der multicell auf dem bereits eingefügten Seitenumbruch fortfahren. (funktioniert nicht)
Es muss die anderen Spalten auf der Seite vor dem Seitenumbruch drucken. Wenn es den Schwellenwert erreicht, sollte es keinen zusätzlichen neuen Seitenumbruch hinzufügen, sondern einfach auf der neuen Seite fortfahren. Siehe Bild für Problemstellung.
Der Code:

Danke im Voraus!
Diese Methode ist für die Rechnungstabelle zuständig. Wie man auf meinem Bild sehen kann, wird sobald ein Seitenumbruch stattfindet, der Rest der Zeile ab der letzten y-Position fortgesetzt, aber auf der NEUEN Seite. Das möchte ich natürlich nicht, da es nicht hineinpasst. Der Seitenumbruch erfolgt, nachdem die y-Position einen Schwellenwert erreicht hat. Ich weiß nicht, wie man Folgendes implementieren kann:
Zeile 1 Spalte 1 erreicht den Schwellenwert nicht.
Zeile 1 Spalte 2 erreicht ihn -> Seitenumbruch, setzt die multicell mit bestehenden Logik auf der nächsten Seite fort. (korrekt)
Zeile 1 Spalte 3 prüft den maximalen y-Wert -> erreicht nicht den Schwellenwert -> kein Seitenumbruch erforderlich. Die Zelle sollte auf der VORHERIGEN Seite gedruckt werden (funktioniert nicht)
Zeile 1 Spalte 4 erreicht wieder den Schwellenwert -> KEIN neuer Seitenumbruch (es sei denn, er ist so lang, dass wir nach der nächsten Seite einen weiteren Seitenumbruch brauchen), da wir bereits einen haben. Mit der multicell auf dem bereits eingefügten Seitenumbruch fortfahren. (funktioniert nicht)
Es muss die anderen Spalten auf der Seite vor dem Seitenumbruch drucken. Wenn es den Schwellenwert erreicht, sollte es keinen zusätzlichen neuen Seitenumbruch hinzufügen, sondern einfach auf der neuen Seite fortfahren. Siehe Bild für Problemstellung.
Der Code:
Code: Alles auswählen
def add_table_row(pdf, product, widths, alignments):
pdf.set_font('Helvetica', '', 9)
x_start = 25
y_before = pdf.get_y()
max_y = y_before
for width, field, align in zip(widths, product, alignments):
# Set x position to 25mm for the first column
pdf.set_xy(x_start, y_before)
pdf.multi_cell(width, 5, field, border=1, align=align)
x_start += width # Move x position for the next column
max_y = max(max_y, pdf.get_y())
pdf.set_y(max_y)
# Add table rows
for idx, product in enumerate(product_lines, start=1):
current_y = pdf.get_y()
if current_y + 10 > pdf.h - 25:
pdf.add_page()
current_y = 25 # Set Y position to top margin for new page
pdf.set_xy(25, current_y) # Reset X position for the new row
formatted_menge = f"{locale.format_string('%.2f', product['menge'], grouping=True)} {product['einheit']}"
formatted_preis_netto = f"{locale.format_string('%.2f', product['preis_netto'], grouping=True)} EUR"
formatted_betrag_netto = f"{product['betrag_netto']} EUR"
product_data = [str(idx) + '.', product['produktname'], formatted_menge,
formatted_preis_netto, product['ust'] + ' %', formatted_betrag_netto]
add_table_row(pdf, product_data, widths, alignments)

Danke im Voraus!
Jetzt habe ich auch einen Windows-Rechner gefunden, wo ich weasyprint installieren konnte. Mit pip war das kein Problem, nur dass GTK3 extra installiert werden muß, ist ein bißchen mehr Aufwand. Wenn Du also Probleme hast, solltest Du hier die konkrete Fehlermeldung posten.
Alles weitere ist deutlich angenehmer. Bei Deinem Beispiel reicht der css-Style `page-break-inside:avoid`:
Alles weitere ist deutlich angenehmer. Bei Deinem Beispiel reicht der css-Style `page-break-inside:avoid`:
Code: Alles auswählen
import random
import jinja2
from weasyprint import HTML
environment = jinja2.Environment(autoescape=True)
template = environment.from_string("""
<style>
@page {
@bottom-center {
content: 'Seite ' counter(page) ' von ' counter(pages);
}
}
tr {page-break-inside:avoid;}
td {vertical-align: top;}
</style>
<table>
<thead>
<tr><th>#</th><th>Beschreibung</th><th>Anzahl</th><th>Preis</th><th>USt</th><th>EUR</th></tr>
</thead>
<tbody>
{%for row in data%}
<tr>
{%for cell in row%}
<td>{{cell}}</td>
{%endfor%}
</tr>
{%endfor%}
</tbody>
</table>
""")
data = [
(
n,
jinja2.utils.generate_lorem_ipsum(n=1, html=False),
random.randint(1,4000),
f"{random.random()*100:.2f}",
"19%",
"EUR"
)
for n in range(1, 21)
]
html_text = template.render(data=data)
html = HTML(string=html_text)
html.write_pdf('tabelle.pdf')
Hi, ich wurde zu weasyprint bekehrt. Funktioniert alles super, nur habe ich deinen gtk3 part nicht verstanden. Das sind meine Errors:Sirius3 hat geschrieben: Donnerstag 6. Juni 2024, 08:56 Jetzt habe ich auch einen Windows-Rechner gefunden, wo ich weasyprint installieren konnte. Mit pip war das kein Problem, nur dass GTK3 extra installiert werden muß, ist ein bißchen mehr Aufwand. Wenn Du also Probleme hast, solltest Du hier die konkrete Fehlermeldung posten.
Alles weitere ist deutlich angenehmer. Bei Deinem Beispiel reicht der css-Style `page-break-inside:avoid`:Code: Alles auswählen
import random import jinja2 from weasyprint import HTML environment = jinja2.Environment(autoescape=True) template = environment.from_string(""" <style> @page { @bottom-center { content: 'Seite ' counter(page) ' von ' counter(pages); } } tr {page-break-inside:avoid;} td {vertical-align: top;} </style> <table> <thead> <tr><th>#</th><th>Beschreibung</th><th>Anzahl</th><th>Preis</th><th>USt</th><th>EUR</th></tr> </thead> <tbody> {%for row in data%} <tr> {%for cell in row%} <td>{{cell}}</td> {%endfor%} </tr> {%endfor%} </tbody> </table> """) data = [ ( n, jinja2.utils.generate_lorem_ipsum(n=1, html=False), random.randint(1,4000), f"{random.random()*100:.2f}", "19%", "EUR" ) for n in range(1, 21) ] html_text = template.render(data=data) html = HTML(string=html_text) html.write_pdf('tabelle.pdf')
Code: Alles auswählen
Fontconfig error: Cannot load default config file: No such file: (null)
Fontconfig error: Cannot load default config file: No such file: (null)
Fontconfig error: Cannot load default config file: No such file: (null)