Seite 1 von 1

pdf verkleinern

Verfasst: Montag 31. Januar 2022, 20:49
von andi007
Moinsen Allerseits,
ich habe mir mit "reportlab.pdfgen" ein pdf mit Schrift und Bildern erstellt. Diese Datei ist aber zig MB groß.
Wenn ich sie im Acrobat-Reader öffne und als komprimierte Datei abspeichere ist sie nur noch etwa 1 MB groß, was auch mein Ziel war.

Wie kann ich mittels Python dies auch erreichen?
Am liebsten würde ich meine Datei mit "reportlab" erstellen und anschließend mit einem weiteren Tool direkt in meinem Programmcode verkleinern.

Hat Jemand von Euch eine Idee, wie ich das hinbekommen kann?

LG Andi

Re: pdf verkleinern

Verfasst: Montag 31. Januar 2022, 21:37
von __blackjack__
Sicherstellen, das komprimiert wird und ansonsten vermute ich stark, dass es an den Bildern liegt, das die vom Acrobat-Reader in schlechterer Qualität und damit halt auch kleiner, gespeichert werden.

Re: pdf verkleinern

Verfasst: Dienstag 1. Februar 2022, 06:55
von noisefloor
Hallo,

wie groß sind denn die Bilder und wie viel DPI haben die? ReportLab komprimiert die ja beim Einbauen ins PDF nichts, sondern skaliert "nur". Das ist dann normalerweise der 1. Punkt, wo PDF Komprimierer ansetzen - Bildgröße verkleinern und / oder Auslösung des Bilds runter.

Kannst du im Skript z.B. mit Pillow machen, bevor du das Bild ns PDF einbaust.

Gruß, noisefloor

Re: pdf verkleinern

Verfasst: Sonntag 6. Februar 2022, 10:05
von andi007
sorry, leider hatte ich keine Emailbenachrichtigung über die Antworten erhalten.

Hier mal mein Code mit dem ich das pdf erzeuge:

