txt-Datei als .pdf ausgeben mit reportlab - best practice

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
WalterT
User
Beiträge: 31
Registriert: Dienstag 5. Dezember 2017, 16:00

Liebe Freunde und Experten,

wie würdet Ihr folgendes lösen:
- Textdatei zeilenweise lesen und die ersten 30 Zeilen mit reportlab auf dem canvas platzieren
- Ich muss dabei den rechten Rand einhalten, d.h. längere Zeilen sind umzubrechen
- CR/LF am Ende einer Zeile nicht mit ausgeben (schwarzes Quadrat)

Ich hab schon einiges probiert: zeilenweises Schreiben, Textobjekt,.... Das beste Ergebnis hab ich mit drawString() erzielt, da stehen zumindest meine 30 Zeilen da, aber sind tw. zu lang. Mit einem Textobjekt komme ich auch nicht recht weiter.

Vielleicht bin ich auch auf dem Holzweg und es geht viel einfacher.

Wer weiß Rat?
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@WalterT: dann zeig doch, was Du versucht hast, und was Dir daran konkret nicht gefällt.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ja, es geht einfach. Wie ich gerade im anderen Thread von dir schon geschrieben habe: keine low-level Operationen auf dem Canvas anwenden, sondern Platypus aus ReportLab nutzen. Damit ist das viel einfacher, weil automatisch Zeilenumbrüche erfolgen, du Seitenränder für die Seite definieren kannst usw.

Gruß , noisefloor
WalterT
User
Beiträge: 31
Registriert: Dienstag 5. Dezember 2017, 16:00

@noisefloor: Du hast ja Recht, ich habe mir Platypus angesehen und bin damit bei weitem überfordert.

bisher habe ich:
...hier passiert alles möglich, u.a.:

.....for-Schleife über eine Liste von Files...
...
pdf = canvas.Canvas(output_folder_element, pagesize = A4)
...
# TXT verarbeiten
elif file_extension in (
".csv",
".CSV",
".txt",
".TXT"):
icon = ICON_PATH + '\\txt.png'
pdf_generate(pdf, icon, element)
pdf.setFont("Courier", 9)
with open(input_folder_element, encoding='ISO8859-1') as file_content:
counter = 1
for file_content_line in file_content.readlines():
pdf.drawString(20, 710-counter*11, file_content_line)
counter = counter + 1
if counter > 30:
break

# pdf generieren - Grundfunktionen
def pdf_generate(pdf_used, icon_used, element_used):
pdf_used.drawImage(icon_used, 20, 770, 50, 50)
pdf_used.drawString(20, 750, element_used)
pdf_used.line(20, 740, 570, 740)

Ich positioniere zuerst eine kleine Grafik (Icon), darunter kommt ein Dateiname, dann ein waagrechter Strich.

Das funktionert so weit. pdf.drawSttring positioniert mir die einzelnen Zeilen auf den canvas, allerdings werden lange Zeilen vom rechten "Papier"-Rand abgeschnitten und CR/LF hab ich auch drinnen als kleines schwarzes Quadrat.

Sorry für den wahrscheinlich primitiven Programmier-Stil, aber mit PYthon bin ich Anfänger (hab früher einmal COBOL, Pascal und PL1 programmiert)
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
ich habe mir Platypus angesehen und bin damit bei weitem überfordert.
Weil...? Es ist ja nun mal so, dass alles mega-einfach ist und man alles nach 1x lesen 110% kapiert hast. BTW: wenn man so Module benutzt hilft es _sehr_ wenn man ein grundlegendes Verständnis von der Objektorientierung von Python sowie Klassen bzw. deren Instanzen und Methoden von Klasse hat.

`drawString` bricht keine Zeilen um. Darum musst du dich kümmern. Bzw. AFAIR hat Reportlab dafür Hilfsklassen / -funktionen an Bord. `drawString` kennt keine Seitenränder. Wenn du welche haben willst, musst du da bei deiner manuellen Kalkulation der String-Länge vor dem Umbruch berücksichtigen. Ich orakle jetzt mal: das wird dich überfordern.

Platypus macht das alles OOTB. Zugegebener Maßen ist die Reportlab Doku bei Platypus ein wenig gewöhnungsbedürftig, weil die Bespiele nicht einfach anfangen. Das was du für den Anfang brauchst ist das kurze Listing auf Seite 69/70 der Doku. Nur das du die Liste `story` in einer Schleife mit den ersten 30 Zeilen deiner Textdatei füllst und 30x `story.append(Paragraph(variablenname_der_textzeile, styleN)` einfügst.

Und vielleicht noch die pagesize von `letter` auf `A4` ändern.

Ansonsten gilt wie immer: probieren, Fehlermeldung und Code posten, dann wird gezielt geholfen.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@WalterT: Anmerkungen zu gezeigten Quelltext: Pfadteile setzt man mit `os.path.join()` zusammen, nicht mit ``+``.

