Bestimmtes PDF automatisiert in ein anderes PDF einfügen und speichern

Brati23

Lt. Commander
Registriert
Sep. 2018
Beiträge
1.396
Guten Abend,

ich suche eine automatisierte Lösung um ein PDF in ein anderes PDF einzuzufügen und das neue danach abzuspeichern.
Soweit vermutlich relativ einfach realisierbar mit PDFCreator und alternativen. OS: Windows 10

Das ganze ist aber abhängig davon welche Nummer das erstellte PDF hat. Ausserdem muss ich über die Windows Suche herausfinden welches PDF hinzugefügt werden muss da die Nummer nur im PDF steht, der Name vom PDF jedoch anders ist.
Zur Zeit nutze ich Nitro PDF Pro welches ich auch gerne weiter verwenden würde. Es besitzt die Option PDF zusammenzuführen.

Beispiel. Zur Zeit sieht der Ablauf so aus:
5000.pdf fertig erstellt (nicht mit Nitro PDF PRO), soeben erstelltes PDF mit Nitro öffnen, Explorer/Ordner mit vielen PDF öffnen, in der Suche 5000 eingeben, File ABC.pdf wird gefunden, File öffnen und in Nitro zusammenfügen, Speichern unter 5000+.pdf.

5001.pdf fertig erstellt (nicht mit Nitro PDF PRO), soeben erstelltes PDF mit Nitro öffnen, Explorer/Ordner in der Suche 5001 eingeben, File EFG.pdf wird gefunden, File öffnen und in Nitro zusammenfügen, Speichern unter 5001+.pdf.
usw. tatütata, hurra!


Eigentlich schnell erledigt aber die Aufgabe steht vermutlich längere Zeit immer wieder an.
Daher wollte ich doch mal fragen was Ihr so für Ideen habt.
Ich bin auch offen für (ganz)andere Lösungen. Manchmal sieht man den Wald vor lauter Bäumen nicht.

Vielen Dank und Liebe Grüsse
Brati
 
Klingt nach einer Möglichkeit für Power Automate. Müsstest du dich mal reinlesen
 
  • Gefällt mir
Reaktionen: Brati23
Ist doch ein preiswerte Lösung, die dir dort vorgeschlagen wurde.

Meine Idee wären hier ein Adobe ColdFusion Server oder Abbyy Vantage.

Hier gibt es auch lokale Lösungen. Preislich geht es dann aber bei 8000€ - 10000€ los plus Entwicklungsaufwand im Professionellen Umfeld, plus Server Hardware/Lizenzen.

ColdFusion ist hier der Klassiker. Mit seiner Sprache einfach per Scripting lässt sich die Lösung bauen. Garantierte Kompatibilität zum PDF Format, auch PDF-A für die Archivierung.

Abbyy hat dafür ein WebAPI mit der du dich nahezu unbegrenzt an vorhandene Prozesse anbinden kannst.

Preise können höher sein, je nach benötigter Seiten Anzahl.

Wir nutzen Abbyy in einer lokalen Installation auf Grund Datenschutz Vorgaben bei der Verarbeitung der PDFs.

Cloud wäre deutlich preiswerter, kann dann aber rechtliche Probleme bringen je nach Inhalt deiner PDFs.
 
Danke auch Dir für Deine Antwort @tRITON

Mir war anscheinend nicht ganz klar was für ein Aufwand bzw. Kosten hinter dieser Automatisierung stecken.

Ich werde mal versuchen selber ein Skript zu erstellen. Bin ein wenig eingerostet aber ist bestimmt wie Fahrrad fahren :)

Es ist nicht im professionellen Umfeld daher nicht tragisch wenns mal nicht läuft oder angepasst werden muss.

Ansonsten wird es eben manuell gemacht.
 
  • Gefällt mir
Reaktionen: dms und Brati23
Brati23 schrieb:
pdf fertig erstellt (nicht mit Nitro PDF PRO) .... File ABC.pdf öffnen und in Nitro zusammenfügen ....
Das ist das eigentliche Problem - du arbeitest mit einem Format, was nicht für's Editieren gedacht ist.
Effizienter wäre es die Daten erst im letzten Schritt als pdf auszugeben.
 
  • Gefällt mir
