pdf verkleinern

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
andi007
User
Beiträge: 11
Registriert: Samstag 20. März 2021, 15:19

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
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

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.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
noisefloor
User
Beiträge: 4196
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

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
andi007
User
Beiträge: 11
Registriert: Samstag 20. März 2021, 15:19

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
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

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.
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

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()
andi007
User
Beiträge: 11
Registriert: Samstag 20. März 2021, 15:19

__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.
andi007
User
Beiträge: 11
Registriert: Samstag 20. März 2021, 15:19

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.
Antworten