Es sieht ziemlich weit eingerückt aus, das würde ich persönlich wohl in mehrere Funktionen aufteilen. Zum Beispiel das ermitteln der Dateinamen die da eingelesen werden, von der Erzeugung des/der PDF(s) trennen. Damit man die Einzelteile leichter testen/ausprobieren kann.

Den Test auf die Dateiendung kann man etwas kürzer machen in dem man die zu vergleichende Zeichenkette in Kleinbuchstaben umwandelt, falls es kein Problem ist das dann '.Txt', '.TxT', … auch erfasst werden. Eventuell ist auch das `glob`-Modul oder das `pathlib`-Modul interessant.

`file.readlines()` liest alle Zeilen auf einmal in den Speicher. Das ist hier unnötig, denn Dateiobjekte sind iterierbar und liefern dabei die einzelnen Zeilen.

Das manuelle hochzählen von `counter` würde man in Python mit der `enumerate()`-Funktion lösen.

Statt auf die 30 selbst zu testen und die Schleife abzubrechen, kann man `itertools.islice()` verwenden.

Ungetestet:

Code: Alles auswählen

                # TXT verarbeiten
                elif file_extension.lower() in ['.csv', '.txt']:
                    icon = os.path.join(ICON_PATH, 'txt.png')
                    pdf_generate(pdf, icon, element)
                    pdf.setFont('Courier', 9)
                    with open(
                        input_folder_element, encoding='ISO-8859-1'
                    ) as lines:
                        for counter, line in enumerate(islice(lines, 30), 1):
                            pdf.drawString(20, 710 - counter * 11, line)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
WalterT
User
Beiträge: 31
Registriert: Dienstag 5. Dezember 2017, 16:00

WOW! Danke schön....

das macht Lust auf mehr ! Besonders dann, wenn man wie ich bis Mitte der Neunziger Cobol programmiert hat mit 25x80 Zeichen am Schirm. Heute unvorstellbar, damals pfeilschnell mit Btrieve im Background...
Da ist es garnicht so einfach, das alte sequenzeile Denkmuster aus dem Hirn zu kriegen...

Ich werde das alles probieren.

@__blackjack__: Sag, wie kriegst du den Code so schön färbig und formatiert hier ins Forum rein?

lg aus dem Salzburger Land
Walter
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@WalterT: Es gibt [ code ]-Tags. Ohne die Leerzeichen zwischen den Klammern. Im vollständigen Editor mit Vorschau gibt es eine Schaltfläche zum Einfügen von den Tags die mit „</>“ beschriftet ist. Du kannst auch einen Beitrag mit Syntaxhervorhebung zitieren um zu sehen wie das im Quelltext des Beitrags aussieht.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
WalterT
User
Beiträge: 31
Registriert: Dienstag 5. Dezember 2017, 16:00

Also: (bin ja lernfähig)

Code: Alles auswählen

# TXT verarbeiten
                elif file_extension.lower() in ['.txt', '.csv']:
                    icon = os.path.join(ICON_PATH, 'txt.png')
                    pdf_generate(pdf, icon, element)
                    text_field = pdf.beginText()
                    text_field.setTextOrigin(20, 700)
                    text_field.setFont("Courier", 9)
                    with open(input_folder_element, encoding='ISO-8859-1') as lines:
                        for counter, line in enumerate(islice(lines, 30), 1):
                            text_field.textLines(line, 0)
                    pdf.drawText(text_field)
hab versucht, das mit einem TextObjekt zu machen. Das funktioniert, ist aber um nichts besser als das einfache drawLine oder gar fpdf, im Gegenteil, unter fpdf hatte ich keine schwarzen kleinen Quadrate für CR/LF oder Sonderzeichen, auch die Einrückungen werden nicht rausgeschnitten.... z.B. macht text_field.textlines diesbezüglich was anders als text_field.textLine ! Mit textline habe ich mehr Schrott in der Ansicht.

Ich will ja nichts weiter als 30 Zeilen einer .txt-Datei in ein pdf bringen, so wie ich das im Notepad sehe. Also doch Platypus :cry: ??
Irgendwie habe ich das Gefühl, ich bin da komplett auf dem falschen Dampfer....
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Also doch Platypus
Ja - habe ich dir schon 2x gesagt. Außerdem habe ich dir gesagt, was du machen müsstest, wenn du weiter bei den low-level Methoden auf dem Canvas bleiben wolltst (willst du aber nicht).

Du machst es dir gerade selber extrem schwer indem du dich weigerst, dich mal eine halbe Stunde oder so mit Platypus zu beschäftigen.

Gruß, noisefloor
WalterT
User
Beiträge: 31
Registriert: Dienstag 5. Dezember 2017, 16:00

Hallo,

danke vorerst.
War 14 Tage im Spital und bin derzeit nicht in der Lage, was zu programmieren, aber ich bleibe an meinem Problem dran, dauert momentan etwas...

lg
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Gute Genesung auf das du bald wieder fit bist.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Antworten