You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by je...@apache.org on 2010/10/22 09:21:35 UTC

svn commit: r1026228 [1/2] - in /pdfbox/trunk/pdfbox/src: main/java/org/apache/pdfbox/cos/ main/java/org/apache/pdfbox/pdmodel/ main/java/org/apache/pdfbox/pdmodel/common/ main/java/org/apache/pdfbox/pdmodel/edit/ main/java/org/apache/pdfbox/pdmodel/gr...

Author: jeremias
Date: Fri Oct 22 07:21:34 2010
New Revision: 1026228

URL: http://svn.apache.org/viewvc?rev=1026228&view=rev
Log:
PDFBOX-865:
Added initial support for optional content groups (OCGs aka layers).

Details:
- Adds PD-layer classes to deal with optional content groups
- Made COSArray an Iterable
- COSDocument: Allow to remove an object from the document objects
- PDPageContentStream: added some functionality from marked content (incomplete)
- PDPageContentStream Bugfix: Form XObjects were always "forgotten" when a second PDPageContentStream was constructed for the same page.
- PDXObjectForm: allow to set the Matrix.
- PDDocumentCatalog: allow to set the PDF version and OCProperties
- PDResources: allow to set Properties (PDPropertyList, used for marked content lookups)
- COSObjectKey learns Comparable
- Moved object cloning functionality from PDFMergerUtility to separate class PDFCloneUtility, because it's reused by LayerUtility introduced below.
- LayerUtil allows to import pages a Form XObjects and create layers with them. These layers can later be switched on and off through the OCG PD-layer classes. The code for is largely adapted from my PDF-plug-in for Apache FOP.

Added:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/LayerUtility.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFCloneUtility.java   (with props)
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/TestOptionalContentGroups.java   (with props)
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestLayerUtility.java   (with props)
Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDResources.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSDictionaryMap.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/edit/PDPageContentStream.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/persistence/util/COSObjectKey.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/TestAll.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java Fri Oct 22 07:21:34 2010
@@ -21,8 +21,6 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
-
-
 import org.apache.pdfbox.exceptions.COSVisitorException;
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 
@@ -32,7 +30,7 @@ import org.apache.pdfbox.pdmodel.common.
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  * @version $Revision: 1.24 $
  */
