KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > Overlay


1 /**
2  * Copyright (c) 2003, 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;
32
33 import org.pdfbox.cos.COSArray;
34 import org.pdfbox.cos.COSBase;
35 import org.pdfbox.cos.COSDictionary;
36 import org.pdfbox.cos.COSName;
37 import org.pdfbox.cos.COSStream;
38 import org.pdfbox.cos.COSInteger;
39 import org.pdfbox.exceptions.COSVisitorException;
40 import org.pdfbox.pdfparser.PDFParser;
41 import org.pdfbox.pdfwriter.COSWriter;
42
43 import org.pdfbox.pdmodel.PDDocument;
44 import org.pdfbox.pdmodel.PDDocumentCatalog;
45 import org.pdfbox.pdmodel.PDPage;
46 import org.pdfbox.pdmodel.PDResources;
47
48 import java.io.FileInputStream JavaDoc;
49 import java.io.FileOutputStream JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.io.ByteArrayOutputStream JavaDoc;
53 import java.io.OutputStream JavaDoc;
54
55 import java.util.ArrayList JavaDoc;
56 import java.util.Iterator JavaDoc;
57 import java.util.List JavaDoc;
58 import java.util.TreeMap JavaDoc;
59 import java.util.Map JavaDoc;
60
61 /**
62  * Overlay on document with another one.<br>
63  * e.g. Overlay an invoice with your company layout<br>
64  * <br>
65  * How it (should) work:<br>
66  * If the document has 10 pages, and the layout 2 the following is the result:<br>
67  * <pre>
68  * Document: 1234567890
69  * Layout : 1212121212
70  * </pre>
71  * <br>
72  *
73  * @author Mario Ivankovits (mario@ops.co.at)
74  * @author <a HREF="ben@benlitchfield.com">Ben Litchfield</a>
75  *
76  * @version $Revision: 1.6 $
77  */

