Andreas-Loibl.de Programmieren :: Java
Hintergrundbild

PDFMerge

Beschreibung

pdfMerge ist ein kleines Java-Programm, das mit Hilfe von Ghostscript mehrere PDF-Dateien zusammenhängen kann. Es ist mein erstes Java-Programm und noch nicht ganz fertig, aber ich möchte es trotzdem hier schon mal reinstellen

Edit: Jetzt ist es eigentlich schon soweit fertig, dass es verwendet werden kann und auch richtig funktioniert. Vielleicht baue ich noch einmal noch weitere Funktionen ein, aber erstmal werde ich es hierbei belassen.

Screenshots

pdfMerge-Screenshot

Changelog

03.08.2005:
- funktioniert auch unter Linux richtig

01.06.2005:
- funktioniert unter Linux und Windows gleich
- Drag'n'Drop vom Windows Explorer in die Liste möglich
- Drag'n'Drop vom Konqueror in die Liste möglich
- Drag'n'Drop in der Liste selbst, also umsortieren der Liste mit der Maus möglich
- Während dem Ghostscript-Aufruf eine ProgressBar anzeigen

00.05.2005 - Erste Version

Download

Einfach pdfmerge.tar.gz runterladen, entpacken und das Shellscript "pdfmerge" aufrufen.

Source-Code

// Datei pdfMerge.java
 
import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
 
public class pdfMerge {
    public static void main( String[] args ) {
        ListFrame frm = new ListFrame(
            "pdfMerge by A.Loibl" // Titel des Fensters
        );
 
        frm.setVisible( true );
        frm.pack();
    }
}
 