-public class COSArray extends COSBase
+public class COSArray extends COSBase implements Iterable<COSBase>
 {
     private List<COSBase> objects = new ArrayList<COSBase>();
 
@@ -235,7 +233,7 @@ public class COSArray extends COSBase
     public int getInt( int index, int defaultValue )
     {
         int retval = defaultValue;
-        if ( index < size() ) 
+        if ( index < size() )
         {
             Object obj = objects.get( index );
             if( obj instanceof COSNumber )
@@ -377,7 +375,7 @@ public class COSArray extends COSBase
     /**
      * This will remove an element from the array.
      * This method will also remove a reference to the object.
-     * 
+     *
      * @param o The object to remove.
      * @return <code>true</code> if the object was removed, <code>false</code>
      *  otherwise
@@ -443,7 +441,7 @@ public class COSArray extends COSBase
     /**
      * This will return the index of the entry or -1 if it is not found.
      * This method will also find references to indirect objects.
-     * 
+     *
      * @param object The object to search for.
      * @return The index of the object or -1.
      */
@@ -538,11 +536,11 @@ public class COSArray extends COSBase
             add( new COSFloat( value[i] ) );
         }
     }
-    
+
     /**
      *  Return contents of COSArray as a Java List.
-     *  
-     *  @return the COSArray as List 
+     *
+     *  @return the COSArray as List
      */
     public List<COSBase> toList()
     {

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java Fri Oct 22 07:21:34 2010
@@ -26,7 +26,6 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.pdfbox.exceptions.COSVisitorException;
-
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.util.DateConverter;
 
@@ -570,11 +569,11 @@ public class COSDictionary extends COSBa
         COSBase name = getDictionaryObject( key );
         if( name != null )
         {
-            if ( name instanceof COSName) 
+            if ( name instanceof COSName)
             {
                 retval = ((COSName)name).getName();
             }
-            else if ( name instanceof COSString) 
+            else if ( name instanceof COSString)
             {
                 retval = ((COSString)name).getString();
             }
@@ -1242,7 +1241,8 @@ public class COSDictionary extends COSBa
      * @since Apache PDFBox 1.1.0
      * @return name-value entries in this dictionary
      */
-    public Set<Map.Entry<COSName, COSBase>> entrySet() {
+    public Set<Map.Entry<COSName, COSBase>> entrySet()
+    {
         return items.entrySet();
     }
 
@@ -1271,7 +1271,7 @@ public class COSDictionary extends COSBa
 
     /**
      * This will add all of the dictionarys keys/values to this dictionary.
-     * Only called when adding keys to a trailer that already exists. 
+     * Only called when adding keys to a trailer that already exists.
      *
      * @param dic The dic to get the keys from.
      */
@@ -1280,9 +1280,9 @@ public class COSDictionary extends COSBa
         for( Map.Entry<COSName, COSBase> entry : dic.entrySet() )
         {
             /*
-             * If we're at a second trailer, we have a linearized 
+             * If we're at a second trailer, we have a linearized
              * pdf file, meaning that the first Size entry represents
-             * all of the objects so we don't need to grab the second. 
+             * all of the objects so we don't need to grab the second.
              */
             if(!entry.getKey().getName().equals("Size")
                     || !items.containsKey(COSName.getPDFName("Size")))
@@ -1338,7 +1338,7 @@ public class COSDictionary extends COSBa
         }
         return retval;
     }
-    
+
     /**
      * {@inheritDoc}
      */

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java Fri Oct 22 07:21:34 2010
@@ -18,7 +18,6 @@ package org.apache.pdfbox.cos;
 
 import java.io.File;
 import java.io.IOException;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -29,7 +28,6 @@ import org.apache.commons.logging.LogFac
 import org.apache.pdfbox.exceptions.COSVisitorException;
 import org.apache.pdfbox.io.RandomAccess;
 import org.apache.pdfbox.io.RandomAccessFile;
-
 import org.apache.pdfbox.pdfparser.PDFObjectStreamParser;
 import org.apache.pdfbox.pdfparser.PDFXrefStreamParser;
 import org.apache.pdfbox.persistence.util.COSObjectKey;
@@ -524,6 +522,17 @@ public class COSDocument extends COSBase
     }
 
     /**
+     * Removes an object from the object pool.
+     * @param key the object key
+     * @return the object that was removed or null if the object was not found
+     */
+    public COSObject removeObject(COSObjectKey key)
+    {
+        COSObject obj = objectPool.remove(key);
+        return obj;
+    }
+
+    /**
      * Used to populate the XRef HashMap. Will add an Xreftable entry
      * that maps ObjectKeys to byte offsets in the file.
      * @param objKey The objkey, with id and gen numbers

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java Fri Oct 22 07:21:34 2010
@@ -18,10 +18,9 @@ package org.apache.pdfbox.cos;
 
 import java.io.IOException;
 import java.io.OutputStream;
-
 import java.util.HashMap;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.pdfbox.exceptions.COSVisitorException;
 import org.apache.pdfbox.persistence.util.COSHEXTable;
@@ -129,9 +128,13 @@ public final class COSName extends COSBa
     * A common COSName value.
     */
     public static final COSName BASE_FONT = new COSName( "BaseFont" );
+
+    /** the COSName for "BaseState". */
+    public static final COSName BASE_STATE = new COSName( "BaseState" );
+
     /**
-    * A common COSName value.
-    */
+     * A common COSName value.
+     */
     public static final COSName BBOX = new COSName( "BBox" );
     /**
      * A common COSName value.
@@ -141,7 +144,7 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName BLACK_POINT = new COSName( "BlackPoint" );
-        
+
     /**
      * A common COSName value.
      */
@@ -651,12 +654,26 @@ public final class COSName extends COSBa
      * "O"
      */
     public static final COSName O = new COSName("O");
-    
+
     /**
      * "Obj"
      */
     public static final COSName OBJ = new COSName("Obj");
 
+    /** the COSName for the content group tag. */
+    public static final COSName OC = new COSName("OC");
+    /** the COSName for an optional content group. */
+    public static final COSName OCG = new COSName("OCG");
+    /** the COSName for the optional content group list. */
+    public static final COSName OCGS = new COSName("OCGs");
+    /** the COSName for the optional content properties. */
+    public static final COSName OCPROPERTIES = new COSName("OCProperties");
+
+    /** the COSName for the "OFF" value. */
+    public static final COSName OFF = new COSName("OFF");
+    /** the COSName for the "ON" value. */
+    public static final COSName ON = new COSName("ON");
+
     /**
      * A common COSName value.
      */
@@ -675,6 +692,9 @@ public final class COSName extends COSBa
     /** "OpenAction" */
     public static final COSName OPEN_ACTION = new COSName("OpenAction");
 
+    /** A common COSName value. */
+     public static final COSName ORDER = new COSName( "Order" );
+
     /**
      * A common COSName value.
      */
@@ -730,7 +750,7 @@ public final class COSName extends COSBa
      */
     public static final COSName PREV = new COSName( "Prev" );
 
-    /** "ProcSet" */
+    /** The COSName value for "ProcSet". */
     public static final COSName PROC_SET = new COSName( "ProcSet" );
 
     /**
@@ -738,6 +758,9 @@ public final class COSName extends COSBa
      */
     public static final COSName PRODUCER = new COSName( "Producer" );
 
+    /** The COSName value for "Properties". */
+    public static final COSName PROPERTIES = new COSName( "Properties" );
+
     /**
      * A common COSName value.
      */
@@ -828,7 +851,7 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName STR_F = new COSName( "StrF" );
-  
+
     /** "StructTreeRoot" */
     public static final COSName STRUCT_TREE_ROOT = new COSName("StructTreeRoot");
 
@@ -848,7 +871,7 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName SUBTYPE = new COSName( "Subtype" );
-    
+
     /**
      * "T"
      */
@@ -902,6 +925,8 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName U = new COSName( "U" );
+    /** the COSName for the "Unchanged" value. */
+    public static final COSName UNCHANGED = new COSName("Unchanged");
     /** "URI" */
     public static final COSName URI = new COSName("URI");
 
@@ -937,7 +962,7 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName WHITE_POINT = new COSName( "WhitePoint" );
-    
+
     /** "XObject" */
     public static final COSName XOBJECT = new COSName( "XObject" );
 
@@ -986,7 +1011,7 @@ public final class COSName extends COSBa
      * that are created.
      *
      * @param aName The name of the COSName object.
-     * @param staticValue Indicates if the COSName object is static so that it can 
+     * @param staticValue Indicates if the COSName object is static so that it can
      *        be stored in the HashMap without synchronizing.
      */
     private COSName( String aName, boolean staticValue )

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java Fri Oct 22 07:21:34 2010
@@ -25,7 +25,6 @@ import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSStream;
-
 import org.apache.pdfbox.pdmodel.common.COSArrayList;
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.pdmodel.common.PDDestinationOrAction;
@@ -33,13 +32,13 @@ import org.apache.pdfbox.pdmodel.common.
 import org.apache.pdfbox.pdmodel.common.PDPageLabels;
 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
-import org.apache.pdfbox.pdmodel.interactive.action.type.PDURIDictionary;
+import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties;
 import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory;
 import org.apache.pdfbox.pdmodel.interactive.action.PDDocumentCatalogAdditionalActions;
+import org.apache.pdfbox.pdmodel.interactive.action.type.PDURIDictionary;
 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
 import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
-
 import org.apache.pdfbox.pdmodel.interactive.pagenavigation.PDThread;
 import org.apache.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences;
 
@@ -551,17 +550,37 @@ public class PDDocumentCatalog implement
     }
 
     /**
+     * Returns the PDF specification version this document conforms to.
+     *
+     * @return The PDF version.
+     */
+    public String getVersion()
+    {
+        return root.getNameAsString(COSName.VERSION);
+    }
+
+    /**
+     * Sets the PDF specification version this document conforms to.
+     *
+     * @param version the PDF version (ex. "1.4")
+     */
+    public void setVersion(String version)
+    {
+        root.setName(COSName.VERSION, version);
+    }
+
+    /**
      * Returns the page labels descriptor of the document.
-     * 
+     *
      * @return the page labels descriptor of the document.
-     * 
+     *
      * @throws IOException If there is a problem retrieving the page labels.
      */
-    public PDPageLabels getPageLabels() throws IOException 
+    public PDPageLabels getPageLabels() throws IOException
     {
         PDPageLabels labels = null;
         COSDictionary dict = (COSDictionary) root.getDictionaryObject(COSName.PAGE_LABELS);
-        if (dict != null) 
+        if (dict != null)
         {
             labels = new PDPageLabels(document, dict);
         }
@@ -573,9 +592,39 @@ public class PDDocumentCatalog implement
      *
      * @param labels the new page label descriptor to set.
      */
-    public void setPageLabels(PDPageLabels labels) 
+    public void setPageLabels(PDPageLabels labels)
     {
         root.setItem(COSName.PAGE_LABELS, labels);
     }
 
+    /**
+     * Get the optional content properties dictionary associated with this document.
+     *
+     * @return the optional properties dictionary or null if it is not present
+     * @since PDF 1.5
+     */
+    public PDOptionalContentProperties getOCProperties()
+    {
+        PDOptionalContentProperties retval = null;
+        COSDictionary dict = (COSDictionary)root.getDictionaryObject(COSName.OCPROPERTIES);
+        if (dict != null)
+        {
+            retval = new PDOptionalContentProperties(dict);
+        }
+
+        return retval;
+    }
+
+    /**
+     * Set the optional content properties dictionary.
+     *
+     * @param ocProperties the optional properties dictionary
+     * @since PDF 1.5
+     */
+    public void setOCProperties(PDOptionalContentProperties ocProperties)
+    {
+        //TODO Check for PDF 1.5 or higher
+        root.setItem(COSName.OCPROPERTIES, ocProperties);
+    }
+
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDResources.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDResources.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDResources.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDResources.java Fri Oct 22 07:21:34 2010
@@ -17,7 +17,6 @@
 package org.apache.pdfbox.pdmodel;
 
 import java.io.IOException;
-
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -33,6 +32,7 @@ import org.apache.pdfbox.pdmodel.graphic
 import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpaceFactory;
 import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject;
 import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+import org.apache.pdfbox.pdmodel.markedcontent.PDPropertyList;
 
 /**
  * This represents a set of resources available at the page/pages/stream level.
@@ -292,4 +292,31 @@ public class PDResources implements COSO
         }
         resources.setItem( COSName.EXT_G_STATE, dic );
     }
+
+    /**
+     * Returns the dictionary mapping resource names to property list dictionaries for marked
+     * content.
+     * @return the property list
+     */
+    public PDPropertyList getProperties()
+    {
+        PDPropertyList retval = null;
+        COSDictionary props = (COSDictionary)resources.getDictionaryObject(COSName.PROPERTIES);
+
+        if (props != null)
+        {
+            retval = new PDPropertyList(props);
+        }
+        return retval;
+    }
+
+    /**
+     * Sets the dictionary mapping resource names to property list dictionaries for marked
+     * content.
+     * @param props the property list
+     */
+    public void setProperties(PDPropertyList props)
+    {
+        resources.setItem(COSName.PROPERTIES, props.getCOSObject());
+    }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSDictionaryMap.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSDictionaryMap.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSDictionaryMap.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSDictionaryMap.java Fri Oct 22 07:21:34 2010
@@ -16,6 +16,14 @@
  */
 package org.apache.pdfbox.pdmodel.common;
 
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSBoolean;
 import org.apache.pdfbox.cos.COSDictionary;
@@ -24,14 +32,6 @@ import org.apache.pdfbox.cos.COSInteger;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSString;
 
-import java.io.IOException;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * This is a Map that will automatically sync the contents to a COSDictionary.
  *
@@ -154,7 +154,7 @@ public class COSDictionaryMap implements
      */
     public Set entrySet()
     {
-        throw new RuntimeException( "Not yet implemented" );
+        return Collections.unmodifiableSet(actuals.entrySet());
     }
 
     /**

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/edit/PDPageContentStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/edit/PDPageContentStream.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/edit/PDPageContentStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/edit/PDPageContentStream.java Fri Oct 22 07:21:34 2010
@@ -18,26 +18,26 @@ package org.apache.pdfbox.pdmodel.edit;
 
 import java.awt.Color;
 import java.awt.color.ColorSpace;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.PathIterator;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-
 import java.text.NumberFormat;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.HashMap;
 
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSString;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.PDResources;
-
 import org.apache.pdfbox.pdmodel.common.COSStreamArray;
 import org.apache.pdfbox.pdmodel.common.PDStream;
-
 import org.apache.pdfbox.pdmodel.font.PDFont;
 import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
 import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
@@ -51,11 +51,6 @@ import org.apache.pdfbox.pdmodel.graphic
 import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
 import org.apache.pdfbox.util.MapUtil;
 
-import org.apache.pdfbox.cos.COSArray;
-import org.apache.pdfbox.cos.COSDictionary;
-import org.apache.pdfbox.cos.COSName;
-import org.apache.pdfbox.cos.COSString;
-
 
 /**
  * This class will is a convenience for creating page content streams.  You MUST
@@ -69,8 +64,8 @@ public class PDPageContentStream
     private PDPage page;
     private OutputStream output;
     private boolean inTextMode = false;
-    private Map<PDFont,String> fontMappings = new HashMap<PDFont,String>();
-    private Map<PDXObject,String> xobjectMappings = new HashMap<PDXObject,String>();
+    private Map<PDFont,String> fontMappings;
+    private Map<PDXObject,String> xobjectMappings;
     private PDResources resources;
     private Map fonts;
     private Map xobjects;
@@ -115,7 +110,12 @@ public class PDPageContentStream
     private static final String BEZIER_312 = "c\n";
     private static final String BEZIER_32 = "v\n";
     private static final String BEZIER_313 = "y\n";
-    
+
+    private static final String MP = "MP\n";
+    private static final String DP = "DP\n";
+    private static final String BMC = "BMC\n";
+    private static final String BDC = "BDC\n";
+    private static final String EMC = "EMC\n";
 
     private static final String SET_STROKING_COLORSPACE = "CS\n";
     private static final String SET_NON_STROKING_COLORSPACE = "cs\n";
@@ -161,8 +161,15 @@ public class PDPageContentStream
             resources = new PDResources();
             page.setResources( resources );
         }
+
+        //Fonts including reverse lookup
         fonts = resources.getFonts();
-        xobjects = resources.getImages();
+        fontMappings = reverseMap(fonts, PDFont.class);
+
+        //XObjects including reverse lookup
+        xobjects = resources.getXObjects();
+        xobjectMappings = reverseMap(xobjects, PDXObject.class);
+
         // If request specifies the need to append to the document
         if(appendContent)
         {
@@ -217,6 +224,17 @@ public class PDPageContentStream
         formatDecimal.setGroupingUsed( false );
     }
 
+    private <T> Map<T, String> reverseMap(Map map, Class<T> keyClass)
+    {
+        Map<T, String> reversed = new java.util.HashMap<T, String>();
+        for (Object o : map.entrySet())
+        {
+            Map.Entry entry = (Map.Entry)o;
+            reversed.put(keyClass.cast(entry.getValue()), (String)entry.getKey());
+        }
+        return reversed;
+    }
+
     /**
      * Begin some text operations.
      *
@@ -300,6 +318,20 @@ public class PDPageContentStream
      */
     public void drawXObject( PDXObject xobject, float x, float y, float width, float height ) throws IOException
     {
+        AffineTransform transform = new AffineTransform(width, 0, 0, height, x, y);
+        drawXObject(xobject, transform);
+    }
+
+    /**
+     * Draw an xobject(form or image) using the given {@link AffineTransform} to position
+     * the xobject.
+     *
+     * @param xobject The xobject to draw.
+     * @param transform the transformation matrix
+     * @throws IOException If there is an error writing to the stream.
+     */
+    public void drawXObject( PDXObject xobject, AffineTransform transform ) throws IOException
+    {
         String xObjectPrefix = null;
         if( xobject instanceof PDXObjectImage )
         {
@@ -318,19 +350,20 @@ public class PDPageContentStream
             xobjects.put( objMapping, xobject );
         }
         saveGraphicsState();
-        concatenate2CTM(width, 0, 0, height, x, y);
+        appendRawCommands( SPACE );
+        concatenate2CTM(transform);
         appendRawCommands( SPACE );
         appendRawCommands( "/" );
         appendRawCommands( objMapping );
         appendRawCommands( SPACE );
         appendRawCommands( XOBJECT_DO );
-        appendRawCommands( SPACE );
         restoreGraphicsState();
     }
 
+
     /**
      * The Td operator.
-     * A current text matrix will be replaced with a new one (1 0 0 1 x y). 
+     * A current text matrix will be replaced with a new one (1 0 0 1 x y).
      * @param x The x coordinate.
      * @param y The y coordinate.
      * @throws IOException If there is an error writing to the stream.
@@ -350,7 +383,7 @@ public class PDPageContentStream
 
     /**
      * The Tm operator. Sets the text matrix to the given values.
-     * A current text matrix will be replaced with the new one. 
+     * A current text matrix will be replaced with the new one.
      * @param a The a value of the matrix.
      * @param b The b value of the matrix.
      * @param c The c value of the matrix.
@@ -381,8 +414,20 @@ public class PDPageContentStream
     }
 
     /**
+    * The Tm operator. Sets the text matrix to the given values.
+    * A current text matrix will be replaced with the new one.
+    * @param matrix the transformation matrix
+    * @throws IOException If there is an error writing to the stream.
+    */
+    public void setTextMatrix(AffineTransform matrix) throws IOException
+    {
+        appendMatrix(matrix);
+        appendRawCommands(SET_TEXT_MATRIX);
+    }
+
+    /**
      * The Tm operator. Sets the text matrix to the given scaling and translation values.
-     * A current text matrix will be replaced with the new one. 
+     * A current text matrix will be replaced with the new one.
      * @param sx The scaling factor in x-direction.
      * @param sy The scaling factor in y-direction.
      * @param tx The translation value in x-direction.
@@ -396,7 +441,7 @@ public class PDPageContentStream
 
     /**
      * The Tm operator. Sets the text matrix to the given translation values.
-     * A current text matrix will be replaced with the new one. 
+     * A current text matrix will be replaced with the new one.
      * @param tx The translation value in x-direction.
      * @param ty The translation value in y-direction.
      * @throws IOException If there is an error writing to the stream.
@@ -408,7 +453,7 @@ public class PDPageContentStream
 
     /**
      * The Tm operator. Sets the text matrix to the given rotation and translation values.
-     * A current text matrix will be replaced with the new one. 
+     * A current text matrix will be replaced with the new one.
      * @param angle The angle used for the counterclockwise rotation in radians.
      * @param tx The translation value in x-direction.
      * @param ty The translation value in y-direction.
@@ -449,6 +494,18 @@ public class PDPageContentStream
     }
 
     /**
+     * The Cm operator. Concatenates the current transformation matrix with the given
+     * {@link AffineTransform}.
+     * @param at the transformation matrix
+     * @throws IOException If there is an error writing to the stream.
+     */
+    public void concatenate2CTM(AffineTransform at) throws IOException
+    {
+        appendMatrix(at);
+        appendRawCommands(CONCATENATE_MATRIX);
+    }
+
+    /**
      * This will draw a string at the current location on the screen.
      *
      * @param text The text to draw.
@@ -858,7 +915,7 @@ public class PDPageContentStream
     }
 
     /**
-     * Append a cubic Bézier curve to the current path. The curve extends from the current 
+     * Append a cubic Bézier curve to the current path. The curve extends from the current
      * point to the point (x3 , y3 ), using (x1 , y1 ) and (x2 , y2 ) as the Bézier control points
      * @param x1 x coordinate of the point 1
      * @param y1 y coordinate of the point 1
@@ -886,7 +943,7 @@ public class PDPageContentStream
     }
 
     /**
-     * Append a cubic Bézier curve to the current path. The curve extends from the current 
+     * Append a cubic Bézier curve to the current path. The curve extends from the current
      * point to the point (x3 , y3 ), using the current point and (x2 , y2 ) as the Bézier control points
      * @param x2 x coordinate of the point 2
      * @param y2 y coordinate of the point 2
@@ -908,7 +965,7 @@ public class PDPageContentStream
     }
 
     /**
-     * Append a cubic Bézier curve to the current path. The curve extends from the current 
+     * Append a cubic Bézier curve to the current path. The curve extends from the current
      * point to the point (x3 , y3 ), using (x1 , y1 ) and (x3 , y3 ) as the Bézier control points
      * @param x1 x coordinate of the point 1
      * @param y1 y coordinate of the point 1
@@ -928,10 +985,10 @@ public class PDPageContentStream
         appendRawCommands( SPACE );
         appendRawCommands( BEZIER_313 );
     }
-    
-    
+
+
     /**
-     * Add a line to the given coordinate. 
+     * Add a line to the given coordinate.
      *
      * @param x The x coordinate.
      * @param y The y coordinate.
@@ -979,7 +1036,7 @@ public class PDPageContentStream
         // lineTo
         lineTo(xEnd, yEnd);
     }
-    
+
     /**
      * Draw a line on the page using the current non stroking color and the current line width.
      *
@@ -995,7 +1052,7 @@ public class PDPageContentStream
         // stroke
         stroke();
     }
-    
+
     /**
      * Add a polygon to the current path.
      * @param x x coordinate of each points
@@ -1021,7 +1078,7 @@ public class PDPageContentStream
         }
         closeSubPath();
     }
-       
+
     /**
      * Draw a polygon on the page using the current non stroking color.
      * @param x x coordinate of each points
@@ -1045,27 +1102,27 @@ public class PDPageContentStream
         addPolygon(x, y);
         fill(PathIterator.WIND_NON_ZERO);
     }
-       
+
     /**
      * Stroke the path.
      */
-    public void stroke() throws IOException 
+    public void stroke() throws IOException
     {
         appendRawCommands( STROKE );
     }
-    
+
     /**
      * Close and stroke the path.
      */
-    public void closeAndStroke() throws IOException 
+    public void closeAndStroke() throws IOException
     {
         appendRawCommands( CLOSE_STROKE );
     }
-    
+
     /**
      * Fill the path.
      */
-    public void fill(int windingRule) throws IOException 
+    public void fill(int windingRule) throws IOException
     {
         if (windingRule == PathIterator.WIND_NON_ZERO)
         {
@@ -1075,17 +1132,17 @@ public class PDPageContentStream
         {
             appendRawCommands( FILL_EVEN_ODD );
         }
-        else 
+        else
         {
             throw new IOException( "Error: unknown value for winding rule" );
         }
-            
+
     }
 
     /**
      * Close subpath.
      */
-    public void closeSubPath() throws IOException 
+    public void closeSubPath() throws IOException
     {
         appendRawCommands( CLOSE_SUBPATH );
     }
@@ -1093,7 +1150,7 @@ public class PDPageContentStream
     /**
      * Clip path.
      */
-    public void clipPath(int windingRule) throws IOException 
+    public void clipPath(int windingRule) throws IOException
     {
         if (windingRule == PathIterator.WIND_NON_ZERO)
         {
@@ -1105,7 +1162,7 @@ public class PDPageContentStream
             appendRawCommands( CLIP_PATH_EVEN_ODD );
             appendRawCommands( NOP );
         }
-        else 
+        else
         {
             throw new IOException( "Error: unknown value for winding rule" );
         }
@@ -1125,10 +1182,47 @@ public class PDPageContentStream
     }
 
     /**
+     * Begin a marked content sequence.
+     * @param tag the tag
+     * @throws IOException if an I/O error occurs
+     */
+    public void beginMarkedContentSequence(COSName tag) throws IOException
+    {
+        appendCOSName(tag);
+        appendRawCommands(SPACE);
+        appendRawCommands(BMC);
+    }
+
+    /**
+     * Begin a marked content sequence with a reference to an entry in the page resources'
+     * Properties dictionary.
+     * @param tag the tag
+     * @param propsName the properties reference
+     * @throws IOException if an I/O error occurs
+     */
+    public void beginMarkedContentSequence(COSName tag, COSName propsName) throws IOException
+    {
+        appendCOSName(tag);
+        appendRawCommands(SPACE);
+        appendCOSName(propsName);
+        appendRawCommands(SPACE);
+        appendRawCommands(BDC);
+    }
+
+    /**
+     * End a marked content sequence.
+     * @throws IOException if an I/O error occurs
+     */
+    public void endMarkedContentSequence() throws IOException
+    {
+        appendRawCommands(EMC);
+    }
+
+    /**
      * q operator. Saves the current graphics state.
      * @throws IOException If an error occurs while writing to the stream.
      */
-    public void saveGraphicsState() throws IOException 
+    public void saveGraphicsState() throws IOException
     {
         appendRawCommands( SAVE_GRAPHICS_STATE);
     }
@@ -1137,7 +1231,7 @@ public class PDPageContentStream
      * Q operator. Restores the current graphics state.
      * @throws IOException If an error occurs while writing to the stream.
      */
-    public void restoreGraphicsState() throws IOException 
+    public void restoreGraphicsState() throws IOException
     {
         appendRawCommands( RESTORE_GRAPHICS_STATE );
     }
@@ -1177,6 +1271,27 @@ public class PDPageContentStream
     }
 
     /**
+     * This will append a {@link COSName} to the content stream.
+     * @param name the name
+     * @throws IOException If an error occurs while writing to the stream.
+     */
+    public void appendCOSName(COSName name) throws IOException
+    {
+        name.writePDF(output);
+    }
+
+    private void appendMatrix(AffineTransform transform) throws IOException
+    {
+        double[] values = new double[6];
+        transform.getMatrix(values);
+        for (double v : values)
+        {
+            appendRawCommands(formatDecimal.format(v));
+            appendRawCommands(SPACE);
+        }
+    }
+
+    /**
      * Close the content stream.  This must be called when you are done with this
      * object.
      * @throws IOException If the underlying stream has a problem being written to.

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java?rev=1026228&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java Fri Oct 22 07:21:34 2010
@@ -0,0 +1,93 @@
+/*
+ * 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.pdmodel.graphics.optionalcontent;
+
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This class represents an optional content group (OCG).
+ *
+ * @since PDF 1.5
+ * @version $Revision$
+ */
+public class PDOptionalContentGroup implements COSObjectable
+{
+
+    private COSDictionary ocg;
+
+    /**
+     * Creates a new optional content group (OCG).
+     * @param name the name of the content group
+     */
+    public PDOptionalContentGroup(String name)
+    {
+        this.ocg = new COSDictionary();
+        this.ocg.setItem(COSName.TYPE, COSName.OCG);
+        setName(name);
+    }
+
+    /**
+     * Creates a new instance based on a given {@link COSDictionary}.
+     * @param dict the dictionary
+     */
+    public PDOptionalContentGroup(COSDictionary dict)
+    {
+        if (!dict.getItem(COSName.TYPE).equals(COSName.OCG))
+        {
+            throw new IllegalArgumentException(
+                    "Provided dictionary is not of type '" + COSName.OCG + "'");
+        }
+        this.ocg = dict;
+    }
+
+    /** {@inheritDoc} */
+    public COSBase getCOSObject()
+    {
+        return this.ocg;
+    }
+
+    /**
+     * Returns the name of the optional content group.
+     * @return the name
+     */
+    public String getName()
+    {
+        return this.ocg.getString(COSName.NAME);
+    }
+
+    /**
+     * Sets the name of the optional content group.
+     * @param name the name
+     */
+    public void setName(String name)
+    {
+        this.ocg.setString(COSName.NAME, name);
+    }
+
+    //TODO Add support for "Intent" and "Usage"
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString()
+    {
+        return super.toString() + " (" + getName() + ")";
+    }
+
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentGroup.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java?rev=1026228&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java Fri Oct 22 07:21:34 2010
@@ -0,0 +1,366 @@
+/*
+ * 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.pdmodel.graphics.optionalcontent;
+
+import java.util.Collection;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.pdmodel.common.COSObjectable;
+
+/**
+ * This class represents the optional content properties dictionary.
+ *
+ * @since PDF 1.5
+ * @version $Revision$
+ */
+public class PDOptionalContentProperties implements COSObjectable
+{
+
+    /**
+     * Enumeration for the BaseState dictionary entry on the "D" dictionary.
+     */
+    public static enum BaseState
+    {
+
+        /** The "ON" value. */
+        ON(COSName.ON),
+        /** The "OFF" value. */
+        OFF(COSName.OFF),
+        /** The "Unchanged" value. */
+        UNCHANGED(COSName.UNCHANGED);
+
+        private COSName name;
+
+        private BaseState(COSName value)
+        {
+            this.name = value;
+        }
+
+        /**
+         * Returns the PDF name for the state.
+         * @return the name of the state
+         */
+        public COSName getName()
+        {
+            return this.name;
+        }
+
+        /**
+         * Returns the base state represented by the given {@link COSName}.
+         * @param state the state name
+         * @return the state enum value
+         */
+        public static BaseState valueOf(COSName state)
+        {
+            if (state == null)
+            {
+                return BaseState.ON;
+            }
+            return BaseState.valueOf(state.getName().toUpperCase());
+        }
+
+    }
+
+    private COSDictionary dict;
+
+    /**
+     * Creates a new optional content properties dictionary.
+     */
+    public PDOptionalContentProperties()
+    {
+        this.dict = new COSDictionary();
+        this.dict.setItem(COSName.OCGS, new COSArray());
+        this.dict.setItem(COSName.D, new COSDictionary());
+    }
+
+    /**
+     * Creates a new instance based on a given {@link COSDictionary}.
+     * @param props the dictionary
+     */
+    public PDOptionalContentProperties(COSDictionary props)
+    {
+        this.dict = props;
+    }
+
+    /** {@inheritDoc} */
+    public COSBase getCOSObject()
+    {
+        return this.dict;
+    }
+
+    private COSArray getOCGs()
+    {
+        COSArray ocgs = (COSArray)this.dict.getItem(COSName.OCGS);
+        if (ocgs == null)
+        {
+            ocgs = new COSArray();
+            this.dict.setItem(COSName.OCGS, ocgs); //OCGs is required
+        }
+        return ocgs;
+    }
+
+    private COSDictionary getD()
+    {
+        COSDictionary d = (COSDictionary)this.dict.getDictionaryObject(COSName.D);
+        if (d == null)
+        {
+            d = new COSDictionary();
+            this.dict.setItem(COSName.D, d); //D is required
+        }
+        return d;
+    }
+
+    /**
+     * Returns the optional content group of the given name.
+     * @param name the group name
+     * @return the optional content group or null, if there is no such group
+     */
+    public PDOptionalContentGroup getGroup(String name)
+    {
+        COSArray ocgs = getOCGs();
+        for (COSBase o : ocgs)
+        {
+            COSDictionary ocg = toDictionary(o);
+            String groupName = ocg.getString(COSName.NAME);
+            if (groupName.equals(name))
+            {
+                return new PDOptionalContentGroup(ocg);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds an optional content group (OCG).
+     * @param ocg the optional content group
+     */
+    public void addGroup(PDOptionalContentGroup ocg)
+    {
+        COSArray ocgs = getOCGs();
+        ocgs.add(ocg.getCOSObject());
+
+        //By default, add new group to the "Order" entry so it appears in the user interface
+        COSArray order = (COSArray)getD().getDictionaryObject(COSName.ORDER);
+        if (order == null)
+        {
+            order = new COSArray();
+            getD().setItem(COSName.ORDER, order);
+        }
+        order.add(ocg);
+    }
+
+    /**
+     * Returns the collection of all optional content groups.
+     * @return the optional content groups
+     */
+    public Collection<PDOptionalContentGroup> getOptionalContentGroups()
+    {
+        Collection<PDOptionalContentGroup> coll = new java.util.ArrayList<PDOptionalContentGroup>();
+        COSArray ocgs = getOCGs();
+        for (COSBase base : ocgs)
+        {
+            COSObject obj = (COSObject)base; //Children must be indirect references
+            coll.add(new PDOptionalContentGroup((COSDictionary)obj.getObject()));
+        }
+        return coll;
+    }
+
+    /**
+     * Returns the base state for optional content groups.
+     * @return the base state
+     */
+    public BaseState getBaseState()
+    {
+        COSDictionary d = getD();
+        COSName name = (COSName)d.getItem(COSName.BASE_STATE);
+        return BaseState.valueOf(name);
+    }
+
+    /**
+     * Sets the base state for optional content groups.
+     * @param state the base state
+     */
+    public void setBaseState(BaseState state)
+    {
+        COSDictionary d = getD();
+        d.setItem(COSName.BASE_STATE, state.getName());
+    }
+
+    /**
+     * Lists all optional content group names.
+     * @return an array of all names
+     */
+    public String[] getGroupNames()
+    {
+        COSArray ocgs = (COSArray)dict.getDictionaryObject(COSName.OCGS);
+        int size = ocgs.size();
+        String[] groups = new String[size];
+        for (int i = 0; i < size; i++)
+        {
+            COSBase obj = (COSBase)ocgs.get(i);
+            COSDictionary ocg = toDictionary(obj);
+            groups[i] = ocg.getString(COSName.NAME);
+        }
+        return groups;
+    }
+
+    /**
+     * Indicates whether a particular optional content group is found in the PDF file.
+     * @param groupName the group name
+     * @return true if the group exists, false otherwise
+     */
+    public boolean hasGroup(String groupName)
+    {
+        String[] layers = getGroupNames();
+        for (String layer : layers)
+        {
+            if (layer.equals(groupName))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Indicates whether an optional content group is enabled.
+     * @param groupName the group name
+     * @return true if the group is enabled
+     */
+    public boolean isGroupEnabled(String groupName)
+    {
+        //TODO handle Optional Content Configuration Dictionaries,
+        //i.e. OCProperties/Configs
+
+        COSDictionary d = getD();
+        COSArray on = (COSArray)d.getDictionaryObject(COSName.ON);
+        if (on != null)
+        {
+            for (COSBase o : on)
+            {
+                COSDictionary group = toDictionary(o);
+                String name = group.getString(COSName.NAME);
+                if (name.equals(groupName))
+                {
+                    return true;
+                }
+            }
+        }
+
+        COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF);
+        if (off != null)
+        {
+            for (COSBase o : off)
+            {
+                COSDictionary group = toDictionary(o);
+                String name = group.getString(COSName.NAME);
+                if (name.equals(groupName))
+                {
+                    return false;
+                }
+            }
+        }
+
+        BaseState baseState = getBaseState();
+        boolean enabled = !baseState.equals(BaseState.OFF);
+        //TODO What to do with BaseState.Unchanged?
+        return enabled;
+    }
+
+    private COSDictionary toDictionary(COSBase o)
+    {
+        if (o instanceof COSObject)
+        {
+            return (COSDictionary)((COSObject)o).getObject();
+        }
+        else
+        {
+            return (COSDictionary)o;
+        }
+    }
+
+    /**
+     * Enables or disables an optional content group.
+     * @param groupName the group name
+     * @param enable true to enable, false to disable
+     * @return true if the group already had an on or off setting, false otherwise
+     */
+    public boolean setGroupEnabled(String groupName, boolean enable)
+    {
+        COSDictionary d = getD();
+        COSArray on = (COSArray)d.getDictionaryObject(COSName.ON);
+        if (on == null)
+        {
+            on = new COSArray();
+            d.setItem(COSName.ON, on);
+        }
+        COSArray off = (COSArray)d.getDictionaryObject(COSName.OFF);
+        if (off == null)
+        {
+            off = new COSArray();
+            d.setItem(COSName.OFF, off);
+        }
+
+        boolean found = false;
+        for (COSBase o : on)
+        {
+            COSDictionary group = toDictionary(o);
+            String name = group.getString(COSName.NAME);
+            if (!enable && name.equals(groupName))
+            {
+                //disable group
+                on.remove(group);
+                off.add(group);
+                found = true;
+                break;
+            }
+        }
+        for (COSBase o : off)
+        {
+            COSDictionary group = toDictionary(o);
+            String name = group.getString(COSName.NAME);
+            if (enable && name.equals(groupName))
+            {
+                //enable group
+                off.remove(group);
+                on.add(group);
+                found = true;
+                break;
+            }
+        }
+
+        if (!found)
+        {
+            PDOptionalContentGroup ocg = getGroup(groupName);
+            if (enable)
+            {
+                on.add(ocg.getCOSObject());
+            }
+            else
+            {
+                off.add(ocg.getCOSObject());
+            }
+        }
+        return found;
+    }
+
+
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/optionalcontent/PDOptionalContentProperties.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java Fri Oct 22 07:21:34 2010
@@ -16,12 +16,14 @@
  */
 package org.apache.pdfbox.pdmodel.graphics.xobject;
 
+import java.awt.geom.AffineTransform;
+
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSFloat;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSNumber;
 import org.apache.pdfbox.cos.COSStream;
-
 import org.apache.pdfbox.pdmodel.PDResources;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.common.PDStream;
@@ -145,13 +147,13 @@ public class PDXObjectForm extends PDXOb
             getCOSStream().setItem( COSName.BBOX, bbox.getCOSArray() );
         }
     }
-    
+
     /**
      * This will get the optional Matrix of an XObjectForm.
      * It maps the form space into the user space
      * @return the form matrix
      */
-    public Matrix getMatrix() 
+    public Matrix getMatrix()
     {
         Matrix retval = null;
         COSArray array = (COSArray)getCOSStream().getDictionaryObject( COSName.MATRIX );
@@ -167,5 +169,21 @@ public class PDXObjectForm extends PDXOb
         }
         return retval;
     }
-    
+
+    /**
+     * Sets the optional Matrix entry for the form XObject.
+     * @param transform the transformation matrix
+     */
+    public void setMatrix(AffineTransform transform)
+    {
+        COSArray matrix = new COSArray();
+        double[] values = new double[6];
+        transform.getMatrix(values);
+        for (double v : values)
+        {
+            matrix.add(new COSFloat((float)v));
+        }
+        getCOSStream().setItem(COSName.MATRIX, matrix);
+    }
+
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java?rev=1026228&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java Fri Oct 22 07:21:34 2010
@@ -0,0 +1,93 @@
+/*
+ * 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.pdmodel.markedcontent;
+
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.common.COSObjectable;
+import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup;
+
+/**
+ * This class represents a property list used for the marked content feature to map a resource name
+ * to a dictionary.
+ *
+ * @since PDF 1.2
+ * @version $Revision$
+ */
+public class PDPropertyList implements COSObjectable
+{
+
+    private COSDictionary props;
+
+    /**
+     * Creates a new property list.
+     */
+    public PDPropertyList()
+    {
+        this.props = new COSDictionary();
+    }
+
+    /**
+     * Creates a new instance based on a given {@link COSDictionary}.
+     * @param dict the dictionary
+     */
+    public PDPropertyList(COSDictionary dict)
+    {
+        this.props = dict;
+    }
+
+    /** {@inheritDoc} */
+    public COSBase getCOSObject()
+    {
+        return this.props;
+    }
+
+    /**
+     * Returns the optional content group belonging to the given resource name.
+     * @param name the resource name
+     * @return the optional content group or null if the group was not found
+     */
+    public PDOptionalContentGroup getOptionalContentGroup(COSName name)
+    {
+        COSDictionary dict = (COSDictionary)props.getDictionaryObject(name);
+        if (dict != null)
+        {
+            if (COSName.OCG.equals(dict.getItem(COSName.TYPE)))
+            {
+                return new PDOptionalContentGroup(dict);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Puts a mapping from a resource name to an optional content group.
+     * @param name the resource name
+     * @param ocg the optional content group
+     */
+    public void putMapping(COSName name, PDOptionalContentGroup ocg)
+    {
+        putMapping(name, (COSDictionary)ocg.getCOSObject());
+    }
+
+    private void putMapping(COSName name, COSDictionary dict)
+    {
+        props.setItem(name, dict);
+    }
+
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/markedcontent/PDPropertyList.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/persistence/util/COSObjectKey.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/persistence/util/COSObjectKey.java?rev=1026228&r1=1026227&r2=1026228&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/persistence/util/COSObjectKey.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/persistence/util/COSObjectKey.java Fri Oct 22 07:21:34 2010
@@ -24,7 +24,7 @@ import org.apache.pdfbox.cos.COSObject;
  * @author Michael Traut
  * @version $Revision: 1.5 $
  */
-public class COSObjectKey
+public class COSObjectKey implements Comparable<COSObjectKey>
 {
     private long number;
     private long generation;
@@ -113,4 +113,33 @@ public class COSObjectKey
     {
         return "" + getNumber() + " " + getGeneration() + " R";
     }
+
+    /** {@inheritDoc} */
+    public int compareTo(COSObjectKey other)
+    {
+        if (getNumber() < other.getNumber())
+        {
+            return -1;
+        }
+        else if (getNumber() > other.getNumber())
+        {
+            return 1;
+        }
+        else
+        {
+            if (getGeneration() < other.getGeneration())
+            {
+                return -1;
+            }
+            else if (getGeneration() > other.getGeneration())
+            {
+                return 1;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+    }
+
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/LayerUtility.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/LayerUtility.java?rev=1026228&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/LayerUtility.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/LayerUtility.java Fri Oct 22 07:21:34 2010
@@ -0,0 +1,320 @@
+/*
+ * 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.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.fontbox.util.BoundingBox;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSStream;
+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.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentGroup;
+import org.apache.pdfbox.pdmodel.graphics.optionalcontent.PDOptionalContentProperties;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.apache.pdfbox.pdmodel.markedcontent.PDPropertyList;
+
+/**
+ * This class allows to import pages as Form XObjects into a PDF file and use them to create
+ * layers (optional content groups).
+ *
+ * @version $Revision$
+ */
+public class LayerUtility
+{
+    private static final boolean DEBUG = true;
+
+    private PDDocument targetDoc;
+    private PDFCloneUtility cloner;
+
+    /**
+     * Creates a new instance.
+     * @param document the PDF document to modify
+     */
+    public LayerUtility(PDDocument document)
+    {
+        this.targetDoc = document;
+        this.cloner = new PDFCloneUtility(document);
+    }
+
+    /**
+     * Returns the PDF document we work on.
+     * @return the PDF document
+     */
+    public PDDocument getDocument()
+    {
+        return this.targetDoc;
+    }
+
+    /**
+     * Some applications may not wrap their page content in a save/restore (q/Q) pair which can
+     * lead to problems with coordinate system transformations when content is appended. This
+     * method lets you add a q/Q pair around the existing page's content.
+     * @param page the page
+     * @throws IOException if an I/O error occurs
+     */
+    public void wrapInSaveRestore(PDPage page) throws IOException
+    {
+        COSDictionary saveGraphicsStateDic = new COSDictionary();
+        COSStream saveGraphicsStateStream = new COSStream(saveGraphicsStateDic,
+                getDocument().getDocument().getScratchFile());
+        OutputStream saveStream = saveGraphicsStateStream.createUnfilteredStream();
+        saveStream.write("q\n".getBytes());
+        saveStream.flush();
+
+        COSStream restoreGraphicsStateStream = new COSStream(saveGraphicsStateDic,
+                getDocument().getDocument().getScratchFile());
+        OutputStream restoreStream = restoreGraphicsStateStream.createUnfilteredStream();
+        restoreStream.write("Q\n".getBytes());
+        restoreStream.flush();
+
+        //Wrap the existing page's content in a save/restore pair (q/Q) to have a controlled
+        //environment to add additional content.
+        COSDictionary pageDictionary = page.getCOSDictionary();
+        COSBase contents = pageDictionary.getDictionaryObject(COSName.CONTENTS);
+        if (contents instanceof COSStream)
+        {
+            COSStream contentsStream = (COSStream)contents;
+
+            COSArray array = new COSArray();
+            array.add(saveGraphicsStateStream);
+            array.add(contentsStream);
+            array.add(restoreGraphicsStateStream);
+
+            pageDictionary.setItem(COSName.CONTENTS, array);
+        }
+        else if( contents instanceof COSArray )
+        {
+            COSArray contentsArray = (COSArray)contents;
+
+            contentsArray.add(0, saveGraphicsStateStream);
+            contentsArray.add(restoreGraphicsStateStream);
+        }
+        else
+        {
+            throw new IOException("Contents are unknown type: " + contents.getClass().getName());
+        }
+    }
+
+    /**
+     * Imports a page from some PDF file as a Form XObject so it can be placed on another page
+     * in the target document.
+     * @param sourceDoc the source PDF document that contains the page to be copied
+     * @param pageNumber the page number of the page to be copied
+     * @return a Form XObject containing the original page's content
+     * @throws IOException if an I/O error occurs
+     */
+    public PDXObjectForm importPageAsForm(PDDocument sourceDoc, int pageNumber) throws IOException
+    {
+        PDPage page = (PDPage)sourceDoc.getDocumentCatalog().getAllPages().get(pageNumber);
+        return importPageAsForm(sourceDoc, page);
+    }
+
+    private static final Set<String> PAGE_TO_FORM_FILTER = new java.util.HashSet<String>(
+            Arrays.asList(new String[] {"Group", "LastModified", "Metadata"}));
+
+    /**
+     * Imports a page from some PDF file as a Form XObject so it can be placed on another page
+     * in the target document.
+     * @param sourceDoc the source PDF document that contains the page to be copied
+     * @param page the page in the source PDF document to be copied
+     * @return a Form XObject containing the original page's content
+     * @throws IOException if an I/O error occurs
+     */
+    public PDXObjectForm importPageAsForm(PDDocument sourceDoc, PDPage page) throws IOException
+    {
+        COSStream pageStream = (COSStream)page.getContents().getCOSObject();
+        PDStream newStream = new PDStream(targetDoc,
+                pageStream.getUnfilteredStream(), false);
+        PDXObjectForm form = new PDXObjectForm(newStream);
+
+        //Copy resources
+        PDResources pageRes = page.findResources();
+        PDResources formRes = new PDResources();
+        cloner.cloneMerge(pageRes, formRes);
+        form.setResources(formRes);
+
+        //Transfer some values from page to form
+        transferDict(page.getCOSDictionary(), form.getCOSStream(), PAGE_TO_FORM_FILTER, true);
+
+        Matrix matrix = form.getMatrix();
+        AffineTransform at = matrix != null ? matrix.createAffineTransform() : new AffineTransform();
+        PDRectangle mediaBox = page.findMediaBox();
+        PDRectangle cropBox = page.findCropBox();
+        PDRectangle viewBox = (cropBox != null ? cropBox : mediaBox);
+
+        //Handle the /Rotation entry on the page dict
+        int rotation = getNormalizedRotation(page);
+
+        //Transform to FOP's user space
+        //at.scale(1 / viewBox.getWidth(), 1 / viewBox.getHeight());
+        at.translate(mediaBox.getLowerLeftX() - viewBox.getLowerLeftX(),
+                mediaBox.getLowerLeftY() - viewBox.getLowerLeftY());
+        switch (rotation)
+        {
+        case 90:
+            at.scale(viewBox.getWidth() / viewBox.getHeight(), viewBox.getHeight() / viewBox.getWidth());
+            at.translate(0, viewBox.getWidth());
+            at.rotate(-Math.PI / 2.0);
+            break;
+        case 180:
+            at.translate(viewBox.getWidth(), viewBox.getHeight());
+            at.rotate(-Math.PI);
+            break;
+        case 270:
+            at.scale(viewBox.getWidth() / viewBox.getHeight(), viewBox.getHeight() / viewBox.getWidth());
+            at.translate(viewBox.getHeight(), 0);
+            at.rotate(-Math.PI * 1.5);
+        default:
+            //no additional transformations necessary
+        }
+        //Compensate for Crop Boxes not starting at 0,0
+        at.translate(-viewBox.getLowerLeftX(), -viewBox.getLowerLeftY());
+        if (!at.isIdentity())
+        {
+            form.setMatrix(at);
+        }
+
+        BoundingBox bbox = new BoundingBox();
+        bbox.setLowerLeftX(viewBox.getLowerLeftX());
+        bbox.setLowerLeftY(viewBox.getLowerLeftY());
+        bbox.setUpperRightX(viewBox.getUpperRightX());
+        bbox.setUpperRightY(viewBox.getUpperRightY());
+        form.setBBox(new PDRectangle(bbox));
+
+        return form;
+    }
+
+    /**
+     * Places the given form over the existing content of the indicated page (like an overlay).
+     * The form is enveloped in a marked content section to indicate that it's part of an
+     * optional content group (OCG), here used as a layer. This optional group is returned and
+     * can be enabled and disabled through methods on {@link PDOptionalContentProperties}.
+     * @param targetPage the target page
+     * @param form the form to place
+     * @param transform the transformation matrix that controls the placement
+     * @param layerName the name for the layer/OCG to produce
+     * @return the optional content group that was generated for the form usage
+     * @throws IOException if an I/O error occurs
+     */
+    public PDOptionalContentGroup appendFormAsLayer(PDPage targetPage,
+            PDXObjectForm form, AffineTransform transform,
+            String layerName) throws IOException
+    {
+        PDDocumentCatalog catalog = targetDoc.getDocumentCatalog();
+        PDOptionalContentProperties ocprops = catalog.getOCProperties();
+        if (ocprops == null)
+        {
+            ocprops = new PDOptionalContentProperties();
+            catalog.setOCProperties(ocprops);
+        }
+        if (ocprops.hasGroup(layerName))
+        {
+            throw new IllegalArgumentException("Optional group (layer) already exists: " + layerName);
+        }
+
+        PDOptionalContentGroup layer = new PDOptionalContentGroup(layerName);
+        ocprops.addGroup(layer);
+
+        PDResources resources = targetPage.findResources();
+        PDPropertyList props = resources.getProperties();
+        if (props == null)
+        {
+            props = new PDPropertyList();
+            resources.setProperties(props);
+        }
+
+        //Find first free resource name with the pattern "MC<index>"
+        int index = 0;
+        PDOptionalContentGroup ocg;
+        COSName resourceName;
+        do
+        {
+            resourceName = COSName.getPDFName("MC" + index);
+            ocg = props.getOptionalContentGroup(resourceName);
+            index++;
+        } while (ocg != null);
+        //Put mapping for our new layer/OCG
+        props.putMapping(resourceName, layer);
+
+        PDPageContentStream contentStream = new PDPageContentStream(
+                targetDoc, targetPage, true, !DEBUG);
+        contentStream.beginMarkedContentSequence(COSName.OC, resourceName);
+        contentStream.drawXObject(form, transform);
+        contentStream.endMarkedContentSequence();
+        contentStream.close();
+
+        return layer;
+    }
+
+    private void transferDict(COSDictionary orgDict, COSDictionary targetDict,
+            Set<String> filter, boolean inclusive) throws IOException
+    {
+        for (Map.Entry<COSName, COSBase> entry : orgDict.entrySet())
+        {
+            COSName key = entry.getKey();
+            if (inclusive && !filter.contains(key.getName()))
+            {
+                continue;
+            }
+            else if (!inclusive && filter.contains(key.getName()))
+            {
+                continue;
+            }
+            targetDict.setItem(key,
+                    cloner.cloneForNewDocument(entry.getValue()));
+        }
+    }
+
+    private static int getNormalizedRotation(PDPage page)
+    {
+        //Handle the /Rotation entry on the page dict
+        int rotation = page.findRotation();
+        while (rotation >= 360)
+        {
+            rotation -= 360;
+        }
+        if (rotation < 0)
+        {
+            rotation = 0;
+        }
+        switch (rotation)
+        {
+        case 90:
+        case 180:
+        case 270:
+            return rotation;
+        default:
+            return 0;
+        }
+    }
+
+
+}

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

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/LayerUtility.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFCloneUtility.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFCloneUtility.java?rev=1026228&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFCloneUtility.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFCloneUtility.java Fri Oct 22 07:21:34 2010
@@ -0,0 +1,244 @@
+/*
+ * 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.IOException;
+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.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.common.COSObjectable;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+
+/**
+ * Utility class used to clone PDF objects. It keeps track of objects it has already cloned.
+ *
+ * @version $Revision$
+ */
+public class PDFCloneUtility
+{
+
+    private PDDocument destination;
+    private Map<Object,COSBase> clonedVersion = new HashMap<Object,COSBase>();
+
+    /**
+     * Creates a new instance for the given target document.
+     * @param dest the destination PDF document that will receive the clones
+     */
+    public PDFCloneUtility(PDDocument dest)
+    {
+        this.destination = dest;
+    }
+
+    /**
+     * Returns the destination PDF document this cloner instance is set up for.
+     * @return the destination PDF document
+     */
+    public PDDocument getDestination()
+    {
+        return this.destination;
+    }
+
+    /**
+     * Deep-clones the given object for inclusion into a different PDF document identified by
+     * the destination parameter.
+     * @param base the initial object as the root of the deep-clone operation
+     * @return the cloned instance of the base object
+     * @throws IOException if an I/O error occurs
+     */
+      public COSBase cloneForNewDocument( Object base ) throws IOException
+      {
+          if( base == null )
+          {
+              return null;
+          }
+          COSBase retval = (COSBase)clonedVersion.get( base );
+          if( retval != null )
+          {
+              //we are done, it has already been converted.
+          }
+          else if( base instanceof List )
+          {
+              COSArray array = new COSArray();
+              List list = (List)base;
+              for( int i=0; i<list.size(); i++ )
+              {
+                  array.add( cloneForNewDocument( list.get( i ) ) );
+              }
+              retval = array;
+          }
+          else if( base instanceof COSObjectable && !(base instanceof COSBase) )
+          {
+              retval = cloneForNewDocument( ((COSObjectable)base).getCOSObject() );
+              clonedVersion.put( base, retval );
+          }
+          else if( base instanceof COSObject )
+          {
+              COSObject object = (COSObject)base;
+              retval = cloneForNewDocument( object.getObject() );
+              clonedVersion.put( base, retval );
+          }
+          else if( base instanceof COSArray )
+          {
+              COSArray newArray = new COSArray();
+              COSArray array = (COSArray)base;
+              for( int i=0; i<array.size(); i++ )
+              {
+                  newArray.add( cloneForNewDocument( array.get( i ) ) );
+              }
+              retval = newArray;
+              clonedVersion.put( base, retval );
+          }
+          else if( base instanceof COSStream )
+          {
+              COSStream originalStream = (COSStream)base;
+              PDStream stream = new PDStream( destination, originalStream.getFilteredStream(), true );
+              clonedVersion.put( base, stream.getStream() );
+              for( Map.Entry<COSName, COSBase> entry :  originalStream.entrySet() )
+              {
+                  stream.getStream().setItem(
+                          entry.getKey(),
+                          cloneForNewDocument(entry.getValue()));
+              }
+              retval = stream.getStream();
+          }
+          else if( base instanceof COSDictionary )
+          {
+              COSDictionary dic = (COSDictionary)base;
+              retval = new COSDictionary();
+              clonedVersion.put( base, retval );
+              for( Map.Entry<COSName, COSBase> entry : dic.entrySet() )
+              {
+                  ((COSDictionary)retval).setItem(
+                          entry.getKey(),
+                          cloneForNewDocument(entry.getValue()));
+              }
+          }
+          else
+          {
+              retval = (COSBase)base;
+          }
+          clonedVersion.put( base, retval );
+          return retval;
+      }
+
+
+      /**
+       * Merges two objects of the same type by deep-cloning its members.
+       * <br/>
+       * Base and target must be instances of the same class.
+       * @param base the base object to be cloned
+       * @param target the merge target
+       * @throws IOException if an I/O error occurs
+       */
+      public void cloneMerge( COSObjectable base, COSObjectable target) throws IOException
+      {
+          if( base == null )
+          {
+              return;
+          }
+          COSBase retval = (COSBase)clonedVersion.get( base );
+          if( retval != null )
+          {
+              return;
+              //we are done, it has already been converted. // ### Is that correct for cloneMerge???
+          }
+          else if( base instanceof List )
+          {
+              COSArray array = new COSArray();
+              List list = (List)base;
+              for( int i = 0; i < list.size(); i++ )
+              {
+                  array.add( cloneForNewDocument( list.get( i ) ) );
+              }
+              ((List)target).add(array);
+          }
+          else if( base instanceof COSObjectable && !(base instanceof COSBase) )
+          {
+              cloneMerge(((COSObjectable)base).getCOSObject(), ((COSObjectable)target).getCOSObject() );
+              clonedVersion.put( base, retval );
+          }
+          else if( base instanceof COSObject )
+          {
+              if(target instanceof COSObject)
+              {
+                  cloneMerge(((COSObject) base).getObject(),((COSObject) target).getObject() );
+              }
+              else if(target instanceof COSDictionary)
+              {
+                  cloneMerge(((COSObject)base).getObject(), ((COSDictionary)target));
+              }
+              clonedVersion.put( base, retval );
+          }
+          else if( base instanceof COSArray )
+          {
+              COSArray array = (COSArray)base;
+              for( int i=0; i<array.size(); i++ )
+              {
+                  ((COSArray)target).add( cloneForNewDocument( array.get( i ) ) );
+              }
+              clonedVersion.put( base, retval );
+          }
+          else if( base instanceof COSStream )
+          {
+            // does that make sense???
+              COSStream originalStream = (COSStream)base;
+              PDStream stream = new PDStream( destination, originalStream.getFilteredStream(), true );
+              clonedVersion.put( base, stream.getStream() );
+              for( Map.Entry<COSName, COSBase> entry : originalStream.entrySet() )
+              {
+                  stream.getStream().setItem(
+                          entry.getKey(),
+                          cloneForNewDocument(entry.getValue()));
+              }
+              retval = stream.getStream();
+              target = retval;
+          }
+          else if( base instanceof COSDictionary )
+          {
+              COSDictionary dic = (COSDictionary)base;
+              clonedVersion.put( base, retval );
+              for( Map.Entry<COSName, COSBase> entry : dic.entrySet() )
+              {
+                  COSName key = entry.getKey();
+                  COSBase value = entry.getValue();
+                  if (((COSDictionary)target).getItem(key) != null)
+                  {
+                      cloneMerge(value, ((COSDictionary)target).getItem(key));
+                  }
+                  else
+                  {
+                      ((COSDictionary)target).setItem( key, cloneForNewDocument(value));
+                  }
+              }
+          }
+          else
+          {
+              retval = (COSBase)base;
+          }
+          clonedVersion.put( base, retval );
+
+      }
+
+}

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

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/PDFCloneUtility.java
------------------------------------------------------------------------------
    svn:keywords = Id