KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > util > PDFMergerUtility


1 /**
2  * Copyright (c) 2003-2006, www.pdfbox.org
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 3. Neither the name of pdfbox; nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * http://www.pdfbox.org
29  *
30  */

31 package org.pdfbox.util;
32
33 import java.io.File JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.HashMap JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.Map JavaDoc;
40
41 import org.pdfbox.cos.COSArray;
42 import org.pdfbox.cos.COSBase;
43 import org.pdfbox.cos.COSDictionary;
44 import org.pdfbox.cos.COSInteger;
45 import org.pdfbox.cos.COSName;
46 import org.pdfbox.cos.COSNumber;
47 import org.pdfbox.cos.COSObject;
48 import org.pdfbox.cos.COSStream;
49 import org.pdfbox.exceptions.COSVisitorException;
50 import org.pdfbox.pdmodel.PDDocument;
51 import org.pdfbox.pdmodel.PDDocumentCatalog;
52 import org.pdfbox.pdmodel.PDDocumentInformation;
53 import org.pdfbox.pdmodel.PDDocumentNameDictionary;
54 import org.pdfbox.pdmodel.PDPage;
55 import org.pdfbox.pdmodel.common.COSArrayList;
56 import org.pdfbox.pdmodel.common.COSObjectable;
57 import org.pdfbox.pdmodel.common.PDStream;
58 import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
59 import org.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
60 import org.pdfbox.pdmodel.interactive.form.PDAcroForm;
61 import org.pdfbox.pdmodel.interactive.form.PDField;
62 import org.pdfbox.pdmodel.interactive.form.PDFieldFactory;
63
64 /**
65  * This class will take a list of pdf documents and merge them, saving the result
66  * in a new document.
67  *
68  * @author <a HREF="mailto:ben@benlitchfield.com">Ben Litchfield</a>
69  * @version $Revision: 1.2 $
70  */

