You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2014/06/12 20:13:32 UTC

svn commit: r1602255 - in /pdfbox/trunk: pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java

Author: lehmi
Date: Thu Jun 12 18:13:31 2014
New Revision: 1602255

URL: http://svn.apache.org/r1602255
Log:
PDFBOX-1596: moved overlay implementation into its own class as proposed by James Green

Added:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java   (with props)
Modified:
    pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java?rev=1602255&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java Thu Jun 12 18:13:31 2014
@@ -0,0 +1,547 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.io.RandomAccessBuffer;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+
+/**
+ * Adds an overlay to an existing PDF document.
+ * 
+ * Based on code contributed by Balazs Jerk.
+ * 
+ */
+public class Overlay
+{
+    /**
+     * Possible loacation of the overlayed pages: foreground or background.
+     */
+    public enum Position
+    {
+        FOREGROUND, BACKGROUND
+    };
+
+    private static final String XOBJECT_PREFIX = "OL";
+
+    private LayoutPage defaultOverlayPage;
+    private LayoutPage firstPageOverlayPage;
+    private LayoutPage lastPageOverlayPage;
+    private LayoutPage oddPageOverlayPage;
+    private LayoutPage evenPageOverlayPage;
+
+    private Map<Integer, PDDocument> specificPageOverlay = new HashMap<Integer, PDDocument>();
+    private Map<Integer, LayoutPage> specificPageOverlayPage = new HashMap<Integer, LayoutPage>();
+
+    private Position position = Position.BACKGROUND;
+
+    private String inputFileName = null;
+    private String outputFilename = null;
+    private String defaultOverlayFilename = null;
+    private String firstPageOverlayFilename = null;
+    private String lastPageOverlayFilename = null;
+    private String oddPageOverlayFilename = null;
+    private String evenPageOverlayFilename = null;
+
+    /**
+     * This will add overlays to a documents.
+     * 
+     * @param specificPageOverlayFile map of overlay files for specific pages
+     * @param useNonSeqParser indicates whether the nonsequential parser is used
+     * @throws IOException if something went wrong
+     */
+    public void overlay(Map<Integer, String> specificPageOverlayFile, boolean useNonSeqParser)
+            throws IOException
+    {
+        PDDocument sourcePDFDocument = null;
+        PDDocument defaultOverlay = null;
+        PDDocument firstPageOverlay = null;
+        PDDocument lastPageOverlay = null;
+        PDDocument oddPageOverlay = null;
+        PDDocument evenPageOverlay = null;
+        try
+        {
+            sourcePDFDocument = PDDocument.load(inputFileName);
+            if (defaultOverlayFilename != null)
+            {
+                defaultOverlay = loadPDF(defaultOverlayFilename, useNonSeqParser);
+                defaultOverlayPage = getLayoutPage(defaultOverlay);
+            }
+            if (firstPageOverlayFilename != null)
+            {
+                firstPageOverlay = loadPDF(firstPageOverlayFilename, useNonSeqParser);
+                firstPageOverlayPage = getLayoutPage(firstPageOverlay);
+            }
+            if (lastPageOverlayFilename != null)
+            {
+                lastPageOverlay = loadPDF(lastPageOverlayFilename, useNonSeqParser);
+                lastPageOverlayPage = getLayoutPage(lastPageOverlay);
+            }
+            if (oddPageOverlayFilename != null)
+            {
+                oddPageOverlay = loadPDF(oddPageOverlayFilename, useNonSeqParser);
+                oddPageOverlayPage = getLayoutPage(oddPageOverlay);
+            }
+            if (evenPageOverlayFilename != null)
+            {
+                if (useNonSeqParser)
+                {
+                    evenPageOverlay = PDDocument.loadNonSeq(new File(evenPageOverlayFilename), null);
+                }
+                else
+                {
+                    evenPageOverlay = PDDocument.load(evenPageOverlayFilename);
+                }
+                evenPageOverlay = loadPDF(evenPageOverlayFilename, useNonSeqParser);
+                evenPageOverlayPage = getLayoutPage(evenPageOverlay);
+            }
+            for (Map.Entry<Integer, String> e : specificPageOverlayFile.entrySet())
+            {
+                PDDocument doc = loadPDF(e.getValue(), useNonSeqParser);
+                specificPageOverlay.put(e.getKey(), doc);
+                specificPageOverlayPage.put(e.getKey(), getLayoutPage(doc));
+            }
+            PDDocumentCatalog pdfCatalog = sourcePDFDocument.getDocumentCatalog();
+            processPages(pdfCatalog.getAllPages());
+
+            sourcePDFDocument.save(outputFilename);
+        }
+        finally
+        {
+            if (sourcePDFDocument != null)
+            {
+                sourcePDFDocument.close();
+            }
+            if (defaultOverlay != null)
+            {
+                defaultOverlay.close();
+            }
+            if (firstPageOverlay != null)
+            {
+                firstPageOverlay.close();
+            }
+            if (lastPageOverlay != null)
+            {
+                lastPageOverlay.close();
+            }
+            if (oddPageOverlay != null)
+            {
+                oddPageOverlay.close();
+            }
+            if (evenPageOverlay != null)
+            {
+                evenPageOverlay.close();
+            }
+            for (Map.Entry<Integer, PDDocument> e : specificPageOverlay.entrySet())
+            {
+                e.getValue().close();
+            }
+            specificPageOverlay.clear();
+            specificPageOverlayPage.clear();
+        }
+    }
+
+    private PDDocument loadPDF(String pdfName, boolean useNonSeqParser) throws IOException
+    {
+        PDDocument pdf = null;
+        if (useNonSeqParser)
+        {
+            pdf = PDDocument.loadNonSeq(new File(pdfName), null);
+        }
+        else
+        {
+            pdf = PDDocument.load(pdfName);
+        }
+        return pdf;
+    }
+
+    /**
+     * Stores the overlay page information.
+     */
+    private static class LayoutPage
+    {
+        private final PDRectangle overlayMediaBox;
+        private final COSStream overlayContentStream;
+        private final COSDictionary overlayResources;
+
+        private LayoutPage(PDRectangle mediaBox, COSStream contentStream, COSDictionary resources)
+        {
+            overlayMediaBox = mediaBox;
+            overlayContentStream = contentStream;
+            overlayResources = resources;
+        }
+    }
+
+    private LayoutPage getLayoutPage(PDDocument doc) throws IOException
+    {
+        PDDocumentCatalog catalog = doc.getDocumentCatalog();
+        PDPage page = (PDPage) catalog.getAllPages().get(0);
+        COSBase contents = page.getCOSDictionary().getDictionaryObject(COSName.CONTENTS);
+        PDResources resources = page.findResources();
+        if (resources == null)
+        {
+            resources = new PDResources();
+        }
+        return new LayoutPage(page.getMediaBox(), createContentStream(contents),
+                resources.getCOSDictionary());
+    }
+
+    private COSStream createContentStream(COSBase contents) throws IOException
+    {
+        List<COSStream> contentStreams = createContentStreamList(contents);
+        // concatenate streams
+        COSStream concatStream = new COSStream(new RandomAccessBuffer());
+        OutputStream out = concatStream.createUnfilteredStream();
+        for (COSStream contentStream : contentStreams)
+        {
+            InputStream in = contentStream.getUnfilteredStream();
+            byte[] buf = new byte[2048];
+            int n;
+            while ((n = in.read(buf)) > 0)
+            {
+                out.write(buf, 0, n);
+            }
+            out.flush();
+        }
+        out.close();
+        concatStream.setFilters(COSName.FLATE_DECODE);
+        return concatStream;
+    }
+
+    private List<COSStream> createContentStreamList(COSBase contents) throws IOException
+    {
+        List<COSStream> contentStreams = new ArrayList<COSStream>();
+        if (contents instanceof COSStream)
+        {
+            contentStreams.add((COSStream) contents);
+        }
+        else if (contents instanceof COSArray)
+        {
+            for (COSBase item : (COSArray) contents)
+            {
+                contentStreams.addAll(createContentStreamList(item));
+            }
+        }
+        else if (contents instanceof COSObject)
+        {
+            contentStreams.addAll(createContentStreamList(((COSObject) contents).getObject()));
+        }
+        else
+        {
+            throw new IOException("Contents are unknown type:" + contents.getClass().getName());
+        }
+        return contentStreams;
+    }
+
+    private void processPages(List<PDPage> pages) throws IOException
+    {
+        int pageCount = 0;
+        for (PDPage page : pages)
+        {
+            COSDictionary pageDictionary = page.getCOSDictionary();
+            COSBase contents = pageDictionary.getDictionaryObject(COSName.CONTENTS);
+            COSArray contentArray = new COSArray();
+            switch (position)
+            {
+            case FOREGROUND:
+                // save state
+                contentArray.add(createStream("q\n"));
+                // original content
+                if (contents instanceof COSStream)
+                {
+                    contentArray.add(contents);
+                }
+                else if (contents instanceof COSArray)
+                {
+                    contentArray.addAll((COSArray) contents);
+                }
+                else
+                {
+                    throw new IOException("Unknown content type:" + contents.getClass().getName());
+                }
+                // restore state
+                contentArray.add(createStream("Q\n"));
+                // overlay content
+                overlayPage(contentArray, page, pageCount + 1, pages.size());
+                break;
+            case BACKGROUND:
+                // overlay content
+                overlayPage(contentArray, page, pageCount + 1, pages.size());
+                // original content
+                if (contents instanceof COSStream)
+                {
+                    contentArray.add(contents);
+                }
+                else if (contents instanceof COSArray)
+                {
+                    contentArray.addAll((COSArray) contents);
+                }
+                else
+                {
+                    throw new IOException("Unknown content type:" + contents.getClass().getName());
+                }
+                break;
+            default:
+                throw new IOException("Unknown type of position:" + position);
+            }
+            pageDictionary.setItem(COSName.CONTENTS, contentArray);
+            pageCount++;
+        }
+    }
+
+    private void overlayPage(COSArray array, PDPage page, int pageNumber, int numberOfPages)
+            throws IOException
+    {
+        LayoutPage layoutPage = null;
+        if (specificPageOverlayPage.containsKey(pageNumber))
+        {
+            layoutPage = specificPageOverlayPage.get(pageNumber);
+        }
+        else if ((pageNumber == 1) && (firstPageOverlayPage != null))
+        {
+            layoutPage = firstPageOverlayPage;
+        }
+        else if ((pageNumber == numberOfPages) && (lastPageOverlayPage != null))
+        {
+            layoutPage = lastPageOverlayPage;
+        }
+        else if ((pageNumber % 2 == 1) && (oddPageOverlayPage != null))
+        {
+            layoutPage = oddPageOverlayPage;
+        }
+        else if ((pageNumber % 2 == 0) && (evenPageOverlayPage != null))
+        {
+            layoutPage = evenPageOverlayPage;
+        }
+        else if (defaultOverlayPage != null)
+        {
+            layoutPage = defaultOverlayPage;
+        }
+        if (layoutPage != null)
+        {
+            PDResources resources = page.findResources();
+            if (resources == null)
+            {
+                resources = new PDResources();
+                page.setResources(resources);
+            }
+            String xObjectId = createOverlayXObject(page, layoutPage,
+                    layoutPage.overlayContentStream);
+            array.add(createOverlayStream(page, layoutPage, xObjectId));
+        }
+    }
+
+    private String createOverlayXObject(PDPage page, LayoutPage layoutPage, COSStream contentStream)
+    {
+        PDResources resources = page.findResources();
+        // determine new ID
+        COSDictionary dict = (COSDictionary) resources.getCOSDictionary().getDictionaryObject(
+                COSName.XOBJECT);
+        if (dict == null)
+        {
+            dict = new COSDictionary();
+            resources.getCOSDictionary().setItem(COSName.XOBJECT, dict);
+        }
+        String xObjectId = getNextUniqueKey(resources.getXObjects(), XOBJECT_PREFIX);
+
+        // wrap the layout content in a BBox and add it to page
+        COSStream xobj = contentStream;
+        xobj.setItem(COSName.RESOURCES, layoutPage.overlayResources);
+        xobj.setItem(COSName.TYPE, COSName.XOBJECT);
+        xobj.setItem(COSName.SUBTYPE, COSName.FORM);
+        xobj.setInt(COSName.FORMTYPE, 1);
+        COSArray matrix = new COSArray();
+        matrix.add(COSInteger.get(1));
+        matrix.add(COSInteger.get(0));
+        matrix.add(COSInteger.get(0));
+        matrix.add(COSInteger.get(1));
+        matrix.add(COSInteger.get(0));
+        matrix.add(COSInteger.get(0));
+        xobj.setItem(COSName.MATRIX, matrix);
+        COSArray bbox = new COSArray();
+        bbox.add(COSInteger.get(0));
+        bbox.add(COSInteger.get(0));
+        bbox.add(COSInteger.get((int) layoutPage.overlayMediaBox.getWidth()));
+        bbox.add(COSInteger.get((int) layoutPage.overlayMediaBox.getHeight()));
+        xobj.setItem(COSName.BBOX, bbox);
+        dict.setItem(xObjectId, xobj);
+
+        return xObjectId;
+    }
+
+    private static String getNextUniqueKey(Map<String, ?> map, String prefix)
+    {
+        int counter = 0;
+        while (map != null && map.get(prefix + counter) != null)
+        {
+            counter++;
+        }
+        return prefix + counter;
+    }
+
+    private COSStream createOverlayStream(PDPage page, LayoutPage layoutPage, String xObjectId)
+            throws IOException
+    {
+        // create a new content stream that executes the XObject content
+        PDRectangle pageMediaBox = page.getMediaBox();
+        float scale = 1;
+        float hShift = (pageMediaBox.getWidth() - layoutPage.overlayMediaBox.getWidth()) / 2.0f;
+        float vShift = (pageMediaBox.getHeight() - layoutPage.overlayMediaBox.getHeight()) / 2.0f;
+        return createStream("q\nq " + scale + " 0 0 " + scale + " " + hShift + " " + vShift
+                + " cm /" + xObjectId + " Do Q\nQ\n");
+    }
+
+    private COSStream createStream(String content) throws IOException
+    {
+        COSStream stream = new COSStream(new RandomAccessBuffer());
+        OutputStream out = stream.createUnfilteredStream();
+        out.write(content.getBytes("ISO-8859-1"));
+        out.close();
+        stream.setFilters(COSName.FLATE_DECODE);
+        return stream;
+    }
+
+    /**
+     * Sets the overlay position.
+     * 
+     * @param overlayPosition the overlay position
+     */
+    public void setOverlayPosition(Position overlayPosition)
+    {
+        position = overlayPosition;
+    }
+
+    /**
+     * Sets the file to be overlayed.
+     * 
+     * @param inputFile the file to be overlayed
+     */
+    public void setInputFile(String inputFile)
+    {
+        inputFileName = inputFile;
+    }
+
+    /**
+     * Returns the input file.
+     * 
+     * @return the input file
+     */
+    public String getInputFile()
+    {
+        return inputFileName;
+    }
+
+    /**
+     * Sets the output file.
+     * 
+     * @param outputFile the output file
+     */
+    public void setOutputFile(String outputFile)
+    {
+        outputFilename = outputFile;
+    }
+
+    /**
+     * Returns the output file.
+     * 
+     * @return the output file
+     */
+    public String getOutputFile()
+    {
+        return outputFilename;
+    }
+
+    /**
+     * Sets the default overlay file.
+     * 
+     * @param defaultOverlayFile the default overlay file
+     */
+    public void setDefaultOverlayFile(String defaultOverlayFile)
+    {
+        defaultOverlayFilename = defaultOverlayFile;
+    }
+
+    /**
+     * Returns the default overlay file.
+     * 
+     * @return the default overlay file
+     */
+    public String getDefaultOverlayFile()
+    {
+        return defaultOverlayFilename;
+    }
+
+    /**
+     * Sets the first page overlay file.
+     * 
+     * @param firstPageOverlayFile the first page overlay file
+     */
+    public void setFirstPageOverlayFile(String firstPageOverlayFile)
+    {
+        firstPageOverlayFilename = firstPageOverlayFile;
+    }
+
+    /**
+     * Sets the last page overlay file.
+     * 
+     * @param lastPageOverlayFile the last page overlay file
+     */
+    public void setLastPageOverlayFile(String lastPageOverlayFile)
+    {
+        lastPageOverlayFilename = lastPageOverlayFile;
+    }
+
+    /**
+     * Sets the odd page overlay file.
+     * 
+     * @param oddPageOverlayFile the odd page overlay file
+     */
+    public void setOddPageOverlayFile(String oddPageOverlayFile)
+    {
+        oddPageOverlayFilename = oddPageOverlayFile;
+    }
+
+    /**
+     * Sets the even page overlay file.
+     * 
+     * @param evenPageOverlayFile the even page overlay file
+     */
+    public void setEvenPageOverlayFile(String evenPageOverlayFile)
+    {
+        evenPageOverlayFilename = evenPageOverlayFile;
+    }
+
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/Overlay.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java?rev=1602255&r1=1602254&r2=1602255&view=diff
==============================================================================
--- pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java (original)
+++ pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java Thu Jun 12 18:13:31 2014
@@ -16,33 +16,17 @@
  */
 package org.apache.pdfbox.tools;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.pdfbox.cos.COSArray;
-import org.apache.pdfbox.cos.COSBase;
-import org.apache.pdfbox.cos.COSDictionary;
-import org.apache.pdfbox.cos.COSInteger;
-import org.apache.pdfbox.cos.COSName;
-import org.apache.pdfbox.cos.COSObject;
-import org.apache.pdfbox.cos.COSStream;
-import org.apache.pdfbox.io.RandomAccessBuffer;
 import org.apache.pdfbox.pdfparser.BaseParser;
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
-import org.apache.pdfbox.pdmodel.PDPage;
-import org.apache.pdfbox.pdmodel.PDResources;
-import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.util.Overlay;
+import org.apache.pdfbox.util.Overlay.Position;
 
 /**
+ * 
  * Adds an overlay to an existing PDF document.
  *  
  * Based on code contributed by Balazs Jerk. 
@@ -50,38 +34,8 @@ import org.apache.pdfbox.pdmodel.common.
  */
 public class OverlayPDF 
 {
-    /**
-     *   Possible loacation of the overlayed pages: foreground or background.
-     */
-    private enum Position 
-    { 
-        FOREGROUND, BACKGROUND 
-    };
-
     private static final Log LOG = LogFactory.getLog(BaseParser.class);
 
-    private static final String XOBJECT_PREFIX = "OL";
-    
-    private LayoutPage defaultOverlayPage;
-    private LayoutPage firstPageOverlayPage;
-    private LayoutPage lastPageOverlayPage;
-    private LayoutPage oddPageOverlayPage;
-    private LayoutPage evenPageOverlayPage;
-
-    private static Map<Integer, PDDocument> specificPageOverlay = new HashMap<Integer, PDDocument>();
-    private static Map<Integer, LayoutPage> specificPageOverlayPage = new HashMap<Integer, LayoutPage>();
-
-    private static Position overlayPosition = Position.BACKGROUND;
-
-    private static boolean useNonSeqParser = false;
-    private static String inputFile = null;
-    private static String outputFile = null;
-    private static String defaultOverlayFile = null;
-    private static String firstPageOverlayFile = null;
-    private static String lastPageOverlayFile = null;
-    private static String oddPageOverlayFile = null;
-    private static String evenPageOverlayFile = null;
-    
     // Command line options
     private static final String POSITION = "-position";
     private static final String ODD = "-odd";
@@ -99,29 +53,30 @@ public class OverlayPDF 
      */
     public static void main(final String[] args) throws Exception 
     {
+        Overlay overlayer = new Overlay();
         Map<Integer, String> specificPageOverlayFile = new HashMap<Integer, String>();
-        
+        boolean useNonSeqParser = false;
         // input arguments
         for (int i = 0; i < args.length; i++) 
         {
             String arg = args[i].trim();
             if (i == 0) 
             {
-                inputFile = arg;
+                overlayer.setInputFile(arg);
             } 
             else if (i == (args.length - 1)) 
             {
-                outputFile = arg;
+                overlayer.setOutputFile(arg);
             } 
             else if (arg.equals(POSITION) && ((i + 1) < args.length)) 
             {
                 if (Position.FOREGROUND.toString().equalsIgnoreCase(args[i + 1].trim())) 
                 {
-                    overlayPosition = Position.FOREGROUND;
+                    overlayer.setOverlayPosition(Position.FOREGROUND);
                 }
                 else if (Position.BACKGROUND.toString().equalsIgnoreCase(args[i + 1].trim())) 
                 {
-                    overlayPosition = Position.BACKGROUND;
+                    overlayer.setOverlayPosition(Position.BACKGROUND);
                 }
                 else
                 {
@@ -131,22 +86,22 @@ public class OverlayPDF 
             } 
             else if (arg.equals(ODD) && ((i + 1) < args.length)) 
             {
-                oddPageOverlayFile = args[i + 1].trim();
+                overlayer.setOddPageOverlayFile(args[i + 1].trim());
                 i += 1;
             } 
             else if (arg.equals(EVEN) && ((i + 1) < args.length)) 
             {
-                evenPageOverlayFile = args[i + 1].trim();
+                overlayer.setEvenPageOverlayFile(args[i + 1].trim());
                 i += 1;
             } 
             else if (arg.equals(FIRST) && ((i + 1) < args.length)) 
             {
-                firstPageOverlayFile = args[i + 1].trim();
+                overlayer.setFirstPageOverlayFile(args[i + 1].trim());
                 i += 1;
             } 
             else if (arg.equals(LAST) && ((i + 1) < args.length)) 
             {
-                lastPageOverlayFile = args[i + 1].trim();
+                overlayer.setLastPageOverlayFile(args[i + 1].trim());
                 i += 1;
             } 
             else if (arg.equals(PAGE) && ((i + 2) < args.length) && (isInteger(args[i + 1].trim()))) 
@@ -158,9 +113,9 @@ public class OverlayPDF 
             {
                 useNonSeqParser = true;
             }
-            else if (defaultOverlayFile == null) 
+            else if (overlayer.getDefaultOverlayFile() == null) 
             {
-                defaultOverlayFile = arg;
+                overlayer.setDefaultOverlayFile(arg);
             } 
             else 
             {
@@ -168,15 +123,14 @@ public class OverlayPDF 
             }
         }
         
-        if ((inputFile == null) || (outputFile == null)) 
+        if (overlayer.getInputFile() == null || overlayer.getOutputFile() == null) 
         {
             usage();
         }
         
         try 
         {
-            OverlayPDF overlayer = new OverlayPDF();
-            overlayer.overlay(specificPageOverlayFile);
+            overlayer.overlay(specificPageOverlayFile,useNonSeqParser);
         } 
         catch (Exception e) 
         {
@@ -205,136 +159,6 @@ public class OverlayPDF 
         System.exit( 1 );
     }
 
-    /**
-     * This will add overlays to a documents.
-     *
-     * @param specificPageOverlayFile map of overlay files for specific pages
-     */
-    public void overlay(Map<Integer, String> specificPageOverlayFile) throws IOException
-    {
-        PDDocument sourcePDFDocument = null;
-        PDDocument defaultOverlay = null;
-        PDDocument firstPageOverlay = null;
-        PDDocument lastPageOverlay = null;
-        PDDocument oddPageOverlay = null;
-        PDDocument evenPageOverlay = null;
-        try 
-        {
-            sourcePDFDocument = PDDocument.load(inputFile);
-            if (defaultOverlayFile != null) 
-            {
-                if (useNonSeqParser)
-                {
-                    defaultOverlay = PDDocument.loadNonSeq(new File(defaultOverlayFile), null);
-                }
-                else
-                {
-                    defaultOverlay = PDDocument.load(defaultOverlayFile);
-                }
-                defaultOverlayPage = getLayoutPage(defaultOverlay); 
-            }
-            if (firstPageOverlayFile != null) 
-            {
-                if (useNonSeqParser)
-                {
-                    firstPageOverlay = PDDocument.loadNonSeq(new File(firstPageOverlayFile), null);
-                }
-                else
-                {
-                    firstPageOverlay = PDDocument.load(firstPageOverlayFile);
-                }
-                firstPageOverlayPage = getLayoutPage(firstPageOverlay); 
-            }
-            if (lastPageOverlayFile != null) 
-            {
-                if (useNonSeqParser)
-                {
-                    lastPageOverlay = PDDocument.loadNonSeq(new File(lastPageOverlayFile), null);
-                }
-                else
-                {
-                    lastPageOverlay = PDDocument.load(lastPageOverlayFile);
-                }
-                lastPageOverlayPage = getLayoutPage(lastPageOverlay); 
-            }
-            if (oddPageOverlayFile != null) 
-            {
-                if (useNonSeqParser)
-                {
-                    oddPageOverlay = PDDocument.loadNonSeq(new File(oddPageOverlayFile), null);
-                }
-                else
-                {
-                    oddPageOverlay = PDDocument.load(oddPageOverlayFile);
-                }
-                oddPageOverlayPage = getLayoutPage(oddPageOverlay); 
-            }
-            if (evenPageOverlayFile != null) 
-            {
-                if (useNonSeqParser)
-                {
-                    evenPageOverlay = PDDocument.loadNonSeq(new File(evenPageOverlayFile), null);
-                }
-                else
-                {
-                    evenPageOverlay = PDDocument.load(evenPageOverlayFile);
-                }
-                evenPageOverlayPage = getLayoutPage(evenPageOverlay); 
-            }
-            for (Map.Entry<Integer, String> e : specificPageOverlayFile.entrySet()) 
-            {
-                PDDocument doc = null;
-                if (useNonSeqParser)
-                {
-                    doc = PDDocument.loadNonSeq(new File(e.getValue()), null);
-                }
-                else
-                {
-                    doc = PDDocument.load(e.getValue());
-                }
-                specificPageOverlay.put(e.getKey(), doc);
-                specificPageOverlayPage.put(e.getKey(), getLayoutPage(doc)); 
-            }
-            PDDocumentCatalog pdfCatalog = sourcePDFDocument.getDocumentCatalog();
-            processPages(pdfCatalog.getAllPages());
-
-            sourcePDFDocument.save(outputFile);
-        } 
-        finally 
-        {
-            if (sourcePDFDocument != null) 
-            {
-                sourcePDFDocument.close();
-            }
-            if (defaultOverlay != null) 
-            {
-                defaultOverlay.close();
-            }
-            if (firstPageOverlay != null) 
-            {
-                firstPageOverlay.close();
-            }
-            if (lastPageOverlay != null) 
-            {
-                lastPageOverlay.close();
-            }
-            if (oddPageOverlay != null) 
-            {
-                oddPageOverlay.close();
-            }
-            if (evenPageOverlay != null) 
-            {
-                evenPageOverlay.close();
-            }
-            for (Map.Entry<Integer, PDDocument> e : specificPageOverlay.entrySet()) 
-            {
-                e.getValue().close();
-            }
-            specificPageOverlay.clear();
-            specificPageOverlayPage.clear();
-        }
-    }
-
     private static boolean isInteger(String str) 
     {
         try 
@@ -348,247 +172,4 @@ public class OverlayPDF 
         return true;
     }
 
-    /**
-     * Stores the overlay page information.
-     */
-    private static class LayoutPage 
-    {
-        private final PDRectangle overlayMediaBox;
-        private final COSStream overlayContentStream;
-        private final COSDictionary overlayResources;
-
-        private LayoutPage(PDRectangle mediaBox, COSStream contentStream, COSDictionary resources) 
-        {
-            overlayMediaBox = mediaBox;
-            overlayContentStream = contentStream;
-            overlayResources = resources;
-        }
-    }
-
-    private LayoutPage getLayoutPage(PDDocument doc) throws IOException 
-    {
-        PDDocumentCatalog catalog = doc.getDocumentCatalog();
-        PDPage page = (PDPage)catalog.getAllPages().get(0);
-        COSBase contents = page.getCOSDictionary().getDictionaryObject(COSName.CONTENTS);
-        PDResources resources = page.findResources();
-        if (resources == null) 
-        {
-            resources = new PDResources();
-        }
-        return new LayoutPage(page.getMediaBox(), createContentStream(contents), resources.getCOSDictionary());
-    }
-    
-    private COSStream createContentStream(COSBase contents) throws IOException 
-    {
-        List<COSStream> contentStreams = createContentStreamList(contents);
-        // concatenate streams
-        COSStream concatStream = new COSStream(new RandomAccessBuffer());
-        OutputStream out = concatStream.createUnfilteredStream();
-        for (COSStream contentStream : contentStreams) 
-        {
-            InputStream in = contentStream.getUnfilteredStream();
-            byte[] buf = new byte[2048];
-            int n;
-            while ((n = in.read(buf)) > 0) 
-            {
-                out.write(buf, 0, n);
-            }
-            out.flush();
-        }
-        out.close();
-        concatStream.setFilters(COSName.FLATE_DECODE);
-        return concatStream;
-    }
-    
-    private List<COSStream> createContentStreamList(COSBase contents) throws IOException 
-    {
-        List<COSStream> contentStreams = new ArrayList<COSStream>();
-        if (contents instanceof COSStream) 
-        {
-            contentStreams.add((COSStream) contents);
-        } 
-        else if (contents instanceof COSArray) 
-        {
-            for (COSBase item : (COSArray) contents) 
-            {
-                contentStreams.addAll(createContentStreamList(item));
-            }
-        } 
-        else if (contents instanceof COSObject) 
-        {
-            contentStreams.addAll(createContentStreamList(((COSObject) contents).getObject()));
-        } 
-        else 
-        {
-            throw new IOException("Contents are unknown type:" + contents.getClass().getName());
-        }
-        return contentStreams;
-    }
-    
-    private void processPages(List<?> pages) throws IOException 
-    {
-        int pageCount = 0;
-        for (Object pageObject : pages) 
-        {
-            PDPage page = (PDPage) pageObject;
-            COSDictionary pageDictionary = page.getCOSDictionary();
-            COSBase contents = pageDictionary.getDictionaryObject(COSName.CONTENTS);
-            COSArray contentArray = new COSArray();
-            switch (overlayPosition) 
-            {
-            case FOREGROUND:
-                // save state
-                contentArray.add(createStream("q\n"));
-                // original content
-                if (contents instanceof COSStream) 
-                {
-                    contentArray.add(contents);
-                } 
-                else if (contents instanceof COSArray) 
-                {
-                    contentArray.addAll((COSArray) contents);
-                } 
-                else 
-                {
-                    throw new IOException("Unknown content type:" + contents.getClass().getName());
-                }
-                // restore state
-                contentArray.add(createStream("Q\n"));
-                // overlay content
-                overlayPage(contentArray, page, pageCount + 1, pages.size());
-                break;
-            case BACKGROUND:
-                // overlay content
-                overlayPage(contentArray, page, pageCount + 1, pages.size());
-                // original content
-                if (contents instanceof COSStream) 
-                {
-                    contentArray.add(contents);
-                } 
-                else if (contents instanceof COSArray) 
-                {
-                    contentArray.addAll((COSArray) contents);
-                } 
-                else 
-                {
-                    throw new IOException("Unknown content type:" + contents.getClass().getName());
-                }
-                break;
-            default:
-                throw new IOException("Unknown type of position:" + overlayPosition);
-            }
-            pageDictionary.setItem(COSName.CONTENTS, contentArray);
-            pageCount++;
-        }
-    }
-
-    private void overlayPage(COSArray array, PDPage page, int pageNumber, int numberOfPages) throws IOException 
-    {
-        LayoutPage layoutPage = null;
-        if (specificPageOverlayPage.containsKey(pageNumber)) 
-        {
-            layoutPage = specificPageOverlayPage.get(pageNumber);
-        } 
-        else if ((pageNumber == 1) && (firstPageOverlayPage != null)) 
-        {
-            layoutPage = firstPageOverlayPage;
-        } 
-        else if ((pageNumber == numberOfPages) && (lastPageOverlayPage != null)) 
-        {
-            layoutPage = lastPageOverlayPage;
-        } 
-        else if ((pageNumber % 2 == 1) && (oddPageOverlayPage != null)) 
-        {
-            layoutPage = oddPageOverlayPage;
-        } 
-        else if ((pageNumber % 2 == 0) && (evenPageOverlayPage != null)) 
-        {
-            layoutPage = evenPageOverlayPage;
-        } 
-        else if (defaultOverlayPage != null) 
-        {
-            layoutPage = defaultOverlayPage;
-        }
-        if (layoutPage != null) 
-        {
-            PDResources resources = page.findResources();
-            if (resources == null) 
-            {
-                resources = new PDResources();
-                page.setResources(resources);
-            }
-            String xObjectId = createOverlayXObject(page, layoutPage, layoutPage.overlayContentStream);
-            array.add(createOverlayStream(page, layoutPage, xObjectId));
-        }
-    }
-    
-    private String createOverlayXObject(PDPage page, LayoutPage layoutPage, COSStream contentStream) 
-    {
-        PDResources resources = page.findResources();
-        // determine new ID
-        COSDictionary dict = (COSDictionary) resources.getCOSDictionary().getDictionaryObject(COSName.XOBJECT);
-        if (dict == null) 
-        {
-            dict = new COSDictionary();
-            resources.getCOSDictionary().setItem(COSName.XOBJECT, dict);
-        }
-        String xObjectId = getNextUniqueKey( resources.getXObjects(), XOBJECT_PREFIX );
-
-        // wrap the layout content in a BBox and add it to page
-        COSStream xobj = contentStream;
-        xobj.setItem(COSName.RESOURCES, layoutPage.overlayResources);
-        xobj.setItem(COSName.TYPE, COSName.XOBJECT);
-        xobj.setItem(COSName.SUBTYPE, COSName.FORM);
-        xobj.setInt(COSName.FORMTYPE, 1);
-        COSArray matrix = new COSArray();
-        matrix.add(COSInteger.get(1));
-        matrix.add(COSInteger.get(0));
-        matrix.add(COSInteger.get(0));
-        matrix.add(COSInteger.get(1));
-        matrix.add(COSInteger.get(0));
-        matrix.add(COSInteger.get(0));
-        xobj.setItem(COSName.MATRIX, matrix);
-        COSArray bbox = new COSArray();
-        bbox.add(COSInteger.get(0));
-        bbox.add(COSInteger.get(0));
-        bbox.add(COSInteger.get((int) layoutPage.overlayMediaBox.getWidth()));
-        bbox.add(COSInteger.get((int) layoutPage.overlayMediaBox.getHeight()));
-        xobj.setItem(COSName.BBOX, bbox);
-        dict.setItem(xObjectId, xobj);
-
-        return xObjectId;
-    }
-
-    // TODO this is obsolete, should be using PDResources#addXObject instead
-    private static String getNextUniqueKey( Map<String,?> map, String prefix )
-    {
-        int counter = 0;
-        while( map != null && map.get( prefix+counter ) != null )
-        {
-            counter++;
-        }
-        return prefix+counter;
-    }
-
-    private COSStream createOverlayStream(PDPage page, LayoutPage layoutPage, String xObjectId) throws IOException 
-    {
-        // create a new content stream that executes the XObject content
-        PDRectangle pageMediaBox = page.getMediaBox();
-        float scale = 1;
-        float hShift = (pageMediaBox.getWidth() - layoutPage.overlayMediaBox.getWidth()) / 2.0f;
-        float vShift = (pageMediaBox.getHeight() - layoutPage.overlayMediaBox.getHeight()) / 2.0f;
-        return createStream("q\nq " + scale + " 0 0 " + scale + " " + hShift + " " + vShift + " cm /" 
-                + xObjectId + " Do Q\nQ\n");
-    }
-
-    private COSStream createStream(String content) throws IOException 
-    {
-        COSStream stream = new COSStream(new RandomAccessBuffer());
-        OutputStream out = stream.createUnfilteredStream();
-        out.write(content.getBytes("ISO-8859-1"));
-        out.close();
-        stream.setFilters(COSName.FLATE_DECODE);
-        return stream;
-    }
-    
 }