Reaktionen: Brati23
Power automate desktop ist doch kostenlos für eine lokale Instanz. Das dürfte für dich doch erstmal ausreichend sein. das Tool ist in der Version bereits recht vollumfänglich und kann wirklich einiges.
 
  • Gefällt mir
Reaktionen: Brati23
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Brati23
Ich würde das vermutlich mit python oder Java machen, die Library PDFBox für Java ist sehr gut.

Den letzten Schritt könntest du natürlich auch mit Nitro machen, wenn du die Liste hast.

Dafür reicht eine main Methode, man müsste sich hier nicht mal mit Klassen beschäftigen sondern könnte das Prozedural einfach runter schreiben.
 
  • Gefällt mir
Reaktionen: Brati23
Danke @sandreas

In Java habe ich noch nicht viel gemacht. Würde mich aber auch noch interessieren.
Kann mir jemand sagen in wie weit ich das script anpassen muss bzw. ob das in die richtige Richtung geht?

- Alle .pdf im Ordner listen:
Java:
Files.find(Paths.get("hier kommt der Pfad rein"),
           Integer.MAX_VALUE,
           ("hier kommt der Pfad rein", "hier kommt der Dateityp rein") -> fileAttr.isRegularFile())
        .forEach(System.out::println);

- PDF laden und den Text extrahieren:
Java:
import java.io.File;
import java.io.IOException;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
public class ReadingText {

   public static void main(String args[]) throws IOException {

      //Loading an existing document
      File file = new File("hier kommt der Pfad rein");
      PDDocument document = PDDocument.load(file);

      //Instantiate PDFTextStripper class
      PDFTextStripper pdfStripper = new PDFTextStripper();

      //Retrieving text from PDF document
      String text = pdfStripper.getText(document);
      System.out.println(text);

      //Closing the document
      document.close();

   }
}
- Gesuchten Text überprüfen und eine Liste mit den Dateien erstellen die man zusammen führen will:
Da muss ich mal noch schauen was ich finde.

- PDF zusammenführen:
Java:
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import java.io.File;
import java.io.IOException;
public class MergePDFs {
   public static void main(String[] args) throws IOException {
      File file1 = new File("hier kommt der Pfad zuz PDF1 rein");       
      File file2 = new File("hier kommt der Pfad zu PDF2 rein"");   
        
      //Instantiating PDFMergerUtility class
      PDFMergerUtility PDFmerger = new PDFMergerUtility();
        
      //Setting the destination file
      PDFmerger.setDestinationFileName("Hier kommt der Zielpfad rein");
        
      //adding the source files
      PDFmerger.addSource(PDF1);
      PDFmerger.addSource(PDF2);
        
      //Merging the two documents
      PDFmerger.mergeDocuments();
      System.out.println("Documents merged");
   }
}
 
Servus, magst du uns den Quellprozess/Software nennen?
Ist das zB ein statisches Kundanschreiben mit festen Anlagen?

Habe selber vor zig Jahren via Java mit ITEXT implementiert - aber würde zB in Richting Ghostskript schauen.

Ungeprüft: https://gist.github.com/brenopolanski/2ae13095ed7e865b60f5


Nachtrag - Früher hatte ich auch ein Dummyprinterscript für solche Dinge

Ungeprüft: https://www.itnator.net/dummy-drucker-anlegen-howto/ + https://www.itnator.net/dokument-drucken-mit-powershell/
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Brati23
Hier ist der Code. Hab ich mal schnell zusammengekloppt, nix rares, aber er läuft bei mir.

Die Dependencies musst du selber dazuladen - sollte nur PDFBox sein, ich hoffe, das kriegst du hin :-) Ansonsten kannste dich ja noch mal melden.

Ich bin mal grob davon ausgegangen, dass du nach Rechnungsnummern suchen willst, daher:

  • inputPathString: Pfad wo die PDFs liegen
  • outputPathString: Pfad wo die fertige PDF hin soll
  • invoiceNumbersString: Komma-getrennte Nummern, nach denen gesucht werden soll