Code: Alles auswählen


    def pdf_empf_header(self, pdfcontainer):
        from reportlab.lib.colors import black, HexColor
        from datetime import datetime
        from reportlab.lib.units import cm
        from reportlab.lib.pagesizes import A4

        logo = "Logo-2800.png"
        seitenbreite, seitenhoehe = A4
        pdfcontainer.setPageCompression(1)
        # Logo
        pdfcontainer.drawImage(icopath + logo, seitenbreite - (14 * cm), seitenhoehe - (4 * cm), 12 * cm, 3 * cm, mask='auto', preserveAspectRatio=True)
        # Logo Unterzeile
        pdfcontainer.setFont('Helvetica', 16)
        pdfcontainer.setFillColor(black)
        pdfcontainer.drawString(seitenbreite - (8 * cm), seitenhoehe - (4.5 * cm), ' GmbH')
        # Ueberschrift
        pdfcontainer.setFont('Helvetica-Bold', 14)
        pdfcontainer.drawString(2 * cm, seitenhoehe - (7 * cm), 'Empfehlungen Aquarientiere')
        pdfcontainer.setFillColor(HexColor('#64a1d5'))
        pdfcontainer.setFont('Helvetica', 14)
        pdfcontainer.drawString(2 * cm, seitenhoehe - (7.5 * cm), "für KW " + format(int(datetime.today().strftime('%V')) + 1, '02d') + " und KW " + format(int(datetime.today().strftime('%V')) + 2, '02d'))
        pdfcontainer.setFillColor(HexColor('#64a1d5'), alpha=0.25)
        breite = seitenbreite * 1.25
        hoehe = seitenbreite * 0.85
        anfangx = breite * 0.25
        anfangy = -hoehe * 0.4
        pdfcontainer.ellipse(anfangx, anfangy, anfangx + breite, anfangy + hoehe, fill=1, stroke=0)
        return pdfcontainer

    def pdf_empf_bild(self, pdfcontainer, dateinamepfad, dateibeschriftung, xpos, ypos, bildbreite, bildhoehe, index=0):
        from reportlab.lib.colors import Color, black, HexColor, white
        from reportlab.lib.units import cm
        from reportlab.pdfbase.pdfmetrics import stringWidth
        from reportlab.lib.pagesizes import A4
        seitenbreite, seitenhoehe = A4
        pdfcontainer.setPageCompression(1)
        pdfcontainer.setFillColor(HexColor('#eeeeee'), alpha=0.75)
        pdfcontainer.setStrokeColor(HexColor('#cccccc'), alpha=0.5)
        pdfcontainer.setLineWidth(3)
        pdfcontainer.roundRect(xpos - (0.25 * cm)+2, ypos - (0.25 * cm)-2, bildbreite + (0.5 * cm), bildhoehe + (0.5 * cm), 3, stroke=1, fill=1)  # draw rectangle
        pdfcontainer.setLineWidth(1)
        pdfcontainer.setStrokeColor(HexColor('#cccccc'), alpha=1)
        pdfcontainer.roundRect(xpos - (0.25 * cm), ypos - (0.25 * cm), bildbreite + (0.5 * cm), bildhoehe + (0.5 * cm), 3, stroke=1, fill=1)  # draw rectangle
        pdfcontainer.setStrokeColor(HexColor('#aaaaaa'), alpha=0.75)
        pdfcontainer.setLineWidth(1)
        pdfcontainer.roundRect(xpos+2, ypos-2, bildbreite-1, bildhoehe-1, 0, stroke=1, fill=0)  # draw rectangle
        pdfcontainer.setStrokeColor(HexColor('#aaaaaa'), alpha=1)
        pdfcontainer.setFillColor(black, alpha=1)
        pdfcontainer.drawImage(dateinamepfad, xpos, ypos, bildbreite, bildhoehe, preserveAspectRatio=True)
        pdfcontainer.setStrokeColor(HexColor('#cccccc'), alpha=1)
        pdfcontainer.setLineWidth(1)
        pdfcontainer.roundRect(xpos, ypos, bildbreite, bildhoehe, 0, stroke=1, fill=0)  # draw rectangle
        pdfcontainer.setFillColor(HexColor('#64a1d5'), alpha=0.85)
        pdfcontainer.setStrokeColor(HexColor('#64a1d5'), alpha=1)
        wortbreite = stringWidth(dateibeschriftung, 'Helvetica-Bold', 16)
        pdfcontainer.roundRect((3.5 * cm) + (index * (seitenbreite - wortbreite*1.1 - (7 * cm))), ypos + (0.25 * cm) - (0.75 * cm), wortbreite * 1.1, 1.25 * cm, 8, stroke=1, fill=1)  # draw rectangle
        pdfcontainer.setFillColor(white)
        pdfcontainer.setFont('Helvetica-Bold', 16)
        pdfcontainer.drawString((0.5 * cm) + (3.5 * cm) + (index * (seitenbreite - wortbreite*1.1 - (7 * cm))), ypos + (0.7 * cm) - (0.75 * cm), dateibeschriftung)

        return pdfcontainer

    def writetopdf(self, exppfad, empdf, pdfdatei):
        '''
        '''
        from reportlab.pdfgen import canvas
        from reportlab.lib.pagesizes import A4
        from reportlab.lib.units import cm

        seitenbreite, seitenhoehe = A4
        bildbreite = 12 * cm
        bildhoehe = 9 * cm

        tmp = pdfdatei.split(".")
        pdf = canvas.Canvas(exppfad + pdfdatei, pagesize=A4, pageCompression=1)
        pdf.setTitle(tmp[0])
        pdf.setPageCompression(1)
        pdf.setCreator('andi')

        for index, zeile in empdf.iterrows():
            if (index % 2 == 0): # oberes Bild
                pdf = self.pdf_empf_header(pdf)
                pdf = self.pdf_empf_bild(pdf, zeile["DATEINAME"], zeile["ARTNR"] + " " + zeile["SUCHBEGRIFF"] + " " + zeile["GROESSE"], (seitenbreite-bildbreite)/2, seitenhoehe - 2 * bildhoehe + 1 * cm, bildbreite, bildhoehe,0)
            else:
                pdf = self.pdf_empf_bild(pdf, zeile["DATEINAME"], zeile["ARTNR"] + " " + zeile["SUCHBEGRIFF"] + " " + zeile["GROESSE"], (seitenbreite-bildbreite)/2, seitenhoehe - (10 * cm) - 2 * bildhoehe, bildbreite, bildhoehe,1)
                pdf.showPage()

        pdf.save()