class ListFrame extends JFrame {
    public ListFrame( String title ) {
        super( title );
 
        addWindowListener(
            new WindowAdapter() {
                public void windowClosing(
                    WindowEvent ev // Wenn das Fenster vom Benutzer geschlossen wird ...
                ) {
                    System.exit( 0 ); // ... dann das Programm beenden
                }
            }
        );
 
        // Container erzeugen
        final JFrame frm = this;
        Container cp = getContentPane();
 
        // Layout erzeugen
        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
 
        cp.setLayout( gb );
 
        /////////////////////////////
        // Die Liste
        //
        final DefaultListModel m = new DefaultListModel();
 
        final DnDList lst = new DnDList( m );
        JScrollPane sp = new JScrollPane( lst );
 
        c.fill = GridBagConstraints.BOTH;
        c.gridx = 0; c.gridy = 0;
        c.weightx = 1.0; c.weighty = 1.0;
        c.gridheight = 3;
 
        gb.setConstraints( sp, c );
        cp.add( sp );
 
        ////////////////////////////
        // Die Buttons
        //
        final JFileChooser fc = new JFileChooser(
            new File( "." )
        );
 
        fc.setFileFilter( new FCFilter() );
        fc.setMultiSelectionEnabled( true );
 
        final JFileChooser fc2 = new JFileChooser(
            new File( "." )
        );
 
        fc2.setFileFilter( new FCFilter() );
        fc2.setMultiSelectionEnabled( false );
 
 
        JButton btnAdd = new JButton( "Hinzufügen...", new ImageIcon( "fileopen.png" ) );
        btnAdd.setHorizontalAlignment( SwingConstants.LEFT );
        btnAdd.addActionListener(
            new ActionListener() {
                public void actionPerformed(
                    ActionEvent ev
                ) {
                    int ret = fc.showOpenDialog( frm );
                    if ( ret == JFileChooser.APPROVE_OPTION ) {
                        File[] f = fc.getSelectedFiles();
                        for ( int i = 0; i < f.length; i++ ) {
                            m.addElement( f[ i ].getPath() );
                        }
                    }
                }
            }
        );
 
        JButton btnRemove = new JButton( "Entfernen", new ImageIcon( "remove.png" ) );
        btnRemove.setHorizontalAlignment( SwingConstants.LEFT );
        btnRemove.addActionListener(
            new ActionListener() {
                public void actionPerformed(
                    ActionEvent ev
                ) {
                    while ( true ) {
                        int i = lst.getSelectedIndex();
                        if ( i < 0 ) break;
 
                        m.remove( i );
                    }
                }
            }
        );
 
        JButton btnSave = new JButton( "Speichern unter...", new ImageIcon( "filesave.png" ) );
        btnSave.setHorizontalAlignment( SwingConstants.LEFT );
        btnSave.addActionListener(
            new ActionListener() {
                public void actionPerformed(
                    ActionEvent ev
                ) {
                    // Im Folgenden wird der neue Default-Speicher-Pfad ermittelt:
                    // Dazu werden die Anfänge aller Pfade verwendet, die gleich sind.
                    // Beispiel:
                    // [ 0 ] = C:\Windows\Desktop\test\test1.pdf
                    // [ 1 ] = C:\Windows\Desktop\test\test2.pdf
                    // [ 2 ] = C:\Windows\Desktop\test\test3.pdf
                    // die Pfade sind bis hier hin gleich: ^
                    // also wird der Pfad davor verwendet: C:\Windows\Desktop\test\test
                    // (das .pdf wird beim Speichern angehängt, wenn es der User nicht mehr ergänzt)
                    String result = m.getElementAt( 0 ).toString();
                    for ( int i = 1; i < m.getSize(); i++ )
                    {
                        String input = result + "<>" + m.getElementAt( i ).toString();
                        Pattern pat = Pattern.compile( "(.*).*?<>\\1.*" );
                        Matcher mat = pat.matcher( input );
                        result = mat.replaceAll( "$1" );
                    }
 
                    File selectedFile = new File(result);
                    if ( selectedFile.isDirectory() ) // Wenn der ermittelte Pfad ein Ordner ist, dann in diesen wechseln ...
                    {
                        fc2.setCurrentDirectory(selectedFile);
                        fc2.setSelectedFile(new File("*.pdf")); // ... und als Dateinamens-Vorgabe '*.pdf' verwenden ...
                    }
                    else // ... ansonsten den Pfad als Default-Dateinamen setzen.
                        fc2.setSelectedFile(selectedFile);
 
                    int ret = fc2.showSaveDialog( frm ); // Dialog anzeigen
                    if ( ret == JFileChooser.APPROVE_OPTION )
                    {
                        File f = fc2.getSelectedFile();
 
                        // Prüfen, ob der Pfad mit '.pdf' oder '.ps' aufhört
                        int fl = Pattern.CASE_INSENSITIVE;
                        Pattern p = Pattern.compile( "\\.(pdf|ps)$", fl );
                        Matcher matcher = p.matcher( f.getAbsolutePath() );
 
                        if ( matcher.find() == false ) f = new File(f.getAbsolutePath() + ".pdf"); // wenn der Pfad nicht auf '.pdf' oder '.ps' endet, '.pdf' anfügen
 
                        String[] ghostscript = { "gs", "gswin32c.exe", "gswin32.exe", "ghostscript.exe" }; // Liste der zu suchenden Programme
                        String cmd = null;
                        boolean inDir = false;
                        if( (new File( "gs" )).isDirectory() ) inDir = true; // Überprüfen, ob das Verzeichnis 'gs' existiert, ...
                        for( int i = 0; i < ghostscript.length; i++ )
                        {
                            try
                            {
                                if( inDir )
                                {
                                    // ... wenn ja, dann den Befehl im Verzeichnis probieren ...
                                    Process pr = Runtime.getRuntime().exec( ghostscript[ i ], null, new File("gs") );
                                    pr.destroy();
                                }
                                else
                                {
                                    // ... ansonsten einfach so probieren
                                    Process pr = Runtime.getRuntime().exec( ghostscript[ i ] );
                                    pr.destroy();
                                }
                            }
                            catch(java.io.IOException ioe) { continue; }
                            cmd = ghostscript[ i ];
                            break;
                        }
 
                        if ( cmd == null )
                        {
                            System.err.println( "Ghostscript konnte nicht gefunden werden!" );
                            System.exit( 1 );
                        }
 
                        cmd = cmd + " -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile="; // der Anfang der Ghostscript-Argumente
                        cmd = cmd + '"' + f.getAbsolutePath() + '"'; // die Ausgabe-Datei anhängen
                        for ( int i = 0; i < m.getSize(); i++ )
                        {
                            // den Befehl zusammensetzen (die Pfade der PDF-Dateien anhängen)
                            cmd = cmd + " " + '"' + ( m.get( i ).toString() ) + '"';
                        }
 
                        ProgressFrame pgframe = new ProgressFrame(); // ein Progress-Fenster erzeugen
                        pgframe.setVisible(true);  // das Progress-Fenster einblenden
                        frm.setVisible(false); // das Hauptfenster ausbldenden
                        pgframe.doCmd( cmd ); // den Ghostscript-Befehl ausführen
                    }
                    else
                    {
                        // Der Dialog wurde vom Benutzer abgebrochen
                    }
                }
            }
        );
 
        // Die Buttons in das Fenster einfügen
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 1; c.gridheight = 1;
        c.weightx = 0.0; c.weighty = 0.0;
        gb.setConstraints( btnAdd, c );
        cp.add( btnAdd );
 
        c.gridy = 1;
        gb.setConstraints( btnRemove, c );
        cp.add( btnRemove );
 
        c.gridy = 2; c.anchor = GridBagConstraints.SOUTH;
        gb.setConstraints( btnSave, c );
        cp.add( btnSave );
    }
}
 