71 public class PDFMergerUtility
72 {
73
74     private List JavaDoc sources;
75     private String JavaDoc destinationFileName;
76
77     /**
78      * Instantiate a new PDFMergerUtility.
79      */

80     public PDFMergerUtility()
81     {
82         sources = new ArrayList JavaDoc();
83     }
84
85     /**
86      * Get the name of the destination file.
87      * @return Returns the destination.
88      */

89     public String JavaDoc getDestinationFileName()
90     {
91         return destinationFileName;
92     }
93
94     /**
95      * Set the name of the destination file.
96      * @param destination
97      * The destination to set.
98      */

99     public void setDestinationFileName(String JavaDoc destination)
100     {
101         this.destinationFileName = destination;
102     }
103
104     /**
105      * Add a source file to the list of files to merge.
106      *
107      * @param source Full path and file name of source document.
108      */

109     public void addSource(String JavaDoc source)
110     {
111         sources.add(new File JavaDoc(source));
112     }
113
114     /**
115      * Add a source file to the list of files to mere.
116      *
117      * @param source File representing source document
118      */

119     public void addSource(File JavaDoc source)
120     {
121         sources.add(source);
122     }
123
124     /**
125      * Merge the list of source documents, saving the result in the destination file.
126      *
127      * @throws IOException If there is an error saving the document.
128      * @throws COSVisitorException If an error occurs while saving the destination file.
129      */

130     public void mergeDocuments() throws IOException JavaDoc, COSVisitorException
131     {
132         PDDocument destination = null;
133         File JavaDoc sourceFile;
134         PDDocument source;
135         if (sources != null && sources.size() > 0)
136         {
137             try
138             {
139                 Iterator JavaDoc sit = sources.iterator();
140                 sourceFile = (File JavaDoc) sit.next();
141                 destination = PDDocument.load(sourceFile);
142                 while (sit.hasNext())
143                 {
144                     sourceFile = (File JavaDoc) sit.next();
145                     source = PDDocument.load(sourceFile);
146                     try
147                     {
148                         appendDocument(destination, source);
149                     }
150                     finally
151                     {
152                         if (source != null)
153                         {
154                             source.close();
155                         }
156                     }
157                 }
158                 destination.save(destinationFileName);
159             }
160             finally
161             {
162                 if (destination != null)
163                 {
164                     destination.close();
165                 }
166             }
167         }
168     }
169
170
171     /**
172      * append all pages from source to destination.
173      *
174      * @param destination the document to receive the pages
175      * @param source the document originating the new pages
176      *
177      * @throws IOException If there is an error accessing data from either document.
178      */

179     public void appendDocument(PDDocument destination, PDDocument source) throws IOException JavaDoc
180     {
181         if( destination.isEncrypted() )
182         {
183             throw new IOException JavaDoc( "Error: destination PDF is encrypted, can't append encrypted PDF documents." );
184         }
185         if( source.isEncrypted() )
186         {
187             throw new IOException JavaDoc( "Error: source PDF is encrypted, can't append encrypted PDF documents." );
188         }
189         PDDocumentInformation destInfo = destination.getDocumentInformation();
190         PDDocumentInformation srcInfo = source.getDocumentInformation();
191         destInfo.getDictionary().mergeInto( srcInfo.getDictionary() );
192         
193         PDDocumentCatalog destCatalog = destination.getDocumentCatalog();
194         PDDocumentCatalog srcCatalog = source.getDocumentCatalog();
195
196         if( destCatalog.getOpenAction() == null )
197         {
198             destCatalog.setOpenAction( srcCatalog.getOpenAction() );
199         }
200
201         PDAcroForm destAcroForm = destCatalog.getAcroForm();
202         PDAcroForm srcAcroForm = srcCatalog.getAcroForm();
203         if( destAcroForm == null )
204         {
205             cloneForNewDocument( destination, srcAcroForm );
206             destCatalog.setAcroForm( srcAcroForm );
207         }
208         else
209         {
210             mergeAcroForm(destination, destAcroForm, srcAcroForm);
211         }
212
213         COSArray destThreads = (COSArray)destCatalog.getCOSDictionary().getDictionaryObject(
214                 COSName.getPDFName( "Threads" ));
215         COSArray srcThreads = (COSArray)cloneForNewDocument(
216                 destination,
217                 destCatalog.getCOSDictionary().getDictionaryObject( COSName.getPDFName( "Threads" )));
218         if( destThreads == null )
219         {
220             destCatalog.getCOSDictionary().setItem( COSName.getPDFName( "Threads" ), srcThreads );
221         }
222         else
223         {
224             destThreads.addAll( srcThreads );
225         }
226
227         COSName names = COSName.getPDFName( "Names" );
228         PDDocumentNameDictionary destNames = destCatalog.getNames();
229         PDDocumentNameDictionary srcNames = srcCatalog.getNames();
230         if( srcNames != null )
231         {
232             if( destNames == null )
233             {
234                 destCatalog.getCOSDictionary().setItem( names, cloneForNewDocument( destination, srcNames ) );
235             }
236             else
237             {
238                 //warning, potential for collision here!!
239
destNames.getCOSDictionary().mergeInto( (COSDictionary)cloneForNewDocument( destination, srcNames ) );
240             }
241         }
242         
243         PDDocumentOutline destOutline = destCatalog.getDocumentOutline();
244         PDDocumentOutline srcOutline = srcCatalog.getDocumentOutline();
245         if( srcOutline != null )
246         {
247             if( destOutline == null )
248             {
249                 PDDocumentOutline cloned =
250                     new PDDocumentOutline( (COSDictionary)cloneForNewDocument( destination, srcOutline ) );
251                 destCatalog.setDocumentOutline( cloned );
252             }
253             else
254             {
255                 PDOutlineItem first = srcOutline.getFirstChild();
256                 PDOutlineItem clonedFirst = new PDOutlineItem( (COSDictionary)cloneForNewDocument(
257                         destination, first ));
258                 destOutline.appendChild( clonedFirst );
259             }
260         }
261         
262         String JavaDoc destPageMode = destCatalog.getPageMode();
263         String JavaDoc srcPageMode = srcCatalog.getPageMode();
264         if( destPageMode == null )
265         {
266             destCatalog.setPageMode( srcPageMode );
267         }
268
269         COSName pageLabels = COSName.getPDFName( "PageLabels" );
270         COSDictionary destLabels = (COSDictionary)destCatalog.getCOSDictionary().getDictionaryObject( pageLabels );
271         COSDictionary srcLabels = (COSDictionary)srcCatalog.getCOSDictionary().getDictionaryObject( pageLabels );
272         if( srcLabels != null )
273         {
274             int destPageCount = destination.getNumberOfPages();
275             COSArray destNums = null;
276             if( destLabels == null )
277             {
278                 destLabels = new COSDictionary();
279                 destNums = new COSArray();
280                 destLabels.setItem( COSName.getPDFName( "Nums" ), destNums );
281                 destCatalog.getCOSDictionary().setItem( pageLabels, destLabels );
282             }
283             else
284             {
285                 destNums = (COSArray)destLabels.getDictionaryObject( COSName.getPDFName( "Nums" ) );
286             }
287             COSArray srcNums = (COSArray)srcLabels.getDictionaryObject( COSName.getPDFName( "Nums" ) );
288             for( int i=0; i<srcNums.size(); i+=2 )
289             {
290                 COSNumber labelIndex = (COSNumber)srcNums.getObject( i );
291                 long labelIndexValue = labelIndex.intValue();
292                 destNums.add( new COSInteger( labelIndexValue + destPageCount ) );
293                 destNums.add( cloneForNewDocument( destination, srcNums.getObject( i+1 ) ) );
294             }
295         }
296         
297         COSName metadata = COSName.getPDFName( "Metadata" );
298         COSStream destMetadata = (COSStream)destCatalog.getCOSDictionary().getDictionaryObject( metadata );
299         COSStream srcMetadata = (COSStream)srcCatalog.getCOSDictionary().getDictionaryObject( metadata );
300         if( destMetadata == null && srcMetadata != null )
301         {
302             PDStream newStream = new PDStream( destination, srcMetadata.getUnfilteredStream(), false );
303             newStream.getStream().mergeInto( srcMetadata );
304             newStream.addCompression();
305             destCatalog.getCOSDictionary().setItem( metadata, newStream );
306         }
307
308         //finally append the pages
309
List JavaDoc pages = source.getDocumentCatalog().getAllPages();
310         Iterator JavaDoc pageIter = pages.iterator();
311         while( pageIter.hasNext() )
312         {
313             PDPage page = (PDPage)pageIter.next();
314             PDPage newPage =
315                 new PDPage( (COSDictionary)cloneForNewDocument( destination, page.getCOSDictionary() ) );
316             destination.addPage( newPage );
317         }
318     }
319     Map JavaDoc clonedVersion = new HashMap JavaDoc();
320     
321     private COSBase cloneForNewDocument( PDDocument destination, Object JavaDoc base ) throws IOException JavaDoc
322     {
323         if( base == null )
324         {
325             return null;
326         }
327         COSBase retval = (COSBase)clonedVersion.get( base );
328         if( retval != null )
329         {
330             //we are done, it has already been converted.
331
}
332         else if( base instanceof List JavaDoc )
333         {
334             COSArray array = new COSArray();
335             List JavaDoc list = (List JavaDoc)base;
336             for( int i=0; i<list.size(); i++ )
337             {
338                 array.add( cloneForNewDocument( destination, list.get( i ) ) );
339             }
340             retval = array;
341         }
342         else if( base instanceof COSObjectable && !(base instanceof COSBase) )
343         {
344             retval = cloneForNewDocument( destination, ((COSObjectable)base).getCOSObject() );
345             clonedVersion.put( base, retval );
346         }
347         else if( base instanceof COSObject )
348         {
349             COSObject object = (COSObject)base;
350             retval = cloneForNewDocument( destination, object.getObject() );
351             clonedVersion.put( base, retval );
352         }
353         else if( base instanceof COSArray )
354         {
355             COSArray newArray = new COSArray();
356             COSArray array = (COSArray)base;
357             for( int i=0; i<array.size(); i++ )
358             {
359                 newArray.add( cloneForNewDocument( destination, array.get( i ) ) );
360             }
361             retval = newArray;
362             clonedVersion.put( base, retval );
363         }
364         else if( base instanceof COSStream )
365         {
366             COSStream originalStream = (COSStream)base;
367             List JavaDoc keys = originalStream.keyList();
368             PDStream stream = new PDStream( destination, originalStream.getFilteredStream(), true );
369             clonedVersion.put( base, stream.getStream() );
370             for( int i=0; i<keys.size(); i++ )
371             {
372                 COSName key = (COSName)keys.get( i );
373                 stream.getStream().setItem( key, cloneForNewDocument(destination,originalStream.getItem(key)));
374             }
375             retval = stream.getStream();
376         }
377         else if( base instanceof COSDictionary )
378         {
379             COSDictionary dic = (COSDictionary)base;
380             List JavaDoc keys = dic.keyList();
381             retval = new COSDictionary();
382             clonedVersion.put( base, retval );
383             for( int i=0; i<keys.size(); i++ )
384             {
385                 COSName key = (COSName)keys.get( i );
386                 ((COSDictionary)retval).setItem( key, cloneForNewDocument(destination,dic.getItem(key)));
387             }
388         }
389         else
390         {
391             retval = (COSBase)base;
392         }
393         clonedVersion.put( base, retval );
394         return retval;
395     }
396
397     private int nextFieldNum = 1;
398     
399     /**
400      * Merge the contents of the source form into the destination form
401      * for the destination file.
402      *
403      * @param destination the destination document
404      * @param destAcroForm the destination form
405      * @param srcAcroForm the source form
406      * @throws IOException If an error occurs while adding the field.
407      */

408     private void mergeAcroForm(PDDocument destination, PDAcroForm destAcroForm, PDAcroForm srcAcroForm)
409         throws IOException JavaDoc
410     {
411         List JavaDoc destFields = destAcroForm.getFields();
412         List JavaDoc srcFields = srcAcroForm.getFields();
413         if( srcFields != null )
414         {
415             if( destFields == null )
416             {
417                 destFields = new COSArrayList();
418                 destAcroForm.setFields( destFields );
419             }
420             Iterator JavaDoc srcFieldsIterator = srcFields.iterator();
421             while (srcFieldsIterator.hasNext())
422             {
423                 PDField srcField = (PDField)srcFieldsIterator.next();
424                 PDField destField =
425                     PDFieldFactory.createField(
426                         destAcroForm,
427                         (COSDictionary)cloneForNewDocument(destination, srcField.getDictionary() ));
428                 // if the form already has a field with this name then we need to rename this field
429
// to prevent merge conflicts.
430
if ( destAcroForm.getField(destField.getFullyQualifiedName()) != null )
431                 {
432                     destField.setPartialName("dummyFieldName"+(nextFieldNum++));
433                 }
434                 destFields.add(destField);
435             }
436         }
437     }
438     
439 }
440
Popular Tags