wie groß sind denn die Bilder und wie viel DPI haben die? ReportLab komprimiert die ja beim Einbauen ins PDF nichts, sondern skaliert "nur". Das ist dann normalerweise der 1. Punkt, wo PDF Komprimierer ansetzen - Bildgröße verkleinern und / oder Auslösung des Bilds runter.
hmm, naja, die Auflösung dürfte doch irrelevant sein, da es sich ja nur um ein künstliches Konstrukt handelt.
Ich hatte es so verstanden, dass wenn ich den Ursprung und die Breite und die Höhe in Pixel angebe, dass reportlab die Bilder dann in genau diesen Pixeln speichert?

LG Andi

Re: pdf verkleinern

Verfasst: Sonntag 6. Februar 2022, 10:16
von __deets__
Du hast falsch verstanden. Nur weil ein Bild an eine Stelle skaliert und verschoben wird, ändert sich doch nicht die Menge der Ursprungsdaten. Oder wird dein PDF kleiner, weil es als thumbnail im explorer dargestellt wird?

Du musst die also entsprechend selbst verkleinern.

Re: pdf verkleinern

Verfasst: Sonntag 6. Februar 2022, 11:18
von Sirius3
Benutze keine kryptisch Abkürzungen. Was ist ein `empf`?
Importe stehen alle am Anfang der Datei, nicht innerhalb von Funktionen, zumal Du da die Importe in jeder Funktion wiederholst.
`pdf_empf_header` und `pdf_empf_bild` bekommen pdfcontainer als Argument und geben es wieder zurück. Das ist wenig sinnvoll, da die Stelle, die die Methode aufruft, ja schon den pdfcontainer kennt, also mit dem Rückgabewert nichts anfangen kann.
Keine Deiner Methoden benutzt `self`. Es scheint also so, dass Du ganz normale Funktionen in eine Klasse gestopft hast, wo sie nicht hinein gehören. Klassen sind keine Selbstzweck für sich.
Das was Du da mit `format` machst, ist sehr umständlich. `format` benutzt man nicht als Funktion, sondern heutzutage via f-Strings.
Du rufst mehrfach `today` auf, was dazu führen kann, dass der erste Aufruf ein Tag früher stattfindet als der zweite, Du bekommst also ein PDF das in sich inkonsistent ist. Man erzeugt nicht einen String aus einem datetime-Objekt, um es danach wieder in ein int umzuwandeln.

Dateipfade sind keine einfachen Strings, die verarbeitet man also nicht mit split oder +.
Heutzutage benutzt man dafür pathlib.Path.

Code: Alles auswählen

from datetime import datetime
from reportlab.pdfgen import canvas
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib.colors import Color, black, HexColor, white

def pdf_empf_header(pdfcontainer, date):
    logo = "Logo-2800.png"
    seitenbreite, seitenhoehe = A4
    pdfcontainer.setPageCompression(1)
    # Logo
    pdfcontainer.drawImage(icopath + logo, seitenbreite - (14 * cm), seitenhoehe - (4 * cm), 12 * cm, 3 * cm, mask='auto', preserveAspectRatio=True)
    # Logo Unterzeile
    pdfcontainer.setFont('Helvetica', 16)
    pdfcontainer.setFillColor(black)
    pdfcontainer.drawString(seitenbreite - (8 * cm), seitenhoehe - (4.5 * cm), ' GmbH')
    # Ueberschrift
    pdfcontainer.setFont('Helvetica-Bold', 14)
    pdfcontainer.drawString(2 * cm, seitenhoehe - (7 * cm), 'Empfehlungen Aquarientiere')
    pdfcontainer.setFillColor(HexColor('#64a1d5'))
    pdfcontainer.setFont('Helvetica', 14)
    week_number = date.isocalendar()[1]
    pdfcontainer.drawString(2 * cm, seitenhoehe - (7.5 * cm), f"für KW {week_number + 1:02d} und KW {week_number + 2:02d}")
    pdfcontainer.setFillColor(HexColor('#64a1d5'), alpha=0.25)
    breite = seitenbreite * 1.25
    hoehe = seitenbreite * 0.85
    anfangx = breite * 0.25
    anfangy = -hoehe * 0.4
    pdfcontainer.ellipse(anfangx, anfangy, anfangx + breite, anfangy + hoehe, fill=1, stroke=0)