class FCFilter extends javax.swing.filechooser.FileFilter
{
    // PDF- und PS-Dateien zulassen
    static int fl = Pattern.CASE_INSENSITIVE;
    static Pattern p = null;
 
    public FCFilter()
    {
        p = Pattern.compile( "\\.(pdf|ps)$", fl );
    }
 
    public boolean accept( File f )
    {
        if ( f.isDirectory() ) return true;
 
        Matcher m = p.matcher( f.getName() );
        return m.find();
    }
 
    public String getDescription()
    {
        return "PDF/PS-Dateien";
    }
}
 
class DnDList extends JList implements DropTargetListener, DragSourceListener, DragGestureListener
{
    // Eine Datei-Liste, die Drag- und Drop-fähig ist.
    // Außerdem kann man die Elemente mit der Maus umsortieren (auch Drag'n'Drop)
    DropTarget dropTarget = null;
    DragSource dragSource = null;
 
    static int fl = Pattern.CASE_INSENSITIVE;
    static Pattern p = null;
 
    public DnDList( DefaultListModel model )
    {
        dropTarget = new DropTarget( this, this );
        dragSource = new DragSource();
        dragSource.createDefaultDragGestureRecognizer( this, DnDConstants.ACTION_MOVE, this );
        this.setModel( model );
    }
 
    public void dragGestureRecognized( DragGestureEvent dge )
    {
        // Das Verschieben eines Elements einleiten
        Object selected = getSelectedValue();
        if ( selected != null )
        {
            StringSelection text = new StringSelection( selected.toString() );
            dragSource.startDrag( dge, DragSource.DefaultMoveDrop, text, this );
        }
        else
        {
            // Nichts wurde ausgewählt
        }
    }
 
    public void dragDropEnd(DragSourceDropEvent dsde)
    {
        if ( dsde.getDropSuccess() )
        {
            // Ein Element wurde erfolgreich an einer neuen Position eingefügt (gedroppt) und kann deswegen ...
            removeElement(); // an der alten Position gelöscht werden
        }
        else
        {
            // Nicht erfolgreich gedroppt
        }
    }
 
    public void dragExit(DropTargetEvent dte) {}
    public void dragExit(DragSourceEvent dse) {}
    public void dragEnter(DropTargetDragEvent dtde) {}
    public void dragEnter(DragSourceDragEvent dsde) {}
    public void dragOver(DropTargetDragEvent dtde) {}
    public void dragOver(DragSourceDragEvent dsde) {}
    public void dropActionChanged(DropTargetDragEvent dtde) {}
    public void dropActionChanged(DragSourceDragEvent dsde) {}
 
