/** * Copyright (c) 2003, www.pdfbox.org * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of pdfbox; nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * http://www.pdfbox.org * */ package org.pdfbox; import org.apache.log4j.Logger; import org.pdfbox.cos.COSArray; import org.pdfbox.cos.COSBase; import org.pdfbox.cos.COSDictionary; import org.pdfbox.cos.COSName; import org.pdfbox.cos.COSStream; import org.pdfbox.cos.COSInteger; import org.pdfbox.exceptions.COSVisitorException; import org.pdfbox.pdfparser.PDFParser; import org.pdfbox.pdfwriter.COSWriter; import org.pdfbox.pdmodel.PDDocument; import org.pdfbox.pdmodel.PDDocumentCatalog; import org.pdfbox.pdmodel.PDPage; import org.pdfbox.pdmodel.PDResources; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.TreeMap; import java.util.Map; /** * Overlay on document with another one.
* e.g. Overlay an invoice with your company layout
*
* How it (should) work:
* If the document has 10 pages, and the layout 2 the following is the result:
*
 * Document: 1234567890
 * Layout  : 1212121212
 * 
*
* * @author Mario Ivankovits (mario@ops.co.at) * @author Ben Litchfield (ben@csh.rit.edu) * * @version $Revision: 1.4 $ */ public class Overlay { /** * COSName constant. */ public static final COSName XOBJECT = COSName.getPDFName("XObject"); /** * COSName constant. */ public static final COSName PROC_SET = COSName.getPDFName("ProcSet"); /** * COSName constant. */ public static final COSName EXT_G_STATE = COSName.getPDFName("ExtGState" ); private static Logger log = Logger.getLogger(Overlay.class); private List layoutPages = new ArrayList(10); private PDDocument pdfOverlay; private PDDocument pdfDocument; private int pageCount = 0; private COSStream saveGraphicsStateStream; private COSStream restoreGraphicsStateStream; /** * This will overlay a document and write out the results.

* * usage: java org.pdfbox.Overlay <overlay.pdf> <document.pdf> <result.pdf> * * @param args The command line arguments. * * @throws IOException If there is an error reading/writing the document. * @throws COSVisitorException If there is an error writing the document. */ public static void main( String[] args ) throws IOException, COSVisitorException { if( args.length != 3 ) { usage(); System.exit(1); } else { PDDocument overlay = null; PDDocument pdf = null; try { overlay = getDocument( args[0] ); pdf = getDocument( args[1] ); Overlay overlayer = new Overlay(); overlayer.overlay( overlay, pdf ); writeDocument( pdf, args[2] ); } finally { if( overlay != null ) { overlay.close(); } if( pdf != null ) { pdf.close(); } } } } private static void writeDocument( PDDocument pdf, String filename ) throws IOException, COSVisitorException { FileOutputStream output = null; COSWriter writer = null; try { output = new FileOutputStream( filename ); writer = new COSWriter( output ); writer.write( pdf ); } finally { if( writer != null ) { writer.close(); } if( output != null ) { output.close(); } } } private static PDDocument getDocument( String filename ) throws IOException { FileInputStream input = null; PDFParser parser = null; PDDocument result = null; try { input = new FileInputStream( filename ); parser = new PDFParser( input ); parser.parse(); result = parser.getPDDocument(); } finally { if( input != null ) { input.close(); } } return result; } private static void usage() { System.err.println( "usage: java " + Overlay.class.getName() + " " ); } /** * Private class. */ private static class LayoutPage { private final COSBase contents; private final COSDictionary res; private final Map objectNameMap; /** * Constructor. * * @param contentsValue The contents. * @param resValue The resource dictionary * @param objectNameMapValue The map */ public LayoutPage(COSBase contentsValue, COSDictionary resValue, Map objectNameMapValue) { contents = contentsValue; res = resValue; objectNameMap = objectNameMapValue; } } /** * This will overlay two documents onto each other. The overlay document is * repeatedly overlayed onto the destination document for every page in the * destination. * * @param overlay The document to copy onto the destination * @param destination The file that the overlay should be placed on. * * @return The destination pdf, same as argument passed in. * * @throws IOException If there is an error accessing data. */ public PDDocument overlay( PDDocument overlay, PDDocument destination ) throws IOException { pdfOverlay = overlay; pdfDocument = destination; PDDocumentCatalog overlayCatalog = pdfOverlay.getDocumentCatalog(); collectLayoutPages( overlayCatalog.getAllPages() ); COSDictionary saveGraphicsStateDic = new COSDictionary(); saveGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() ); OutputStream saveStream = saveGraphicsStateStream.createUnfilteredStream(); saveStream.write( " q\n".getBytes() ); saveStream.flush(); restoreGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() ); OutputStream restoreStream = restoreGraphicsStateStream.createUnfilteredStream(); restoreStream.write( " Q\n".getBytes() ); restoreStream.flush(); PDDocumentCatalog pdfCatalog = pdfDocument.getDocumentCatalog(); processPages( pdfCatalog.getAllPages() ); return pdfDocument; } private void collectLayoutPages( List pages) throws IOException { Iterator pagesIter = pages.iterator(); while( pagesIter.hasNext() ) { PDPage page = (PDPage)pagesIter.next(); COSBase contents = page.getCOSDictionary().getDictionaryObject( COSName.CONTENTS ); PDResources resources = page.findResources(); if( resources == null ) { resources = new PDResources(); page.setResources( resources ); } COSDictionary res = resources.getCOSDictionary(); if( contents instanceof COSStream ) { COSStream stream = (COSStream) contents; Map objectNameMap = new TreeMap(); stream = makeUniqObjectNames(objectNameMap, stream); layoutPages.add(new LayoutPage(stream, res, objectNameMap)); } else if( contents instanceof COSArray ) { throw new UnsupportedOperationException("Layout pages with COSArray currently not supported."); // layoutPages.add(new LayoutPage(contents, res)); } else { throw new IOException( "Contents are unknown type:" + contents.getClass().getName() ); } } } private COSStream makeUniqObjectNames(Map objectNameMap, COSStream stream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(10240); byte[] buf = new byte[10240]; int read; InputStream is = stream.getUnfilteredStream(); while ((read = is.read(buf)) > -1) { baos.write(buf, 0, read); } buf = baos.toByteArray(); baos = new ByteArrayOutputStream(buf.length + 100); StringBuffer sbObjectName = new StringBuffer(10); boolean bInObjectIdent = false; boolean bInText = false; boolean bInEscape = false; for (int i = 0; i // // // array.add(0, saveGraphicsStateStream ); array.add( restoreGraphicsStateStream ); array.add(layoutPage.contents); } /** * merges two dictionaries. * * @param dest * @param source */ private void mergeDictionary(COSName name, COSDictionary dest, COSDictionary source, Map objectNameMap) { COSDictionary destDict = (COSDictionary) dest.getDictionaryObject(name); COSDictionary sourceDict = (COSDictionary) source.getDictionaryObject(name); if (destDict == null) { destDict = new COSDictionary(); dest.setItem(name, destDict); } if( sourceDict != null ) { Iterator iterKeys = sourceDict.keyList().iterator(); while (iterKeys.hasNext()) { COSName key = (COSName) iterKeys.next(); COSName mappedKey = (COSName) objectNameMap.get(key.getName()); if (mappedKey == null) { // object not needet if (log.isDebugEnabled()) { log.debug("MergeDict " + name.getName() + ": Object " + key.getName() + " not used by content. Discarded."); } continue; } destDict.setItem(mappedKey, sourceDict.getItem(key)); } } } /** * merges two arrays. * * @param dest * @param source */ private void mergeArray(COSName name, COSDictionary dest, COSDictionary source) { COSArray destDict = (COSArray) dest.getDictionaryObject(name); COSArray sourceDict = (COSArray) source.getDictionaryObject(name); if (destDict == null) { destDict = new COSArray(); dest.setItem(name, destDict); } for (int sourceDictIdx = 0; sourceDict != null && sourceDictIdx