def pdf_empf_bild(pdfcontainer, dateinamepfad, dateibeschriftung, xpos, ypos, bildbreite, bildhoehe, index=0):
    seitenbreite, seitenhoehe = A4
    pdfcontainer.setPageCompression(1)
    pdfcontainer.setFillColor(HexColor('#eeeeee'), alpha=0.75)
    pdfcontainer.setStrokeColor(HexColor('#cccccc'), alpha=0.5)
    pdfcontainer.setLineWidth(3)
    pdfcontainer.roundRect(xpos - (0.25 * cm)+2, ypos - (0.25 * cm)-2, bildbreite + (0.5 * cm), bildhoehe + (0.5 * cm), 3, stroke=1, fill=1)  # draw rectangle
    pdfcontainer.setLineWidth(1)
    pdfcontainer.setStrokeColor(HexColor('#cccccc'), alpha=1)
    pdfcontainer.roundRect(xpos - (0.25 * cm), ypos - (0.25 * cm), bildbreite + (0.5 * cm), bildhoehe + (0.5 * cm), 3, stroke=1, fill=1)  # draw rectangle
    pdfcontainer.setStrokeColor(HexColor('#aaaaaa'), alpha=0.75)
    pdfcontainer.setLineWidth(1)
    pdfcontainer.roundRect(xpos+2, ypos-2, bildbreite-1, bildhoehe-1, 0, stroke=1, fill=0)  # draw rectangle
    pdfcontainer.setStrokeColor(HexColor('#aaaaaa'), alpha=1)
    pdfcontainer.setFillColor(black, alpha=1)
    pdfcontainer.drawImage(dateinamepfad, xpos, ypos, bildbreite, bildhoehe, preserveAspectRatio=True)
    pdfcontainer.setStrokeColor(HexColor('#cccccc'), alpha=1)
    pdfcontainer.setLineWidth(1)
    pdfcontainer.roundRect(xpos, ypos, bildbreite, bildhoehe, 0, stroke=1, fill=0)  # draw rectangle
    pdfcontainer.setFillColor(HexColor('#64a1d5'), alpha=0.85)
    pdfcontainer.setStrokeColor(HexColor('#64a1d5'), alpha=1)
    wortbreite = stringWidth(dateibeschriftung, 'Helvetica-Bold', 16)
    pdfcontainer.roundRect((3.5 * cm) + (index * (seitenbreite - wortbreite*1.1 - (7 * cm))), ypos + (0.25 * cm) - (0.75 * cm), wortbreite * 1.1, 1.25 * cm, 8, stroke=1, fill=1)  # draw rectangle
    pdfcontainer.setFillColor(white)
    pdfcontainer.setFont('Helvetica-Bold', 16)
    pdfcontainer.drawString((0.5 * cm) + (3.5 * cm) + (index * (seitenbreite - wortbreite*1.1 - (7 * cm))), ypos + (0.7 * cm) - (0.75 * cm), dateibeschriftung)