    public void drop(DropTargetDropEvent dtde)
    {
        // Ein Element wurde auf die Liste fallengelassen
        try
        {
            Transferable transferable = dtde.getTransferable();
 
            // Erstmal überprüfen ob es sich bei dem fallengelassenen Element um eine Dateiliste (z.B. vom Windows-Explorer) handelt ...
            if( transferable.isDataFlavorSupported( DataFlavor.javaFileListFlavor ) )
            {
                dtde.acceptDrop( DnDConstants.ACTION_COPY_OR_MOVE ); // ... wenn ja, dann akzeptieren
 
                java.util.List fileList = (java.util.List) transferable.getTransferData( DataFlavor.javaFileListFlavor );
                Iterator iterator = fileList.iterator(); // Die Dateiliste durchgehen
                while ( iterator.hasNext() ) // .. solange noch Dateien in der Liste sind
                {
                    File file = (File) iterator.next();
                    if ( file.isFile() == true ) // Wenn der Pfad eine Datei ist, dann .. (es hätte ja auch ein Ordner sein können)
                    {
                        p = Pattern.compile( "\\.(pdf|ps)$", fl ); // überprüfen, ob die Datei mit '.pdf' oder '.ps' aufhört ...
                        Matcher m = p.matcher( file.getAbsolutePath() );
 
                        if ( m.find() != false ) addElement( file.getAbsolutePath() ); // .. wenn ja, dann das Element an das Ende der Liste einfügen
                    }
                }
                dtde.getDropTargetContext().dropComplete( true ); // Drop-Erfolgsmeldung schicken
            }
            else
            {
                // Wenn es sich bei dem fallengelassenen Element um einen String handelt (z.B. von der eigenen Liste (verschieben!) oder vom Linux-Konqueror) ..
                if( transferable.isDataFlavorSupported( DataFlavor.stringFlavor ) ) 
                {
                    dtde.acceptDrop( DnDConstants.ACTION_MOVE ); // Drop akzeptieren
                    int index = locationToIndex( dtde.getLocation() ); // Die Position des Elements in der Liste ermitteln, auf die gedroppt wurde
 
                    p = Pattern.compile( "\\.(pdf|ps)$", fl ); // überprüfen, ob die Datei mit '.pdf' oder '.ps' aufhört ...
                    String s = (String) transferable.getTransferData( DataFlavor.stringFlavor );
 
                    if ( s.startsWith( "file://" ) ) // wenn der Pfad mit file:// angeht, dann ist es ein Drop einer Datei vom Linux-Konqueror aus
                    {
                        String[] arrDateien = s.split( "file://" ); // deswegen muss die Dateiliste jetzt erzeugt werden, indem der String an dem Code "file://" gesplittet wird
                        for ( int i = 0; i < arrDateien.length; i++ )
                        {
                            Matcher m = p.matcher( s );
                            if ( m.find() ) addElement( arrDateien[i] ); // wenn die Datei eine '.pdf' oder '.ps' ist, dann ans Ende der Liste einfügen
                        }
                    }
                    else // es ist (warscheinlich) eine Verschiebung auf der eigenen Liste
                    {
                        Matcher m = p.matcher( s );
                        if ( m.find() ) addElementAt( index + 1, s ); // wenn die Datei eine '.pdf' oder '.ps' ist, dann an der ermittelten Position (index) das Element einfügen
                    }
 
                    dtde.getDropTargetContext().dropComplete( true ); // Drop-Erfolgsmeldung schicken
                }
                else
                {
                    // Wenn es weder eine Dateiliste, noch ein String ist ...
                    dtde.rejectDrop(); // ... dann Drop nicht annehmen
                }
            }
        }
        catch( IOException exception )
        {
            exception.printStackTrace();
            System.err.println( "Fehler: " + exception.getMessage());
            dtde.rejectDrop();
        }
        catch( UnsupportedFlavorException ufException )
        {
            ufException.printStackTrace();
            System.err.println( "Fehler: " + ufException.getMessage());
            dtde.rejectDrop();
        }
    }
 
    public void addElement( Object s ){
        // Element in die Liste einfügen (ans Ende)
        (( DefaultListModel )getModel()).addElement (s.toString());
    }
 
    public void addElementAt( int index, Object s ){
        // Element in die Liste an der Position 'index' einfügen
        (( DefaultListModel )getModel()).add (index, s.toString());
    }
 
    public void removeElement(){
        // markiertes Element in der Liste löschen
        (( DefaultListModel)getModel()).removeElementAt( getSelectedIndex());
    }
}
 
