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
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 ); } } }