Du kannst das natürlich auch noch so umbauen, dass an das Programm Argumente übergeben werden oder das du das aus einer Config-Datei liest, aber das überlasse ich dir :-)

Falls dir der Code gefällt, bist du herzlich eingeladen, mir via Github-Sponsors etwas zu spenden, es wäre für einen guten Zweck, dieses Jahr konnte ich €206,00 an die Welthungerhilfe spenden :-).

Java:
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


public class Main {
    private static Map<String, Path> fileTexts = new HashMap<>();

    public static void main(String[] args) {
        var inputPathString = "data/input";
        var outputPathString = "data/output/merged.pdf";
        var invoiceNumbersString = "161126,1234 5678";

        /*
        var inputPathString = args[0];
        var outputPath = args[1];
        var mergeTexts = args[2].split(",");
        */


        try {
            var invoiceNumbers = invoiceNumbersString.split(",");
            var mergeFiles = new ArrayList<Path>();
            Files.find(Paths.get(inputPathString),
                            Integer.MAX_VALUE,
                            (p, type) -> type.isRegularFile())
                    .forEach(Main::extractPdfText);

            for (var invNum : invoiceNumbers) {
                System.out.println("processing " + invNum);
                for (Map.Entry<String, Path> entry : fileTexts.entrySet()) {
                    if (entry.getKey().contains(invNum)) {
                        System.out.println("=> found in " + entry.getValue());
                        mergeFiles.add(entry.getValue());
                        break;
                    }
                }
            }

            PDFMergerUtility PDFmerger = new PDFMergerUtility();

            //Setting the destination file
            PDFmerger.setDestinationFileName(outputPathString);
            for (var source : mergeFiles) {
                PDFmerger.addSource(source.toFile());
            }
            PDFmerger.mergeDocuments(null);


        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private static void extractPdfText(Path p) {
        if (!p.toString().toLowerCase().endsWith(".pdf")) {
            return;
        }
        try {
            PDDocument document = Loader.loadPDF(p.toFile());

            //Instantiate PDFTextStripper class
            PDFTextStripper pdfStripper = new PDFTextStripper();

            //Retrieving text from PDF document
            String text = pdfStripper.getText(document);
            fileTexts.put(text, p);
            // System.out.println(text);

            //Closing the document
            document.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
 
  • Gefällt mir
Reaktionen: nutrix, dms und Brati23
dms schrieb:
Servus, magst du uns den Quellprozess/Software nennen?
Ist das zB ein statisches Kundanschreiben mit festen Anlagen?
Es ist ein Warenwirtschaftssystem welches mir ans Herz gewachsen ist und die Funktion einen dazu passenden QR-Codes hinzufügen leider nicht beherrscht. Deshalb wird der QR-Code zur Zeit per Nitro hinzugefügt.
IText sieht interessant aus. Muss ich mir in mal in Ruhe anschauen.
dms schrieb:
Nachtrag - Früher hatte ich auch ein Dummyprinterscript für solche Dinge
Auch ein guter Ansatz mit dem Dummy-Drucker.
sandreas schrieb:
Hier ist der Code. Hab ich mal schnell zusammengekloppt, nix rares, aber er läuft bei mir.
Wow. Das werde ich gleich mal versuchen. Vielen Dank! Ich gebe noch eine Rückmeldung ob ich es hinbekommen habe.
sandreas schrieb:
Falls dir der Code gefällt, bist du herzlich eingeladen, mir via Github-Sponsors etwas zu spenden
Auf jeden Fall.
 
Ich hab mich dann nach einer PM noch entschlossen, das ganze als Projekt hochzuladen, falls es noch jemand anders mal einfach nutzen oder modifizieren möchte.

https://get.hidrive.com/8jKGow58
Gültig für 7 Tage, also bis 28.01.2024

Enthalten sind:
  • Sourcecode inkl. Test-PDFs als IntelliJ Projekt (inkl. Maven)
  • Eine Jar-Datei, die man einfach ausführen kann (out/artifacts/pdfmerger_jar/pdfmerger.jar)
Möchte man nur das Programm benutzen, kopiert man einfach die pdfmerger.jar irgendwo hin und ruft sie wie folgt auf:

Bash:
# input-pfad (wird rekursiv gescannt)
# output-pdf (wird erstellt UND überschrieben wenns existiert, also vorsicht)
# Rechnungsnummern getrennt mit einem Komma (Leerzeichen werden nicht entfernt, also nicht mit `, ` arbeiten)
java -jar pdfmerger.jar data/input data/output/merged.pdf "161126,1234 5678"

Viel Spaß... :-)
Hinweis: Ich mache keinen stundenlangen Support für dieses Tool. Jeder, der mit dem, was da ist, nicht klar kommt, muss Java lernen. Gegen eine kleine Spende bei GH-Sponsors (siehe oben) lasse ich mich vielleicht dazu breitschlagen, das Programm anzupassen, je nach meiner verfügbaren Zeit :p
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: nutrix und Brati23
Omg Danke Dir! :) Echt nett.

Leider bekomme ich es nicht hin, dass der erwähnte Ablauf automatisch von statten geht.
5001.pdf fertig erstellt (nicht mit Nitro PDF PRO), soeben erstelltes PDF mit Nitro öffnen, Explorer/Ordner in der Suche 5001 eingeben, File EFG.pdf wird gefunden, File öffnen und in Nitro zusammenfügen, Speichern unter 5001+.pdf.
Der Name vom PDF 5000.pdf 5001.pdf 5002.pdf ändert sich ja immer bzw. zählt hoch. Müsste ich also alle Nummern einzeln in Deiner Datei hinterlegen?
Wenn Du keine Zeit hast für Support ist das schon in Ordnung. Da habe ich natürlich vollstes Verständniss. Ist ja Deine Freizeit.
(Datei "RG 5001.pdf" Datei "Dock01100010.pdf" (enthält "5001") und pdfmerger.jar sind auf dem Desktop.)
C:\Users\Brati\Desktop>java -jar pdfmerger.jar data/input data/output/merged.pdf "5001"
Exception in thread "main" java.lang.RuntimeException: java.nio.file.NoSuchFileException: data\input
at Main.main(Main.java:62)
Caused by: java.nio.file.NoSuchFileException: data\input
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(WindowsFileAttributeViews.java:53)
at java.base/sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(WindowsFileAttributeViews.java:38)
at java.base/sun.nio.fs.WindowsFileSystemProvider.readAttributes(WindowsFileSystemProvider.java:197)
at java.base/java.nio.file.Files.readAttributes(Files.java:1853)
at java.base/java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:220)
at java.base/java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:277)
at java.base/java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:323)
at java.base/java.nio.file.FileTreeIterator.<init>(FileTreeIterator.java:71)
at java.base/java.nio.file.Files.find(Files.java:4016)
at Main.main(Main.java:34)
 
Brati23 schrieb:
Wenn Du keine Zeit hast für Support ist das schon in Ordnung. Da habe ich natürlich vollstes Verständniss. Ist ja Deine Freizeit.
Das Problem ist, dass ich deinen Ablauf nicht verstehe bzw. du ihn nicht vollständig erklärt hast.

Wieso sollte man eine vorhandene Datei 5000 haben, dann alle PDFs nach 5000 durchsuchen und dann eine 5001 erstelle, nur um dann alle Dateien wieder nach 5001 zu durchsuchen?

Suche doch einfach nach 5000, 5001, 5002 und füge alle Dateien zusammen, die dafür gefunden werden?!

Suche 5000 => ABC.pdf
Suche 5001 => DEF.pdf
Suche 5002 => GHI.pdf

Füge zusammen: ABC.pdf + DEF.pdf + GHI.pdf
Fertig.

Falls der Ablauf NICHT so sein sollte, dann mache mal ein konkretes Beispiel, was du zu lösen versuchst.
 
Zurück
Oben