class ProgressFrame extends JFrame
{
    // Ein Fenster mit einem Fortschrittsbalken wird angezeigt, um die Aktivität des Programms zu signalisieren
    JProgressBar b = null;
    public ProgressFrame()
    {
        // Fenster erzeugen
        super( "pdfMerge" ); // Titel setzen
 
        // Wenn das Fenster vom Benutzer geschlossen wird das ganze Programm beenden
        addWindowListener(
            new WindowAdapter() {
                    public void windowClosing(
                        WindowEvent ev
                    ) {
                        System.exit( 0 );
                    }
                }
            );
 
        this.setSize(250, 100); // Fenstergröße setzen
        this.setLocationRelativeTo(null); // Fenster zentrieren (auf dem Bildschirm)
 
        // Layout des Fensters
        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        Container cp = getContentPane();
        cp.setLayout( gb );
 
        // Label mit Text über dem Fortschrittsbalken einfügen
        JLabel lblBitteWarten = new JLabel( "Bitte warten..." );
        c.gridx = 0; c.gridy = 0;
        c.weightx = 0; c.weighty = 0;
        gb.setConstraints( lblBitteWarten, c ); // die Eigenschaften 'c' dem Label 'lblBitteWarten' zuweisen
        cp.add( lblBitteWarten );
 
        // Fortschrittsbalken einfügen
        b = new JProgressBar();
        b.setStringPainted( true ); // den Text (0%) anzeigen
        b.setIndeterminate( false );
        c.gridx = 0; c.gridy = 1;
        c.weightx = 0; c.weighty = 0;
        gb.setConstraints( b, c ); // die Eigenschaften 'c' der ProgressBar 'b' zuweisen
        cp.add( b );
    }
 
    public void doCmd( String cmd )
    {
        // Führt den angegebenen Befehl 'cmd' aus (Ghostscript-Kommando)
        try
        {
            Process pr = null;
            if( (new File( "gs" )).isDirectory() )
            {
                pr = Runtime.getRuntime().exec( cmd, null, new File("gs") ); // Befehl 'cmd' im Ordner 'gs' ausführen
            }
            else if( (new File( "/bin/sh" )).isFile() )
            {
                String[] cmdArray = { "/bin/sh", "-c", cmd };
                pr = Runtime.getRuntime().exec( cmdArray ); // Befehl 'cmd' ausführen
            }
            else
            {
                pr = Runtime.getRuntime().exec( cmd ); // Befehl 'cmd' ausführen
            }
            final BufferedReader procerr = new BufferedReader( new InputStreamReader( pr.getErrorStream() ) ); // die Fehlerausgabe des Prozesses auffangen
 
            java.util.Timer timer = new java.util.Timer(); // Timer erzeugen, der 4/10 Sekunden später ausgeführt wird
            timer.schedule(
                new TimerTask()
                {
                    public void run()
                    {
                        // Fortschrittbalken-Eigenschaften neu setzen (Unbekannte Laufzeit)
                        b.setStringPainted( false );
                        b.setIndeterminate( true );
                        // Auf das Ende des Ghost-Script-Prozesses warten
                        try { while ( procerr.readLine() != null ) {} }
                        catch(Exception e) { System.err.println( e ); System.exit( 1 ); } // Bei einem Fehler beenden
                        // Fortschrittbalken-Eigenschaften neu setzen (100% - wir sind fertig)
                        b.setIndeterminate( false );
                        b.setStringPainted( true );
                        b.setValue( 100 );
 
                        java.util.Timer timer = new java.util.Timer(); // Timer erzeugen, der 1 sek später ...
                        timer.schedule(
                              new TimerTask()
                              {
                                  public void run()
                                  {
                                      System.exit( 0 );  // ... das Programm beendet
                                  }
                              }, 1000);
                    }
                }, 400);
        }
        catch(java.io.IOException ioe) // Fehlerbehandlung: Beim Ausführen von Ghostscript ist ein Fehler aufgetreten
        {
            System.err.println("Es ist ein Fehler beim Ausführen von Ghostscript aufgetreten");
            System.exit( 1 );
        }
    }
}