78 public class Overlay
79 {
80     /**
81      * COSName constant.
82      */

83     public static final COSName XOBJECT = COSName.getPDFName("XObject");
84     /**
85      * COSName constant.
86      */

87     public static final COSName PROC_SET = COSName.getPDFName("ProcSet");
88     /**
89      * COSName constant.
90      */

91     public static final COSName EXT_G_STATE = COSName.getPDFName("ExtGState" );
92
93     private List JavaDoc layoutPages = new ArrayList JavaDoc(10);
94
95     private PDDocument pdfOverlay;
96     private PDDocument pdfDocument;
97     private int pageCount = 0;
98     private COSStream saveGraphicsStateStream;
99     private COSStream restoreGraphicsStateStream;
100
101     /**
102      * This will overlay a document and write out the results.<br/><br/>
103      *
104      * usage: java org.pdfbox.Overlay &lt;overlay.pdf&gt; &lt;document.pdf&gt; &lt;result.pdf&gt;
105      *
106      * @param args The command line arguments.
107      *
108      * @throws IOException If there is an error reading/writing the document.
109      * @throws COSVisitorException If there is an error writing the document.
110      */

111     public static void main( String JavaDoc[] args ) throws IOException JavaDoc, COSVisitorException
112     {
113         if( args.length != 3 )
114         {
115             usage();
116             System.exit(1);
117         }
118         else
119         {
120             PDDocument overlay = null;
121             PDDocument pdf = null;
122
123             try
124             {
125                 overlay = getDocument( args[0] );
126                 pdf = getDocument( args[1] );
127                 Overlay overlayer = new Overlay();
128                 overlayer.overlay( overlay, pdf );
129                 writeDocument( pdf, args[2] );
130             }
131             finally
132             {
133                 if( overlay != null )
134                 {
135                     overlay.close();
136                 }
137                 if( pdf != null )
138                 {
139                     pdf.close();
140                 }
141             }
142         }
143     }
144
145     private static void writeDocument( PDDocument pdf, String JavaDoc filename ) throws IOException JavaDoc, COSVisitorException
146     {
147         FileOutputStream JavaDoc output = null;
148         COSWriter writer = null;
149         try
150         {
151             output = new FileOutputStream JavaDoc( filename );
152             writer = new COSWriter( output );
153             writer.write( pdf );
154         }
155         finally
156         {
157             if( writer != null )
158             {
159                 writer.close();
160             }
161             if( output != null )
162             {
163                 output.close();
164             }
165         }
166     }
167
168     private static PDDocument getDocument( String JavaDoc filename ) throws IOException JavaDoc
169     {
170         FileInputStream JavaDoc input = null;
171         PDFParser parser = null;
172         PDDocument result = null;
173         try
174         {
175             input = new FileInputStream JavaDoc( filename );
176             parser = new PDFParser( input );
177             parser.parse();
178             result = parser.getPDDocument();
179         }
180         finally
181         {
182             if( input != null )
183             {
184                 input.close();
185             }
186         }
187         return result;
188     }
189
190     private static void usage()
191     {
192         System.err.println( "usage: java " + Overlay.class.getName() + "<overlay.pdf> <document.pdf> <result.pdf>" );
193     }
194
195     /**
196      * Private class.
197      */

198     private static class LayoutPage
199     {
200         private final COSBase contents;
201         private final COSDictionary res;
202         private final Map JavaDoc objectNameMap;
203
204         /**
205          * Constructor.
206          *
207          * @param contentsValue The contents.
208          * @param resValue The resource dictionary
209          * @param objectNameMapValue The map
210          */

211         public LayoutPage(COSBase contentsValue, COSDictionary resValue, Map JavaDoc objectNameMapValue)
212         {
213             contents = contentsValue;
214             res = resValue;
215             objectNameMap = objectNameMapValue;
216         }
217     }
218
219     /**
220      * This will overlay two documents onto each other. The overlay document is
221      * repeatedly overlayed onto the destination document for every page in the
222      * destination.
223      *
224      * @param overlay The document to copy onto the destination
225      * @param destination The file that the overlay should be placed on.
226      *
227      * @return The destination pdf, same as argument passed in.
228      *
229      * @throws IOException If there is an error accessing data.
230      */

231     public PDDocument overlay( PDDocument overlay, PDDocument destination ) throws IOException JavaDoc
232     {
233         pdfOverlay = overlay;
234         pdfDocument = destination;
235
236         PDDocumentCatalog overlayCatalog = pdfOverlay.getDocumentCatalog();
237         collectLayoutPages( overlayCatalog.getAllPages() );
238
239         COSDictionary saveGraphicsStateDic = new COSDictionary();
240         saveGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() );
241         OutputStream JavaDoc saveStream = saveGraphicsStateStream.createUnfilteredStream();
242         saveStream.write( " q\n".getBytes() );
243         saveStream.flush();
244
245         restoreGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() );
246         OutputStream JavaDoc restoreStream = restoreGraphicsStateStream.createUnfilteredStream();
247         restoreStream.write( " Q\n".getBytes() );
248         restoreStream.flush();
249
250
251         PDDocumentCatalog pdfCatalog = pdfDocument.getDocumentCatalog();
252         processPages( pdfCatalog.getAllPages() );
253
254         return pdfDocument;
255     }
256
257     private void collectLayoutPages( List JavaDoc pages) throws IOException JavaDoc
258     {
259         Iterator JavaDoc pagesIter = pages.iterator();
260         while( pagesIter.hasNext() )
261         {
262             PDPage page = (PDPage)pagesIter.next();
263             COSBase contents = page.getCOSDictionary().getDictionaryObject( COSName.CONTENTS );
264             PDResources resources = page.findResources();
265             if( resources == null )
266             {
267                 resources = new PDResources();
268                 page.setResources( resources );
269             }
270             COSDictionary res = resources.getCOSDictionary();
271
272             if( contents instanceof COSStream )
273             {
274                 COSStream stream = (COSStream) contents;
275                 Map JavaDoc objectNameMap = new TreeMap JavaDoc();
276                 stream = makeUniqObjectNames(objectNameMap, stream);
277
278                 layoutPages.add(new LayoutPage(stream, res, objectNameMap));
279             }
280             else if( contents instanceof COSArray )
281             {
282                 throw new UnsupportedOperationException JavaDoc("Layout pages with COSArray currently not supported.");
283                 // layoutPages.add(new LayoutPage(contents, res));
284
}
285             else
286             {
287                 throw new IOException JavaDoc( "Contents are unknown type:" + contents.getClass().getName() );
288             }
289         }
290     }
291
292     private COSStream makeUniqObjectNames(Map JavaDoc objectNameMap, COSStream stream) throws IOException JavaDoc
293     {
294         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc(10240);
295
296         byte[] buf = new byte[10240];
297         int read;
298         InputStream JavaDoc is = stream.getUnfilteredStream();
299         while ((read = is.read(buf)) > -1)
300         {
301             baos.write(buf, 0, read);
302         }
303
304         buf = baos.toByteArray();
305         baos = new ByteArrayOutputStream JavaDoc(buf.length + 100);
306         StringBuffer JavaDoc sbObjectName = new StringBuffer JavaDoc(10);
307         boolean bInObjectIdent = false;
308         boolean bInText = false;
309         boolean bInEscape = false;
310         for (int i = 0; i<buf.length; i++)
311         {
312             byte b = buf[i];
313
314             if (!bInEscape)
315             {
316                 if (!bInText && b == '(')
317                 {
318                     bInText = true;
319                 }
320                 if (bInText && b == ')')
321                 {
322                     bInText = false;
323                 }
324                 if (b == '\\')
325                 {
326                     bInEscape = true;
327                 }
328
329                 if (!bInText && !bInEscape)
330                 {
331                     if (b == '/')
332                     {
333                         bInObjectIdent = true;
334                     }
335                     else if (bInObjectIdent && Character.isWhitespace((char) b))
336                     {
337                         bInObjectIdent = false;
338
339                         // System.err.println(sbObjectName);
340
// String object = sbObjectName.toString();
341

342                         String JavaDoc objectName = sbObjectName.toString().substring(1);
343                         String JavaDoc newObjectName = objectName + "overlay";
344                         baos.write('/');
345                         baos.write(newObjectName.getBytes());
346
347                         objectNameMap.put(objectName, COSName.getPDFName(newObjectName));
348
349                         sbObjectName.delete(0, sbObjectName.length());
350                     }
351                 }
352
353                 if (bInObjectIdent)
354                 {
355                     sbObjectName.append((char) b);
356                     continue;
357                 }
358             }
359             else
360             {
361                 bInEscape = false;
362             }
363
364             baos.write(b);
365         }
366
367         COSDictionary streamDict = new COSDictionary();
368         streamDict.setItem(COSName.LENGTH, new COSInteger(baos.size()));
369         COSStream output = new COSStream(streamDict, pdfDocument.getDocument().getScratchFile());
370         output.setFilters(stream.getFilters());
371         OutputStream JavaDoc os = output.createUnfilteredStream();
372         baos.writeTo(os);
373         os.close();
374
375         return output;
376     }
377
378     private void processPages( List JavaDoc pages ) throws IOException JavaDoc
379     {
380         Iterator JavaDoc pageIter = pages.iterator();
381         while( pageIter.hasNext() )
382         {
383             PDPage page = (PDPage)pageIter.next();
384             COSDictionary pageDictionary = page.getCOSDictionary();
385             COSBase contents = pageDictionary.getDictionaryObject( COSName.CONTENTS );
386             if( contents instanceof COSStream )
387             {
388                 COSStream contentsStream = (COSStream)contents;
389                 // System.err.println("stream");
390
pageCount++;
391
392                 COSArray array = new COSArray();
393
394                 array.add(contentsStream);
395
396                 mergePage( array, page );
397
398                 pageDictionary.setItem(COSName.CONTENTS, array);
399             }
400             else if( contents instanceof COSArray )
401             {
402                 COSArray contentsArray = (COSArray)contents;
403
404                 mergePage( contentsArray, page );
405             }
406             else
407             {
408                 throw new IOException JavaDoc( "Contents are unknown type:" + contents.getClass().getName() );
409             }
410         }
411     }
412
413     private void mergePage(COSArray array, PDPage page )
414     {
415         int layoutPageNum = pageCount % layoutPages.size();
416         LayoutPage layoutPage = (LayoutPage) layoutPages.get(layoutPageNum);
417         PDResources resources = page.findResources();
418         if( resources == null )
419         {
420             resources = new PDResources();
421             page.setResources( resources );
422         }
423         COSDictionary docResDict = resources.getCOSDictionary();
424         COSDictionary layoutResDict = layoutPage.res;
425         mergeArray(PROC_SET, docResDict, layoutResDict);
426         mergeDictionary(COSName.FONT, docResDict, layoutResDict, layoutPage.objectNameMap);
427         mergeDictionary(XOBJECT, docResDict, layoutResDict, layoutPage.objectNameMap);
428         mergeDictionary(EXT_G_STATE, docResDict, layoutResDict, layoutPage.objectNameMap);
429
430         //we are going to wrap the existing content around some save/restore
431
//graphics state, so the result is
432
//
433
//<save graphics state>
434
//<all existing content streams>
435
//<restore graphics state>
436
//<overlay content>
437
array.add(0, saveGraphicsStateStream );
438         array.add( restoreGraphicsStateStream );
439         array.add(layoutPage.contents);
440     }
441
442     /**
443      * merges two dictionaries.
444      *
445      * @param dest
446      * @param source
447      */