def writetopdf(pdf_path, empdf):
    pdf = canvas.Canvas(str(pdf_path), pagesize=A4, pageCompression=1)
    pdf.setTitle(pdf_path.stem)
    pdf.setPageCompression(1)
    pdf.setCreator('andi')

    seitenbreite, seitenhoehe = A4
    bildbreite = 12 * cm
    bildhoehe = 9 * cm
    date = datetime.today()
    for index, zeile in empdf.iterrows():
        position = index % 2
        if position == 0: # oberes Bild
            pdf = self.pdf_empf_header(pdf, date)
        pdf = self.pdf_empf_bild(pdf,
            zeile["DATEINAME"],
            f'{zeile["ARTNR"]} {zeile["SUCHBEGRIFF"]} {zeile["GROESSE"]}'
            (seitenbreite-bildbreite)/2,
            seitenhoehe - 2 * bildhoehe + (1 if position == 0 else -10) * cm,
            bildbreite, bildhoehe, position)
        if position == 1:
            pdf.showPage()

    pdf.save()

Re: pdf verkleinern

Verfasst: Sonntag 6. Februar 2022, 15:48
von andi007
__deets__ hat geschrieben: Sonntag 6. Februar 2022, 10:16 Du hast falsch verstanden. Nur weil ein Bild an eine Stelle skaliert und verschoben wird, ändert sich doch nicht die Menge der Ursprungsdaten. Oder wird dein PDF kleiner, weil es als thumbnail im explorer dargestellt wird?

Du musst die also entsprechend selbst verkleinern.
Danke. Ich hatte das echt so verstanden, dass drawImage das Bild mit den angegeben Pixel neu "malt".

Mit
pdfBild = ImageOps.fit(orgBild, (1200, 900))
pdfBild.save(root + pdfverzeichnis + "\\" + filename + file_extension, optimize=True, quality=30)
erreiche ich das gewünschte.

Re: pdf verkleinern

Verfasst: Sonntag 6. Februar 2022, 16:09
von andi007
Sirius3 hat geschrieben: Sonntag 6. Februar 2022, 11:18 Benutze keine kryptisch Abkürzungen. Was ist ein `empf`?
ok, die Datei heisst Empfehlungen, ich versuche halt immer zuuuuu lange Begriffe zu vermeiden.
Sirius3 hat geschrieben: Sonntag 6. Februar 2022, 11:18 Importe stehen alle am Anfang der Datei, nicht innerhalb von Funktionen, zumal Du da die Importe in jeder Funktion wiederholst.
ok, ich dachte, dass die so nur beim Aufruf der Methode geladen werden, ich brauche sie ja nicht in jeder Funtkion.
Sirius3 hat geschrieben: Sonntag 6. Februar 2022, 11:18 `pdf_empf_header` und `pdf_empf_bild` bekommen pdfcontainer als Argument und geben es wieder zurück. Das ist wenig sinnvoll, da die Stelle, die die Methode aufruft, ja schon den pdfcontainer kennt, also mit dem Rückgabewert nichts anfangen kann.

Keine Deiner Methoden benutzt `self`. Es scheint also so, dass Du ganz normale Funktionen in eine Klasse gestopft hast, wo sie nicht hinein gehören. Klassen sind keine Selbstzweck für sich.
naja, ich komme halt aus der prozeduralen Programmierung. Hatte mich mit Klassen nie näher auseinandergesetzt.
Sirius3 hat geschrieben: Sonntag 6. Februar 2022, 11:18 Das was Du da mit `format` machst, ist sehr umständlich. `format` benutzt man nicht als Funktion, sondern heutzutage via f-Strings.
Danke, ich bin bei python ein Quereinsteiger und arbeite mich in ein vorhandenes Projekt rein und erweitere es. Vieles war schon so vorhanden und ich habe es dann so weitergeführt.
Sirius3 hat geschrieben: Sonntag 6. Februar 2022, 11:18 Du rufst mehrfach `today` auf, was dazu führen kann, dass der erste Aufruf ein Tag früher stattfindet als der zweite, Du bekommst also ein PDF das in sich inkonsistent ist. Man erzeugt nicht einen String aus einem datetime-Objekt, um es danach wieder in ein int umzuwandeln.
Guter Einwand, sollte zwar keine Auswirkungen haben, da hast Du recht, das ist so sicherer!


Danke und LG

Andi
Sirius3 hat geschrieben: Sonntag 6. Februar 2022, 11:18 Dateipfade sind keine einfachen Strings, die verarbeitet man also nicht mit split oder +.
Heutzutage benutzt man dafür pathlib.Path.