448     private void mergeDictionary(COSName name, COSDictionary dest, COSDictionary source, Map JavaDoc objectNameMap)
449     {
450         COSDictionary destDict = (COSDictionary) dest.getDictionaryObject(name);
451         COSDictionary sourceDict = (COSDictionary) source.getDictionaryObject(name);
452
453         if (destDict == null)
454         {
455             destDict = new COSDictionary();
456             dest.setItem(name, destDict);
457         }
458         if( sourceDict != null )
459         {
460
461             Iterator JavaDoc iterKeys = sourceDict.keyList().iterator();
462             while (iterKeys.hasNext())
463             {
464                 COSName key = (COSName) iterKeys.next();
465                 COSName mappedKey = (COSName) objectNameMap.get(key.getName());
466                 if (mappedKey == null)
467                 {
468                     // object not needet
469
continue;
470                 }
471
472                 destDict.setItem(mappedKey, sourceDict.getItem(key));
473             }
474         }
475     }
476
477     /**
478      * merges two arrays.
479      *
480      * @param dest
481      * @param source
482      */

483     private void mergeArray(COSName name, COSDictionary dest, COSDictionary source)
484     {
485         COSArray destDict = (COSArray) dest.getDictionaryObject(name);
486         COSArray sourceDict = (COSArray) source.getDictionaryObject(name);
487
488         if (destDict == null)
489         {
490             destDict = new COSArray();
491             dest.setItem(name, destDict);
492         }
493
494         for (int sourceDictIdx = 0; sourceDict != null && sourceDictIdx<sourceDict.size(); sourceDictIdx++)
495         {
496             COSBase key = sourceDict.get(sourceDictIdx);
497             if (key instanceof COSName)
498             {
499                 COSName keyname = (COSName) key;
500
501                 boolean bFound = false;
502                 for (int destDictIdx = 0; destDictIdx<destDict.size(); destDictIdx++)
503                 {
504                     COSBase destkey = destDict.get(destDictIdx);
505                     if (destkey instanceof COSName)
506                     {
507                         COSName destkeyname = (COSName) destkey;
508                         if (destkeyname.equals(keyname))
509                         {
510                             bFound = true;
511                             break;
512                         }
513                     }
514                 }
515                 if (!bFound)
516                 {
517                     destDict.add(keyname);
518                 }
519             }
520         }
521     }
522 }
Popular Tags