]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2301: Enable support for PDF sub-page transitions.
authorGlenn Adams <gadams@apache.org>
Fri, 1 Nov 2013 14:34:18 +0000 (14:34 +0000)
committerGlenn Adams <gadams@apache.org>
Fri, 1 Nov 2013 14:34:18 +0000 (14:34 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1537948 13f79535-47bb-0310-9956-ffa450edef68

83 files changed:
src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
src/java/org/apache/fop/area/AreaTreeParser.java
src/java/org/apache/fop/area/Trait.java
src/java/org/apache/fop/fo/Constants.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/FObj.java
src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/TraitSetter.java
src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
src/java/org/apache/fop/pdf/AbstractPDFStream.java
src/java/org/apache/fop/pdf/PDFDictionary.java
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFFactory.java
src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFLayer.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFNavigator.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFNavigatorAction.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFPaintingState.java
src/java/org/apache/fop/pdf/PDFResources.java
src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFTransitionAction.java [new file with mode: 0644]
src/java/org/apache/fop/render/AbstractRenderer.java
src/java/org/apache/fop/render/afp/AFPPainter.java
src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
src/java/org/apache/fop/render/intermediate/IFGraphicContext.java
src/java/org/apache/fop/render/intermediate/IFPainter.java
src/java/org/apache/fop/render/intermediate/IFParser.java
src/java/org/apache/fop/render/intermediate/IFRenderer.java
src/java/org/apache/fop/render/intermediate/IFSerializer.java
src/java/org/apache/fop/render/java2d/Java2DPainter.java
src/java/org/apache/fop/render/java2d/Java2DRenderer.java
src/java/org/apache/fop/render/pcl/PCLPainter.java
src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
src/java/org/apache/fop/render/pdf/PDFPainter.java
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java [deleted file]
src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java [deleted file]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java [deleted file]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java [deleted file]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java
src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/ps/PSPainter.java
src/java/org/apache/fop/render/txt/TXTRenderer.java
src/java/org/apache/fop/render/xml/XMLRenderer.java
src/java/org/apache/fop/util/AbstractPaintingState.java
src/sandbox/org/apache/fop/render/svg/SVGPainter.java
status.xml
test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java
test/java/org/apache/fop/fonts/FontsTestSuite.java
test/java/org/apache/fop/pdf/PDFDestsTestCase.java
test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java
test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java
test/java/org/apache/fop/pdf/PDFStreamTestCase.java
test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java
test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java
test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml [new file with mode: 0644]

index d6f0c694c46649acd6c89fba4dec3ae0e07fd8ab..2e58a7f4545721d14f18192efac7961713455d0b 100644 (file)
@@ -38,6 +38,7 @@
           <xs:complexContent>
             <xs:extension base="mf:contentType">
               <xs:attribute name="transform" type="xs:string"/>
+              <xs:attribute name="layer" type="xs:string"/>
             </xs:extension>
           </xs:complexContent>
         </xs:complexType>
index c79f975c1e3bd949c32ce08c5dae3c2f8199ba4b..f6de99dd0a18e5e05848a1f50206a19b928fd597 100644 (file)
@@ -1040,7 +1040,7 @@ public class AreaTreeParser {
         }
 
         private static final Object[] SUBSET_COMMON = new Object[] {
-            Trait.PROD_ID};
+            Trait.PROD_ID, Trait.LAYER};
         private static final Object[] SUBSET_LINK = new Object[] {
             Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK};
         private static final Object[] SUBSET_COLOR = new Object[] {
index cd0d4becfe1b5097f52a20f83d13fd4fb1aeff76..eac9d440d1e02f07ffeba2bd230aa0f3bda95cbd 100644 (file)
@@ -169,9 +169,11 @@ public final class Trait implements Serializable {
     /** shift direction trait */
     public static final Integer SHIFT_DIRECTION = 42;
 
+    /** For optional content groups. */
+    public static final Integer LAYER = 43;
 
     /** Maximum value used by trait keys */
-    public static final int MAX_TRAIT_KEY = 42;
+    public static final int MAX_TRAIT_KEY = 43;
 
     private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1];
 
@@ -243,6 +245,7 @@ public final class Trait implements Serializable {
                 new TraitInfo("block-progression-direction", Direction.class));
         put(SHIFT_DIRECTION,
                 new TraitInfo("shift-direction", Direction.class));
+        put(LAYER, new TraitInfo("layer", String.class));
 
     }
 
index 28f02a7628d480c30cc438840b8899a3e72c496a..086cbca402d42c8fe072528259cdccb56bde1ca8 100644 (file)
@@ -816,8 +816,11 @@ public interface Constants {
     /** Scope for table header */
     int PR_X_HEADER_COLUMN = 290;
 
+    /** For specifying PDF optional content group (layer) binding. */
+    int PR_X_LAYER = 291;
+
     /** Number of property constants defined */
-    int PROPERTY_COUNT = 290;
+    int PROPERTY_COUNT = 291;
 
     // compound property constants
 
index 36e3f21c4843801681191421a80525c9d9b65b07..cc4fe9e2ce3cf6bb68fa5a873692c6bd5047b151 100644 (file)
@@ -2721,6 +2721,13 @@ public final class FOPropertyMapping implements Constants {
         m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO"));
         m.setDefault("auto");
         addPropertyMaker("z-index", m);
+
+        // fox:layer
+        m  = new StringProperty.Maker(PR_X_LAYER);
+        m.setInherited(false);
+        m.setDefault("");
+        addPropertyMaker("fox:layer", m);
+
     }
 
     private void createShorthandProperties() {
index b6f2ec83e1cfb493e76e08994bc3048ff56e7830..0d1a2a84befc7dd94d235cbc7a0f4ce409e193f8 100644 (file)
@@ -74,7 +74,8 @@ public abstract class FObj extends FONode implements Constants {
     private int bidiLevel = -1;
 
     // The value of properties relevant for all fo objects
-    private String id = null;
+    private String id;
+    private String layer;
     // End of property values
 
     /**
@@ -173,6 +174,7 @@ public abstract class FObj extends FONode implements Constants {
      */
     public void bind(PropertyList pList) throws FOPException {
         id = pList.get(PR_ID).getString();
+        layer = pList.get(PR_X_LAYER).getString();
     }
 
     /**
@@ -583,6 +585,16 @@ public abstract class FObj extends FONode implements Constants {
         return (id != null && id.length() > 0);
     }
 
+    /** @return the "layer" property. */
+    public String getLayer() {
+        return layer;
+    }
+
+    /** @return whether this object has an layer set */
+    public boolean hasLayer() {
+        return (layer != null && layer.length() > 0);
+    }
+
     /** {@inheritDoc} */
     public String getNamespaceURI() {
         return FOElementMapping.URI;
index 09b47f02a8fe369bd54df261e717b1699bee0ad7..82db43e59f46333488c5b252c09f28fa6ff56669 100644 (file)
@@ -66,6 +66,8 @@ public class ExtensionElementMapping extends ElementMapping {
         PROPERTY_ATTRIBUTES.add("border-before-end-radius");
         PROPERTY_ATTRIBUTES.add("border-after-start-radius");
         PROPERTY_ATTRIBUTES.add("border-after-end-radius");
+        //Optional content groups (layers)
+        PROPERTY_ATTRIBUTES.add("layer");
     }
 
     /**
index a23cd28f1c4ec574512d31a62beb14e1f74fdb79..eced48939833098268f5def703fe68527ce37b62 100644 (file)
@@ -867,6 +867,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
             transferForeignAttributes(viewportBlockArea);
 
             TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
+            TraitSetter.setLayer(viewportBlockArea, getBlockContainerFO().getLayer());
             TraitSetter.addBorders(viewportBlockArea,
                     getBlockContainerFO().getCommonBorderPaddingBackground(),
                     discardBorderBefore, discardBorderAfter, false, false, this);
index 55b17e953ba9382d819fe8ae47632cbc3f1b3497..b29b95cb10cb9453c878c5c3509b6180bcf4e000 100644 (file)
@@ -381,6 +381,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
                     getBlockFO().getCommonBorderPaddingBackground(),
                     startIndent, endIndent,
                     this);
+            TraitSetter.setLayer(curBlockArea, getBlockFO().getLayer());
 
             curBlockArea.setLocale(getBlockFO().getCommonHyphenation().getLocale());
             curBlockArea.setLocation(FONode.getLocatorString(getBlockFO().getLocator()));
index 739d535ca321f457ff93503beec5a4b3aec74f92..af40f06811da9e3d7746bd9a1621288a52732cb4 100644 (file)
@@ -617,4 +617,15 @@ public final class TraitSetter {
             area.addTrait(Trait.PROD_ID, id);
         }
     }
+
+    /**
+     * Sets the optional content group layer as a trait on the area.
+     * @param area the area to set the traits on
+     * @param layer the layer ID to set
+     */
+    public static void setLayer(Area area, String layer) {
+        if (layer != null && layer.length() > 0) {
+            area.addTrait(Trait.LAYER, layer);
+        }
+    }
 }
index dd80db1d1715b95356397caffb1a3f8e21d19e03..61dcf45c96abf3f36f8e2418170a9762fc8f5485 100644 (file)
@@ -213,6 +213,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
         }
         if (fobj instanceof Inline || fobj instanceof BasicLink) {
             TraitSetter.setProducerID(area, fobj.getId());
+            TraitSetter.setLayer(area, fobj.getLayer());
         }
         return area;
     }
index 41eed4885dda9a8689ca324747d110feedce9eb8..13bd1bda109ecda1372e054f336d238fbff45b5b 100644 (file)
@@ -143,7 +143,7 @@ public abstract class AbstractPDFStream extends PDFObject {
      */
     protected int outputStreamData(StreamCache encodedStream, OutputStream out) throws IOException {
         int length = 0;
-        byte[] p = encode("stream\n");
+        byte[] p = encode("\nstream\n");
         out.write(p);
         length += p.length;
 
@@ -186,7 +186,7 @@ public abstract class AbstractPDFStream extends PDFObject {
                 throws IOException {
         int bytesWritten = 0;
         //Stream header
-        byte[] buf = encode("stream\n");
+        byte[] buf = encode("\nstream\n");
         out.write(buf);
         bytesWritten += buf.length;
 
index 6bacd31a3410e00e0944667695e64155e4b93e5b..ae0b950fd385ecfbac26a1af85151cf6f3bf0704 100644 (file)
@@ -131,7 +131,7 @@ public class PDFDictionary extends PDFObject {
         } else {
             textBuffer.append('\n');
         }
-        textBuffer.append(">>\n");
+        textBuffer.append(">>");
     }
 
 }
index ff9f61201a4496bcac4ff52b8c21f50342105d8c..bcd54fcb91814c7a84530ccb5913d23e0fccfec9 100644 (file)
@@ -155,6 +155,12 @@ public class PDFDocument {
 
     private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
 
+    private List<PDFLayer> layers;
+
+    private List<PDFNavigator> navigators;
+
+    private List<PDFNavigatorAction> navigatorActions;
+
     private PDFFactory factory;
 
     private FileIDGenerator fileIDGenerator;
@@ -477,6 +483,24 @@ public class PDFDocument {
         if (obj instanceof PDFGoToRemote) {
             this.gotoremotes.add((PDFGoToRemote) obj);
         }
+        if (obj instanceof PDFLayer) {
+            if (this.layers == null) {
+                this.layers = new ArrayList<PDFLayer>();
+            }
+            this.layers.add((PDFLayer) obj);
+        }
+        if (obj instanceof PDFNavigator) {
+            if (this.navigators == null) {
+                this.navigators = new ArrayList<PDFNavigator>();
+            }
+            this.navigators.add((PDFNavigator) obj);
+        }
+        if (obj instanceof PDFNavigatorAction) {
+            if (this.navigatorActions == null) {
+                this.navigatorActions = new ArrayList<PDFNavigatorAction>();
+            }
+            this.navigatorActions.add((PDFNavigatorAction) obj);
+        }
     }
 
     /**
@@ -889,6 +913,34 @@ public class PDFDocument {
         this.accessibilityEnabled = enableAccessibility;
     }
 
+    /**
+     *
+     */
+    public PDFReference resolveExtensionReference(String id) {
+        if (layers != null) {
+            for (PDFLayer layer : layers) {
+                if (layer.hasId(id)) {
+                    return layer.makeReference();
+                }
+            }
+        }
+        if (navigators != null) {
+            for (PDFNavigator navigator : navigators) {
+                if (navigator.hasId(id)) {
+                    return navigator.makeReference();
+                }
+            }
+        }
+        if (navigatorActions != null) {
+            for (PDFNavigatorAction action : navigatorActions) {
+                if (action.hasId(id)) {
+                    return action.makeReference();
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Writes out the entire document
      *
@@ -1009,7 +1061,7 @@ public class PDFDocument {
         streamIndirectObjects(trailerObjects, stream);
         TrailerDictionary trailerDictionary = createTrailerDictionary();
         long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
-        String trailer = "startxref\n" + startxref + "\n%%EOF\n";
+        String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
         stream.write(encode(trailer));
     }
 
index 631499af11c65fdc4928955beb2c1221840f9820..5af33866e4e9f2cce628aa1656e873a7a77fcf05 100644 (file)
@@ -1821,4 +1821,28 @@ public class PDFFactory {
         return obj;
     }
 
+    public PDFLayer makeLayer(String id) {
+        PDFLayer layer = new PDFLayer(id);
+        getDocument().registerObject(layer);
+        return layer;
+    }
+
+    public PDFSetOCGStateAction makeSetOCGStateAction(String id) {
+        PDFSetOCGStateAction action = new PDFSetOCGStateAction(id);
+        getDocument().registerObject(action);
+        return action;
+    }
+
+    public PDFTransitionAction makeTransitionAction(String id) {
+        PDFTransitionAction action = new PDFTransitionAction(id);
+        getDocument().registerObject(action);
+        return action;
+    }
+
+    public PDFNavigator makeNavigator(String id) {
+        PDFNavigator navigator = new PDFNavigator(id);
+        getDocument().registerObject(navigator);
+        return navigator;
+    }
+
 }
diff --git a/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java
new file mode 100644 (file)
index 0000000..c2d033a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Identified Dictionary.
+ */
+public class PDFIdentifiedDictionary extends PDFDictionary {
+
+    private final String id;
+
+    public PDFIdentifiedDictionary(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    public boolean hasId(String id) {
+        return (this.id != null) && (id != null) && this.id.equals(id);
+    }
+
+}
+
diff --git a/src/java/org/apache/fop/pdf/PDFLayer.java b/src/java/org/apache/fop/pdf/PDFLayer.java
new file mode 100644 (file)
index 0000000..f8f434e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Optional Content Group Dictionary, which we will call a 'layer'.
+ */
+public class PDFLayer extends PDFIdentifiedDictionary {
+
+    public abstract static class Resolver {
+        private boolean resolved;
+        private PDFLayer layer;
+        private Object extension;
+        public Resolver(PDFLayer layer, Object extension) {
+            this.layer = layer;
+            this.extension = extension;
+        }
+        public PDFLayer getLayer() {
+            return layer;
+        }
+        public Object getExtension() {
+            return extension;
+        }
+        public void resolve() {
+            if (!resolved) {
+                performResolution();
+                resolved = true;
+            }
+        }
+        protected void performResolution() {
+        }
+    }
+
+    private Resolver resolver;
+
+    public PDFLayer(String id) {
+        super(id);
+        put("Type", new PDFName("OCG"));
+    }
+
+    @Override
+    public int output(OutputStream stream) throws IOException {
+        if (resolver != null) {
+            resolver.resolve();
+        }
+        return super.output(stream);
+    }
+
+    public void setResolver(Resolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public void populate(Object name, Object intent, Object usage) {
+        if (name != null) {
+            put("Name", name);
+        }
+        if (intent != null) {
+            put("Intent", intent);
+        }
+        if (usage != null) {
+            put("Usage", usage);
+        }
+    }
+
+}
+
diff --git a/src/java/org/apache/fop/pdf/PDFNavigator.java b/src/java/org/apache/fop/pdf/PDFNavigator.java
new file mode 100644 (file)
index 0000000..fdb9746
--- /dev/null
@@ -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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Navigation Node Dictionary, which we call a 'navigator'.
+ * This class is used to for sub-page navigation.
+ */
+public class PDFNavigator extends PDFIdentifiedDictionary {
+
+    public abstract static class Resolver {
+        private boolean resolved;
+        private PDFNavigator navigator;
+        private Object extension;
+        public Resolver(PDFNavigator navigator, Object extension) {
+            this.navigator = navigator;
+            this.extension = extension;
+        }
+        public PDFNavigator getNavigator() {
+            return navigator;
+        }
+        public Object getExtension() {
+            return extension;
+        }
+        public void resolve() {
+            if (!resolved) {
+                performResolution();
+                resolved = true;
+            }
+        }
+        protected void performResolution() {
+        }
+    }
+
+    private Resolver resolver;
+
+    public PDFNavigator(String id) {
+        super(id);
+        put("Type", new PDFName("NavNode"));
+    }
+
+    @Override
+    public int output(OutputStream stream) throws IOException {
+        if (resolver != null) {
+            resolver.resolve();
+        }
+        return super.output(stream);
+    }
+
+    public void setResolver(Resolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public void populate(Object nextAction, Object nextNode, Object prevAction, Object prevNode, Object duration) {
+        if (nextAction != null) {
+            put("NA", nextAction);
+        }
+        if (nextNode != null) {
+            put("Next", nextNode);
+        }
+        if (prevAction != null) {
+            put("PA", prevAction);
+        }
+        if (prevNode != null) {
+            put("Prev", prevNode);
+        }
+        if (duration != null) {
+            put("Dur", duration);
+        }
+    }
+
+}
+
diff --git a/src/java/org/apache/fop/pdf/PDFNavigatorAction.java b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java
new file mode 100644 (file)
index 0000000..ba32269
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+public abstract class PDFNavigatorAction extends PDFIdentifiedDictionary {
+
+    protected PDFNavigatorAction(String id) {
+        super(id);
+    }
+
+}
index 29d022f61f37e496498b6b8012cc24be7ca2794d..f6528a30c972c625021bb3fb0c725ea1e6d73aaa 100644 (file)
@@ -44,8 +44,6 @@ import org.apache.fop.util.AbstractPaintingState;
  * previous state then the necessary values can be overridden.
  * The current transform behaves differently to other values as the
  * matrix is combined with the current resolved value.
- * It is impossible to optimise the result without analysing the all
- * the possible combinations after completing.
  */
 public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState {
 
@@ -187,6 +185,36 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
         return newState;
     }
 
+    public void setLayer(String layer) {
+        getPDFData().setLayer(layer);
+    }
+
+    public String getLayer() {
+        return getPDFData().getLayer();
+    }
+
+    public boolean getLayerChanged() {
+        String layerCurrent = getLayer();
+        if (layerCurrent == null) {
+            return false;
+        } else if (getStateStack().isEmpty()) {
+            return true;
+        } else {
+            for (int i = getStackLevel(); i > 0; --i) {
+                String layerPrev = ((PDFData) getStateStack().get(i - 1)).getLayer();
+                if (layerPrev == null) {
+                    continue;
+                } else {
+                    // Both current and prior are set, so, if same, then we know layer
+                    // didn't change (and can stop search), otherwise it did change.
+                    return !layerCurrent.equals(layerPrev);
+                }
+            }
+            // Current layer set, but no prior saved layer set, so must have changed.
+            return true;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     protected AbstractData instantiateData() {
@@ -209,7 +237,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
         AbstractData data = getData();
         AbstractData copy = (AbstractData)data.clone();
         data.clearTransform();
-        getStateStack().add(copy);
+        getStateStack().push(copy);
     }
 
     private PDFData getPDFData() {
index cded7c00a97e2ae8e22e3200da8c40339070c192..6d09d57386f02602a217dd8fa8f767b5b24099af 100644 (file)
@@ -37,8 +37,8 @@ import org.apache.fop.fonts.base14.ZapfDingbats;
 /**
  * Class representing a /Resources object.
  *
- * /Resources object contain a list of references to the fonts for the
- * document
+ * /Resources object contain a list of references to the fonts, patterns,
+ * shadings, etc.,  for the document.
  */
 public class PDFResources extends PDFDictionary {
 
@@ -73,6 +73,9 @@ public class PDFResources extends PDFDictionary {
     /** Map of ICC color spaces (key: ICC profile description) */
     protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>();
 
+    /** Named properties */
+    protected Map<String, PDFReference> properties = new LinkedHashMap<String, PDFReference>();
+
     /**
      * create a /Resources object.
      *
@@ -191,6 +194,25 @@ public class PDFResources extends PDFDictionary {
         return cs;
     }
 
+    /**
+     * Add a named property.
+     *
+     * @param name name of property
+     * @param property reference to property value
+     */
+    public void addProperty(String name, PDFReference property) {
+        this.properties.put(name, property);
+    }
+
+    /**
+     * Get a named property.
+     *
+     * @param name name of property
+     */
+    public PDFReference getProperty(String name) {
+        return this.properties.get(name);
+    }
+
     @Override
     public int output(OutputStream stream) throws IOException {
         populateDictionary();
@@ -253,6 +275,14 @@ public class PDFResources extends PDFDictionary {
             }
             put("ColorSpace", dict);
         }
+
+        if (!properties.isEmpty()) {
+            PDFDictionary dict = new PDFDictionary(this);
+            for (String name : properties.keySet()) {
+                dict.put(name, properties.get(name));
+            }
+            put("Properties", dict);
+        }
     }
 
 }
diff --git a/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java
new file mode 100644 (file)
index 0000000..a47c5cd
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class PDFSetOCGStateAction extends PDFNavigatorAction {
+
+    public abstract static class Resolver {
+        private boolean resolved;
+        private PDFSetOCGStateAction action;
+        private Object extension;
+        public Resolver(PDFSetOCGStateAction action, Object extension) {
+            this.action = action;
+            this.extension = extension;
+        }
+        public PDFSetOCGStateAction getAction() {
+            return action;
+        }
+        public Object getExtension() {
+            return extension;
+        }
+        public void resolve() {
+            if (!resolved) {
+                performResolution();
+                resolved = true;
+            }
+        }
+        protected void performResolution() {
+        }
+    }
+
+    private Resolver resolver;
+
+    public PDFSetOCGStateAction(String id) {
+        super(id);
+        put("Type", new PDFName("Action"));
+        put("S", new PDFName("SetOCGState"));
+    }
+
+    @Override
+    public int output(OutputStream stream) throws IOException {
+        if (resolver != null) {
+            resolver.resolve();
+        }
+        return super.output(stream);
+    }
+
+    public void setResolver(Resolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public void populate(Object state, Object preserveRB, Object nextAction) {
+        if (state != null) {
+            put("State", state);
+        }
+        if (preserveRB != null) {
+            put("PreserveRB", preserveRB);
+        }
+        if (nextAction != null) {
+            put("Next", nextAction);
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/pdf/PDFTransitionAction.java b/src/java/org/apache/fop/pdf/PDFTransitionAction.java
new file mode 100644 (file)
index 0000000..01f8fcf
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class PDFTransitionAction extends PDFNavigatorAction {
+
+    public abstract static class Resolver {
+        private boolean resolved;
+        private PDFTransitionAction action;
+        private Object extension;
+        public Resolver(PDFTransitionAction action, Object extension) {
+            this.action = action;
+            this.extension = extension;
+        }
+        public PDFTransitionAction getAction() {
+            return action;
+        }
+        public Object getExtension() {
+            return extension;
+        }
+        public void resolve() {
+            if (!resolved) {
+                performResolution();
+                resolved = true;
+            }
+        }
+        protected void performResolution() {
+        }
+    }
+
+    private Resolver resolver;
+
+    public PDFTransitionAction(String id) {
+        super(id);
+        put("Type", new PDFName("Action"));
+        put("S", new PDFName("Trans"));
+    }
+
+    @Override
+    public int output(OutputStream stream) throws IOException {
+        if (resolver != null) {
+            resolver.resolve();
+        }
+        return super.output(stream);
+    }
+
+    public void setResolver(Resolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public void populate(Object transition, Object nextAction) {
+        if (transition != null) {
+            put("Trans", transition);
+        }
+        if (nextAction != null) {
+            put("Next", nextAction);
+        }
+    }
+}
index e274e5c4b8bb09921feddc3b5783e952aa218f53..9a94e0cc62e63e796a68d86a8bb91834f8fdef3b 100644 (file)
@@ -28,6 +28,7 @@ import java.io.OutputStream;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
+import java.util.Stack;
 
 import org.w3c.dom.Document;
 
@@ -112,8 +113,12 @@ public abstract class AbstractRenderer
     /** the currently active PageViewport */
     protected PageViewport currentPageViewport;
 
+    /* warned XML handlers */
     private Set warnedXMLHandlers;
 
+    /* layers stack */
+    private Stack<String> layers;
+
     /** {@inheritDoc} */
     public abstract void setupFontInfo(FontInfo fontInfo) throws FOPException;
 
@@ -471,6 +476,10 @@ public abstract class AbstractRenderer
      * @param children  The children to render within the block viewport
      */
     protected void renderBlockViewport(BlockViewport bv, List children) {
+        boolean inNewLayer = false;
+        if (maybeStartLayer(bv)) {
+            inNewLayer = true;
+        }
         // clip and position viewport if necessary
         if (bv.getPositioning() == Block.ABSOLUTE) {
             // save positions
@@ -506,6 +515,7 @@ public abstract class AbstractRenderer
             currentIPPosition = saveIP;
             currentBPPosition = saveBP + bv.getAllocBPD();
         }
+        maybeEndLayer(bv, inNewLayer);
     }
 
     /**
@@ -573,6 +583,10 @@ public abstract class AbstractRenderer
     protected void renderBlock(Block block) {
         assert block != null;
         List children = block.getChildAreas();
+        boolean inNewLayer = false;
+        if (maybeStartLayer(block)) {
+            inNewLayer = true;
+        }
         if (block instanceof BlockViewport) {
             if (children != null) {
                 renderBlockViewport((BlockViewport) block, children);
@@ -607,6 +621,45 @@ public abstract class AbstractRenderer
                 currentBPPosition = saveBP + block.getAllocBPD();
             }
         }
+        maybeEndLayer(block, inNewLayer);
+    }
+
+    /**
+     * Establish new optional content group layer.
+     * @param layer name of layer
+     */
+    protected abstract void startLayer(String layer);
+
+    /**
+     * Finish current optional content group layer.
+     */
+    protected abstract void endLayer();
+
+    protected boolean maybeStartLayer(Area area) {
+        String layer = (String) area.getTrait(Trait.LAYER);
+        if (layer != null) {
+            if (layers == null) {
+                layers = new Stack<String>();
+            }
+            if (layers.empty() || !layers.peek().equals(layer)) {
+                layers.push(layer);
+                startLayer(layer);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected void maybeEndLayer(Area area, boolean inNewLayer) {
+        if (inNewLayer) {
+            assert layers != null;
+            assert !layers.empty();
+            String layer = (String) area.getTrait(Trait.LAYER);
+            assert layer != null;
+            assert layers.peek().equals(layer);
+            endLayer();
+            layers.pop();
+        }
     }
 
     /**
@@ -746,6 +799,10 @@ public abstract class AbstractRenderer
      * @param ip the inline parent to render
      */
     protected void renderInlineParent(InlineParent ip) {
+        boolean inNewLayer = false;
+        if (maybeStartLayer(ip)) {
+            inNewLayer = true;
+        }
         int level = ip.getBidiLevel();
         List children = ip.getChildAreas();
         renderInlineAreaBackAndBorders(ip);
@@ -782,6 +839,7 @@ public abstract class AbstractRenderer
         }
         currentIPPosition = saveIP + ip.getAllocIPD();
         currentBPPosition = saveBP;
+        maybeEndLayer(ip, inNewLayer);
     }
 
     /**
index 12713bb24ca61cf1f87c4ed38e005bf90d3fe9e5..aaa702afa62358fbb0c0884e075a81585d537343 100644 (file)
@@ -164,7 +164,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
         try {
             saveGraphicsState();
             concatenateTransformationMatrix(transform);
index 2dd046fa9bb850e3167dd7e5a65840470b6ed62c..f69fe20914fb228c00b53bf770f37da962d9da73 100644 (file)
@@ -126,8 +126,8 @@ public abstract class AbstractIFPainter<T extends IFDocumentHandler> implements
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform[] transforms) throws IFException {
-        startGroup(combine(transforms));
+    public void startGroup(AffineTransform[] transforms, String layer) throws IFException {
+        startGroup(combine(transforms), layer);
     }
 
     /**
index 868615360fd347f5bd23bdeb101395d2f5fe75d6..c1742be1ff2088fae1452ff35ec7bbf8c0c60eeb 100644 (file)
@@ -89,6 +89,7 @@ public class IFGraphicContext extends GraphicContext {
     public static class Group {
 
         private AffineTransform[] transforms;
+        private String layer;
 
         /**
          * Construct a Group.
@@ -106,6 +107,16 @@ public class IFGraphicContext extends GraphicContext {
             this(new AffineTransform[] {transform});
         }
 
+        /**
+         * Construct a layer Group, i.e., a Group with no transforms
+         * but with a optional content group layer label.
+         * @param layer a layer label
+         */
+        public Group(String layer) {
+            this();
+            this.layer = layer;
+        }
+
         /** Default constructor. */
         public Group() {
             this(EMPTY_TRANSFORM_ARRAY);
@@ -116,12 +127,17 @@ public class IFGraphicContext extends GraphicContext {
             return this.transforms;
         }
 
+        /** @return layer */
+        public String getLayer() {
+            return this.layer;
+        }
+
         /**
          * @param painter a painter
          * @throws IFException in not caught
          */
         public void start(IFPainter painter) throws IFException {
-            painter.startGroup(transforms);
+            painter.startGroup(transforms, layer);
         }
 
         /**
@@ -136,6 +152,11 @@ public class IFGraphicContext extends GraphicContext {
         public String toString() {
             StringBuffer sb = new StringBuffer("group: ");
             IFUtil.toString(getTransforms(), sb);
+            if ((layer != null) && (layer.length() > 0)) {
+                sb.append(" layer(");
+                sb.append(layer);
+                sb.append(')');
+            }
             return sb.toString();
         }
 
index 59929228751e16feee08bbd7a10d653170c202aa..d43b5cb85b44cb1cf0c1089f19c57916e00b3662 100644 (file)
@@ -113,19 +113,21 @@ public interface IFPainter {
     /**
      * Starts a new group of graphical elements. Corresponds to SVG's g element.
      * @param transforms a series of transformation matrices establishing the new coordinate system
+     * @param layer an optional layer label (or null if none)
      * @throws IFException if an error occurs while handling this element
      */
-    void startGroup(AffineTransform[] transforms) throws IFException;
+    void startGroup(AffineTransform[] transforms, String layer) throws IFException;
 
     /**
      * Starts a new group of graphical elements. Corresponds to SVG's g element.
      * @param transform the transformation matrix establishing the new coordinate system
+     * @param layer an optional layer label (or null if none)
      * @throws IFException if an error occurs while handling this element
      */
-    void startGroup(AffineTransform transform) throws IFException;
+    void startGroup(AffineTransform transform, String layer) throws IFException;
 
     /**
-     * Ends the current group and restores the previous coordinate system.
+     * Ends the current group and restores the previous coordinate system (and layer).
      * @throws IFException if an error occurs while handling this element
      */
     void endGroup() throws IFException;
index 1af1d4a0677e5825f3aed138d6a0984cd70c243c..519726291dac488e7eca4d775993f8540a87997a 100644 (file)
@@ -592,7 +592,8 @@ public class IFParser implements IFConstants {
                 String transform = attributes.getValue("transform");
                 AffineTransform[] transforms
                     = AffineTransformArrayParser.createAffineTransform(transform);
-                painter.startGroup(transforms);
+                String layer = attributes.getValue("layer");
+                painter.startGroup(transforms, layer);
             }
 
             public void endElement() throws IFException {
@@ -800,8 +801,8 @@ public class IFParser implements IFConstants {
                     }
                     painter.drawImage(uri, new Rectangle(x, y, width, height));
                 }
-                resetForeignAttributes();
                 resetStructureTreeElement();
+                resetForeignAttributes();
                 inForeignObject = false;
             }
 
index 30ceda10869d444b2336af46062d0b9842776e4c..e40a8f6b3df7d2441e7d650fda7e7e94a90ba0ee 100644 (file)
@@ -742,6 +742,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
     protected void renderBlockViewport(BlockViewport bv, List children) {
         //Essentially the same code as in the super class but optimized for the IF
 
+        // Handle new layer.
+        boolean inNewLayer = false;
+        if (maybeStartLayer(bv)) {
+            inNewLayer = true;
+        }
+
         //This is the content-rect
         Dimension dim = new Dimension(bv.getIPD(), bv.getBPD());
         viewportDimensionStack.push(dim);
@@ -842,6 +848,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
             currentBPPosition += bv.getAllocBPD();
         }
         viewportDimensionStack.pop();
+        maybeEndLayer(bv, inNewLayer);
     }
 
     /** {@inheritDoc} */
@@ -849,7 +856,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
         StructureTreeElement structElem
                 = (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT);
         establishStructureTreeElement(structElem);
-        pushdID(viewport);
+        pushID(viewport);
         Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD());
         viewportDimensionStack.push(dim);
         super.renderInlineViewport(viewport);
@@ -895,10 +902,27 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
         restoreGraphicsState();
     }
 
+    /** {@inheritDoc} */
+    protected void startLayer(String layer) {
+        if (log.isTraceEnabled()) {
+            log.trace("startLayer() layer=" + layer);
+        }
+        saveGraphicsState();
+        pushGroup(new IFGraphicContext.Group(layer));
+    }
+
+    /** {@inheritDoc} */
+    protected void endLayer() {
+        if (log.isTraceEnabled()) {
+            log.trace("endLayer()");
+        }
+        restoreGraphicsState();
+    }
+
     /** {@inheritDoc} */
     protected void renderInlineArea(InlineArea inlineArea) {
         saveInlinePosIfTargetable(inlineArea);
-        pushdID(inlineArea);
+        pushID(inlineArea);
         super.renderInlineArea(inlineArea);
         popID(inlineArea);
     }
@@ -965,7 +989,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
             log.trace("renderBlock() " + block);
         }
         saveBlockPosIfTargetable(block);
-        pushdID(block);
+        pushID(block);
         IFContext context = documentHandler.getContext();
         Locale oldLocale = context.getLanguage();
         context.setLanguage(block.getLocale());
@@ -977,7 +1001,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
         popID(block);
     }
 
-    private void pushdID(Area area) {
+    private void pushID(Area area) {
         String prodID = (String) area.getTrait(Trait.PROD_ID);
         if (prodID != null) {
             ids.push(prodID);
index 1ffd428633c6af0ac6b1b6f95c29f8b8614553c0..395e797193a359dc3b50f20ca0f220aac02eb095 100644 (file)
@@ -428,21 +428,24 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform[] transforms) throws IFException {
-        startGroup(IFUtil.toString(transforms));
+    public void startGroup(AffineTransform[] transforms, String layer) throws IFException {
+        startGroup(IFUtil.toString(transforms), layer);
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
-        startGroup(IFUtil.toString(transform));
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
+        startGroup(IFUtil.toString(transform), layer);
     }
 
-    private void startGroup(String transform) throws IFException {
+    private void startGroup(String transform, String layer) throws IFException {
         try {
             AttributesImpl atts = new AttributesImpl();
             if (transform != null && transform.length() > 0) {
                 addAttribute(atts, "transform", transform);
             }
+            if (layer != null && layer.length() > 0) {
+                addAttribute(atts, "layer", layer);
+            }
             handler.startElement(EL_GROUP, atts);
         } catch (SAXException e) {
             throw new IFException("SAX error in startGroup()", e);
index 07440ff0b03d711f58c6b99728e9eb2f044ab531..328d1a4f858637770c874e5045ed9758aa64a0a0 100644 (file)
@@ -143,7 +143,7 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> {
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
         saveGraphicsState();
         try {
             concatenateTransformationMatrix(transform);
index a98aff353c62623c04c0bbfd677abd5722fe7fdd..d343c55d8a797feb21b6bc56893b9d574b66e801 100644 (file)
@@ -475,6 +475,14 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
         restoreGraphicsState();
     }
 
+    /** {@inheritDoc} */
+    protected void startLayer(String layer) {
+    }
+
+    /** {@inheritDoc} */
+    protected void endLayer() {
+    }
+
     /** {@inheritDoc} */
     protected List breakOutOfStateStack() {
         log.debug("Block.FIXED --> break out");
index c51ee834f82ac2a4e4c338965ade13751bb05dd6..f934eed8c07d54c0b74cc715d5e9961c5ed1dc5c 100644 (file)
@@ -126,7 +126,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
         saveGraphicsState();
         try {
             concatenateTransformationMatrix(transform);
index dde6b0ef3c76cf6307dff1b6077a97995c460840..ac7b1d905195fdb7480740354a339eaff6cb02b2 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFFilterList;
 import org.apache.fop.pdf.PDFNumber;
 import org.apache.fop.pdf.PDFPaintingState;
+import org.apache.fop.pdf.PDFReference;
 import org.apache.fop.pdf.PDFResourceContext;
 import org.apache.fop.pdf.PDFStream;
 import org.apache.fop.pdf.PDFText;
@@ -55,7 +56,7 @@ public class PDFContentGenerator {
     private PDFColorHandler colorHandler;
 
     /** drawing state */
-    protected PDFPaintingState currentState = null;
+    protected PDFPaintingState currentState;
     /** Text generation utility holding the current font status */
     protected PDFTextUtil textutil;
 
@@ -156,15 +157,23 @@ public class PDFContentGenerator {
      */
     protected void comment(String text) {
         if (WRITE_COMMENTS) {
-            currentStream.add("% " + text + "\n");
+            getStream().add("% " + text + "\n");
         }
     }
 
     /** Save graphics state. */
     protected void saveGraphicsState() {
         endTextObject();
-        currentState.save();
-        currentStream.add("q\n");
+        getState().save();
+        getStream().add("q\n");
+    }
+
+    /** Save graphics state with optional layer. */
+    protected void saveGraphicsState(String layer) {
+        endTextObject();
+        getState().save();
+        maybeBeginLayer(layer);
+        getStream().add("q\n");
     }
 
     /**
@@ -174,9 +183,9 @@ public class PDFContentGenerator {
      */
     protected void saveGraphicsState(String structElemType, int sequenceNum) {
         endTextObject();
-        currentState.save();
+        getState().save();
         beginMarkedContentSequence(structElemType, sequenceNum);
-        currentStream.add("q\n");
+        getStream().add("q\n");
     }
 
     /**
@@ -208,18 +217,18 @@ public class PDFContentGenerator {
         if (structElemType != null) {
             String actualTextProperty = actualText == null ? ""
                     : " /ActualText " + PDFText.escapeText(actualText);
-            currentStream.add(structElemType + " <</MCID " + String.valueOf(mcid)
+            getStream().add(structElemType + " <</MCID " + String.valueOf(mcid)
                     + actualTextProperty + ">>\n"
                     + "BDC\n");
         } else {
-            currentStream.add("/Artifact\nBMC\n");
+            getStream().add("/Artifact\nBMC\n");
             this.inArtifactMode = true;
         }
         this.inMarkedContentSequence = true;
     }
 
     void endMarkedContentSequence() {
-        currentStream.add("EMC\n");
+        getStream().add("EMC\n");
         this.inMarkedContentSequence = false;
         this.inArtifactMode = false;
     }
@@ -231,9 +240,10 @@ public class PDFContentGenerator {
      */
     protected void restoreGraphicsState(boolean popState) {
         endTextObject();
-        currentStream.add("Q\n");
+        getStream().add("Q\n");
+        maybeEndLayer();
         if (popState) {
-            currentState.restore();
+            getState().restore();
         }
     }
 
@@ -251,11 +261,42 @@ public class PDFContentGenerator {
      */
     protected void restoreGraphicsStateAccess() {
         endTextObject();
-        currentStream.add("Q\n");
+        getStream().add("Q\n");
         if (this.inMarkedContentSequence) {
             endMarkedContentSequence();
         }
-        currentState.restore();
+        getState().restore();
+    }
+
+    private void maybeBeginLayer(String layer) {
+        if ((layer != null) && (layer.length() > 0)) {
+            getState().setLayer(layer);
+            beginOptionalContent(layer);
+        }
+    }
+
+    private void maybeEndLayer() {
+        if (getState().getLayerChanged()) {
+            endOptionalContent();
+        }
+    }
+
+    private int ocNameIndex = 0;
+
+    private void beginOptionalContent(String layerId) {
+        String name;
+        PDFReference layer = document.resolveExtensionReference(layerId);
+        if (layer != null) {
+            name = "oc" + ++ocNameIndex;
+            document.getResources().addProperty(name, layer);
+        } else {
+            name = "unknown";
+        }
+        getStream().add("/OC /" + name + " BDC\n");
+    }
+
+    private void endOptionalContent() {
+        getStream().add("EMC\n");
     }
 
     /** Indicates the beginning of a text object. */
@@ -310,8 +351,8 @@ public class PDFContentGenerator {
     public void concatenate(AffineTransform transform) {
         this.transform = transform;
         if (!transform.isIdentity()) {
-            currentState.concatenate(transform);
-            currentStream.add(CTMHelper.toPDFString(transform, false) + " cm\n");
+            getState().concatenate(transform);
+            getStream().add(CTMHelper.toPDFString(transform, false) + " cm\n");
         }
     }
 
@@ -333,7 +374,7 @@ public class PDFContentGenerator {
      * @param content the PDF content
      */
     public void add(String content) {
-        currentStream.add(content);
+        getStream().add(content);
     }
 
     /**
@@ -350,9 +391,9 @@ public class PDFContentGenerator {
      * @param width line width in points
      */
     public void updateLineWidth(float width) {
-        if (currentState.setLineWidth(width)) {
+        if (getState().setLineWidth(width)) {
             //Only write if value has changed WRT the current line width
-            currentStream.add(format(width) + " w\n");
+            getStream().add(format(width) + " w\n");
         }
     }
 
@@ -362,7 +403,7 @@ public class PDFContentGenerator {
      */
     public void updateCharacterSpacing(float value) {
         if (getState().setCharacterSpacing(value)) {
-            currentStream.add(format(value) + " Tc\n");
+            getStream().add(format(value) + " Tc\n");
         }
     }
 
@@ -400,7 +441,7 @@ public class PDFContentGenerator {
         if (pdf != null) {
             colorHandler.establishColor(pdf, col, fill);
         } else {
-            setColor(col, fill, this.currentStream);
+            setColor(col, fill, getStream());
         }
     }
 
index 0a3d34ef4afdc893e72b6f0f198ecceb7ac9ead2..648cdce7aeb674abab3080ad1126bd3271d92639 100644 (file)
@@ -306,8 +306,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
                 throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe);
             }
         } else if (extension instanceof PDFDictionaryAttachment) {
-            PDFDictionaryAttachment dictionaryExtension = (PDFDictionaryAttachment) extension;
-            pdfUtil.renderDictionaryExtension(dictionaryExtension, currentPage);
+            pdfUtil.renderDictionaryExtension((PDFDictionaryAttachment) extension, currentPage);
         } else if (extension != null) {
             log.debug("Don't know how to handle extension object. Ignoring: "
                     + extension + " (" + extension.getClass().getName() + ")");
index d9fdd399cf664e0e675b303a93e9154a67f06ae6..f85328b8b55814ccb61543dc2a2cb81fed95e65e 100644 (file)
@@ -143,8 +143,8 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
-        generator.saveGraphicsState();
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
+        generator.saveGraphicsState(layer);
         generator.concatenate(toPoints(transform));
     }
 
index 962cd784727b82bd6dc3c9d0587695d55f4559b8..d27fead120c0f2a8a46d51dc914a156b25f4e5f9 100644 (file)
@@ -27,6 +27,7 @@ import java.io.OutputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.EnumMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.io.IOUtils;
@@ -55,24 +56,33 @@ import org.apache.fop.pdf.PDFFileSpec;
 import org.apache.fop.pdf.PDFICCBasedColorSpace;
 import org.apache.fop.pdf.PDFICCStream;
 import org.apache.fop.pdf.PDFInfo;
+import org.apache.fop.pdf.PDFLayer;
 import org.apache.fop.pdf.PDFMetadata;
 import org.apache.fop.pdf.PDFName;
 import org.apache.fop.pdf.PDFNames;
+import org.apache.fop.pdf.PDFNavigator;
+import org.apache.fop.pdf.PDFNull;
 import org.apache.fop.pdf.PDFNumber;
 import org.apache.fop.pdf.PDFOutputIntent;
 import org.apache.fop.pdf.PDFPage;
 import org.apache.fop.pdf.PDFPageLabels;
 import org.apache.fop.pdf.PDFReference;
+import org.apache.fop.pdf.PDFSetOCGStateAction;
 import org.apache.fop.pdf.PDFText;
+import org.apache.fop.pdf.PDFTransitionAction;
 import org.apache.fop.pdf.PDFXMode;
 import org.apache.fop.pdf.Version;
 import org.apache.fop.pdf.VersionController;
+import org.apache.fop.render.pdf.extensions.PDFActionExtension;
+import org.apache.fop.render.pdf.extensions.PDFArrayExtension;
+import org.apache.fop.render.pdf.extensions.PDFCollectionEntryExtension;
 import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment;
-import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryExtension;
-import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryType;
 import org.apache.fop.render.pdf.extensions.PDFDictionaryExtension;
 import org.apache.fop.render.pdf.extensions.PDFDictionaryType;
 import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment;
+import org.apache.fop.render.pdf.extensions.PDFObjectType;
+import org.apache.fop.render.pdf.extensions.PDFPageExtension;
+import org.apache.fop.render.pdf.extensions.PDFReferenceExtension;
 
 import static org.apache.fop.render.pdf.PDFEncryptionOption.ENCRYPTION_PARAMS;
 import static org.apache.fop.render.pdf.PDFEncryptionOption.NO_ACCESSCONTENT;
@@ -260,10 +270,189 @@ class PDFRenderingUtil {
 
     public void renderDictionaryExtension(PDFDictionaryAttachment attachment, PDFPage currentPage) {
         PDFDictionaryExtension extension = attachment.getExtension();
-        if (extension.getDictionaryType() == PDFDictionaryType.Catalog) {
+        PDFDictionaryType type = extension.getDictionaryType();
+        if (type == PDFDictionaryType.Action) {
+            addNavigatorAction(extension);
+        } else if (type == PDFDictionaryType.Layer) {
+            addLayer(extension);
+        } else if (type == PDFDictionaryType.Navigator) {
+            addNavigator(extension);
+        } else {
+            renderDictionaryExtension(extension, currentPage);
+        }
+    }
+
+    public void addLayer(PDFDictionaryExtension extension) {
+        assert extension.getDictionaryType() == PDFDictionaryType.Layer;
+        String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+        if ((id != null) && (id.length() > 0)) {
+            PDFLayer layer = pdfDoc.getFactory().makeLayer(id);
+            layer.setResolver(new PDFLayer.Resolver(layer, extension) {
+                public void performResolution() {
+                    PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+                    Object name = extension.findEntryValue("Name");
+                    Object intent = extension.findEntryValue("Intent");
+                    Object usage = makeDictionary(extension.findEntryValue("Usage"));
+                    getLayer().populate(name, intent, usage);
+                }
+            });
+        }
+    }
+
+    public void addNavigatorAction(PDFDictionaryExtension extension) {
+        assert extension.getDictionaryType() == PDFDictionaryType.Action;
+        String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+        if ((id != null) && (id.length() > 0)) {
+            String type = extension.getProperty(PDFActionExtension.PROPERTY_TYPE);
+            if (type != null) {
+                if (type.equals("SetOCGState")) {
+                    PDFSetOCGStateAction action = pdfDoc.getFactory().makeSetOCGStateAction(id);
+                    action.setResolver(new PDFSetOCGStateAction.Resolver(action, extension) {
+                        public void performResolution() {
+                            PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+                            Object state = makeArray(extension.findEntryValue("State"));
+                            Object preserveRB = extension.findEntryValue("PreserveRB");
+                            Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next"));
+                            getAction().populate(state, preserveRB, nextAction);
+                        }
+                    });
+                } else if (type.equals("Trans")) {
+                    PDFTransitionAction action = pdfDoc.getFactory().makeTransitionAction(id);
+                    action.setResolver(new PDFTransitionAction.Resolver(action, extension) {
+                        public void performResolution() {
+                            PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+                            Object transition = makeDictionary(extension.findEntryValue("Trans"));
+                            Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next"));
+                            getAction().populate(transition, nextAction);
+                        }
+                    });
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        }
+    }
+
+    public void addNavigator(PDFDictionaryExtension extension) {
+        assert extension.getDictionaryType() == PDFDictionaryType.Navigator;
+        String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+        if ((id != null) && (id.length() > 0)) {
+            PDFNavigator navigator = pdfDoc.getFactory().makeNavigator(id);
+            navigator.setResolver(new PDFNavigator.Resolver(navigator, extension) {
+                public void performResolution() {
+                    PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+                    Object nextAction = makeDictionary(extension.findEntryValue("NA"));
+                    Object next = makeDictionary(extension.findEntryValue("Next"));
+                    Object prevAction = makeDictionary(extension.findEntryValue("PA"));
+                    Object prev = makeDictionary(extension.findEntryValue("Prev"));
+                    Object duration = extension.findEntryValue("Dur");
+                    getNavigator().populate(nextAction, next, prevAction, prev, duration);
+                }
+            });
+        }
+    }
+
+    private Object makeArray(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof PDFReferenceExtension) {
+            return resolveReference((PDFReferenceExtension) value);
+        } else if (value instanceof List<?>) {
+            return populateArray(new PDFArray(), (List<?>) value);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private Object populateArray(PDFArray array, List<?> entries) {
+        for (PDFCollectionEntryExtension entry : (List<PDFCollectionEntryExtension>) entries) {
+            PDFObjectType type = entry.getType();
+            if (type == PDFObjectType.Array) {
+                array.add(makeArray(entry.getValue()));
+            } else if (type == PDFObjectType.Boolean) {
+                array.add(entry.getValueAsBoolean());
+            } else if (type == PDFObjectType.Dictionary) {
+                array.add(makeDictionary(entry.getValue()));
+            } else if (type == PDFObjectType.Name) {
+                array.add(new PDFName(entry.getValueAsString()));
+            } else if (type == PDFObjectType.Number) {
+                array.add(new PDFNumber(entry.getValueAsNumber()));
+            } else if (type == PDFObjectType.Reference) {
+                array.add(resolveReference((PDFReferenceExtension) entry));
+            } else if (type == PDFObjectType.String) {
+                array.add(entry.getValue());
+            }
+        }
+        return array;
+    }
+
+    private Object makeDictionary(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof PDFReferenceExtension) {
+            return resolveReference((PDFReferenceExtension) value);
+        } else if (value instanceof List<?>) {
+            return populateDictionary(new PDFDictionary(), (List<?>) value);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private Object populateDictionary(PDFDictionary dictionary, List<?> entries) {
+        for (PDFCollectionEntryExtension entry : (List<PDFCollectionEntryExtension>) entries) {
+            PDFObjectType type = entry.getType();
+            String key = entry.getKey();
+            if (type == PDFObjectType.Array) {
+                dictionary.put(key, makeArray(entry.getValue()));
+            } else if (type == PDFObjectType.Boolean) {
+                dictionary.put(key, entry.getValueAsBoolean());
+            } else if (type == PDFObjectType.Dictionary) {
+                dictionary.put(key, makeDictionary(entry.getValue()));
+            } else if (type == PDFObjectType.Name) {
+                dictionary.put(key, new PDFName(entry.getValueAsString()));
+            } else if (type == PDFObjectType.Number) {
+                dictionary.put(key, new PDFNumber(entry.getValueAsNumber()));
+            } else if (type == PDFObjectType.Reference) {
+                dictionary.put(key, resolveReference((PDFReferenceExtension) entry));
+            } else if (type == PDFObjectType.String) {
+                dictionary.put(key, entry.getValue());
+            }
+        }
+        return dictionary;
+    }
+
+    private Object makeDictionaryOrArray(Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof PDFReferenceExtension) {
+            return resolveReference((PDFReferenceExtension) value);
+        } else if (value instanceof List<?>) {
+            if (hasKeyedEntry((List<?>) value)) {
+                return populateDictionary(new PDFDictionary(), (List<?>) value);
+            } else {
+                return populateArray(new PDFArray(), (List<?>) value);
+            }
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private boolean hasKeyedEntry(List<?> entries) {
+        for (PDFCollectionEntryExtension entry : (List<PDFCollectionEntryExtension>) entries) {
+            if (entry.getKey() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void renderDictionaryExtension(PDFDictionaryExtension extension, PDFPage currentPage) {
+        PDFDictionaryType type = extension.getDictionaryType();
+        if (type == PDFDictionaryType.Catalog) {
             augmentDictionary(pdfDoc.getRoot(), extension);
-        } else if (extension.getDictionaryType() == PDFDictionaryType.Page) {
-            if (extension.matchesPageNumber(currentPage.getPageIndex() + 1)) {
+        } else if (type == PDFDictionaryType.Page) {
+            assert extension instanceof PDFPageExtension;
+            if (((PDFPageExtension) extension).matchesPageNumber(currentPage.getPageIndex() + 1)) {
                 augmentDictionary(currentPage, extension);
             }
         } else {
@@ -272,9 +461,11 @@ class PDFRenderingUtil {
     }
 
     private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) {
-        for (PDFDictionaryEntryExtension entry : extension.getEntries()) {
+        for (PDFCollectionEntryExtension entry : extension.getEntries()) {
             if (entry instanceof PDFDictionaryExtension) {
                 dictionary.put(entry.getKey(), augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry));
+            } else if (entry instanceof PDFArrayExtension) {
+                dictionary.put(entry.getKey(), augmentArray(new PDFArray(dictionary), (PDFArrayExtension) entry));
             } else {
                 augmentDictionary(dictionary, entry);
             }
@@ -282,22 +473,68 @@ class PDFRenderingUtil {
         return dictionary;
     }
 
-    private void augmentDictionary(PDFDictionary dictionary, PDFDictionaryEntryExtension entry) {
-        PDFDictionaryEntryType type = entry.getType();
+    private void augmentDictionary(PDFDictionary dictionary, PDFCollectionEntryExtension entry) {
+        PDFObjectType type = entry.getType();
         String key = entry.getKey();
-        if (type == PDFDictionaryEntryType.Boolean) {
+        if (type == PDFObjectType.Boolean) {
             dictionary.put(key, entry.getValueAsBoolean());
-        } else if (type == PDFDictionaryEntryType.Name) {
+        } else if (type == PDFObjectType.Name) {
             dictionary.put(key, new PDFName(entry.getValueAsString()));
-        } else if (type == PDFDictionaryEntryType.Number) {
+        } else if (type == PDFObjectType.Number) {
             dictionary.put(key, new PDFNumber(entry.getValueAsNumber()));
-        } else if (type == PDFDictionaryEntryType.String) {
+        } else if (type == PDFObjectType.Reference) {
+            assert entry instanceof PDFReferenceExtension;
+            dictionary.put(key, resolveReference((PDFReferenceExtension) entry));
+        } else if (type == PDFObjectType.String) {
             dictionary.put(key, entry.getValueAsString());
         } else {
             throw new IllegalStateException();
         }
     }
 
+    private Object resolveReference(PDFReferenceExtension entry) {
+        PDFReference reference = (PDFReference) entry.getResolvedReference();
+        if (reference == null) {
+            reference = pdfDoc.resolveExtensionReference(entry.getReferenceId());
+            if (reference != null) {
+                entry.setResolvedReference(reference);
+            }
+            return reference;
+        }
+        return PDFNull.INSTANCE;
+    }
+
+    private PDFArray augmentArray(PDFArray array, PDFArrayExtension extension) {
+        for (PDFCollectionEntryExtension entry : extension.getEntries()) {
+            if (entry instanceof PDFDictionaryExtension) {
+                array.add(augmentDictionary(new PDFDictionary(array), (PDFDictionaryExtension) entry));
+            } else if (entry instanceof PDFArrayExtension) {
+                array.add(augmentArray(new PDFArray(array), (PDFArrayExtension) entry));
+            } else {
+                augmentArray(array, entry);
+            }
+        }
+        return array;
+    }
+
+    private void augmentArray(PDFArray array, PDFCollectionEntryExtension entry) {
+        PDFObjectType type = entry.getType();
+        if (type == PDFObjectType.Boolean) {
+            array.add(entry.getValueAsBoolean());
+        } else if (type == PDFObjectType.Name) {
+            array.add(new PDFName(entry.getValueAsString()));
+        } else if (type == PDFObjectType.Number) {
+            array.add(new PDFNumber(entry.getValueAsNumber()));
+        } else if (type == PDFObjectType.Reference) {
+            assert entry instanceof PDFReferenceExtension;
+            array.add(resolveReference((PDFReferenceExtension) entry));
+        } else if (type == PDFObjectType.String) {
+            array.add(entry.getValueAsString());
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
     public PDFDocument setupPDFDocument(OutputStream out) throws IOException {
         if (this.pdfDoc != null) {
             throw new IllegalStateException("PDFDocument already set up");
diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java
deleted file mode 100644 (file)
index 9de7e95..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-/* $Id$ */
-
-package org.apache.fop.render.pdf.extensions;
-
-import org.apache.fop.fo.FONode;
-
-// CSOFF: LineLengthCheck
-
-/**
- * Base class for the PDF dictionary related extension elements.
- */
-public abstract class AbstractPDFDictionaryElement extends AbstractPDFExtensionElement {
-
-    public static final String ATT_KEY = PDFDictionaryEntryExtension.PROPERTY_KEY;
-
-    /**
-     * Default constructor
-     *
-     * @param parent parent of this node
-     * @see org.apache.fop.fo.FONode#FONode(FONode)
-     */
-    public AbstractPDFDictionaryElement(FONode parent) {
-        super(parent);
-    }
-}
-
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java
new file mode 100644 (file)
index 0000000..e4a5747
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:action.
+ */
+public class PDFActionElement extends PDFDictionaryElement {
+
+    public static final String ATT_TYPE = PDFActionExtension.PROPERTY_TYPE;
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    PDFActionElement(FONode parent) {
+        super(parent, PDFDictionaryType.Action);
+    }
+
+    @Override
+    public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+        super.processNode(elementName, locator, attlist, propertyList);
+        String type = attlist.getValue(ATT_TYPE);
+        if (type != null) {
+            getDictionaryExtension().setProperty(PDFActionExtension.PROPERTY_TYPE, type);
+        }
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java
new file mode 100644 (file)
index 0000000..778b8a2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFActionExtension extends PDFDictionaryExtension {
+
+    public static final String PROPERTY_TYPE = "type";
+
+    PDFActionExtension() {
+        super(PDFDictionaryType.Action);
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java
new file mode 100644 (file)
index 0000000..1f3ba22
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:array.
+ */
+public class PDFArrayElement extends PDFCollectionEntryElement {
+
+    private PDFArrayExtension extension;
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    PDFArrayElement(FONode parent) {
+        super(parent, PDFObjectType.Array, new PDFArrayExtension());
+    }
+
+    public PDFArrayExtension getArrayExtension() {
+        assert getExtension() instanceof PDFArrayExtension;
+        return (PDFArrayExtension) getExtension();
+    }
+
+    @Override
+    public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+        super.processNode(elementName, locator, attlist, propertyList);
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+    }
+
+    @Override
+    protected void addChildNode(FONode child) throws FOPException {
+        PDFArrayExtension extension = getArrayExtension();
+        if (child instanceof PDFCollectionEntryElement) {
+            PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension();
+            if (entry.getKey() == null) {
+                extension.addEntry(entry);
+            }
+        }
+    }
+
+    @Override
+    public void endOfNode() throws FOPException {
+        super.endOfNode();
+    }
+
+    @Override
+    public String getLocalName() {
+        return PDFObjectType.Array.elementName();
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java
new file mode 100644 (file)
index 0000000..80c6c94
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import java.util.List;
+import java.util.Map;
+
+// CSOFF: LineLengthCheck
+
+public class PDFArrayExtension extends PDFCollectionExtension {
+
+    private static final long serialVersionUID = -1L;
+
+    private Map<String, String> properties;
+    private List<PDFCollectionEntryExtension> entries;
+
+    PDFArrayExtension() {
+        super(PDFObjectType.Array);
+        this.properties = new java.util.HashMap<String, String>();
+        this.entries = new java.util.ArrayList<PDFCollectionEntryExtension>();
+    }
+
+    @Override
+    public void setValue(Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue() {
+        return getEntries();
+    }
+
+    public void setProperty(String name, String value) {
+        properties.put(name, value);
+    }
+
+    public String getProperty(String name) {
+        return properties.get(name);
+    }
+
+    @Override
+    public void addEntry(PDFCollectionEntryExtension entry) {
+        if (entry.getKey() != null) {
+            throw new IllegalArgumentException();
+        } else {
+            entries.add(entry);
+        }
+    }
+
+    public List<PDFCollectionEntryExtension> getEntries() {
+        return entries;
+    }
+
+    public PDFCollectionEntryExtension getLastEntry() {
+        if (entries.size() > 0) {
+            return entries.get(entries.size() - 1);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getElementName() {
+        return PDFObjectType.Array.elementName();
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java
new file mode 100644 (file)
index 0000000..029357d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:catalog.
+ */
+public class PDFCatalogElement extends PDFDictionaryElement {
+
+    PDFCatalogElement(FONode parent) {
+        super(parent, PDFDictionaryType.Catalog);
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java
new file mode 100644 (file)
index 0000000..381f6fb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFCatalogExtension extends PDFDictionaryExtension {
+
+    PDFCatalogExtension() {
+        super(PDFDictionaryType.Catalog);
+    }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java
new file mode 100644 (file)
index 0000000..4185cee
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for collection entries: pdf:{array,boolean,dictionary,name,number,reference,string}. The specific type
+ * of entry is established at construction type.
+ */
+public class PDFCollectionEntryElement extends AbstractPDFExtensionElement {
+
+    public static final String ATT_KEY = PDFCollectionEntryExtension.PROPERTY_KEY;
+
+    private PDFCollectionEntryExtension extension;
+    private StringBuffer characters;
+
+    PDFCollectionEntryElement(FONode parent, PDFObjectType type, PDFCollectionEntryExtension extension) {
+        super(parent);
+        this.extension = extension;
+    }
+
+    PDFCollectionEntryElement(FONode parent, PDFObjectType type) {
+        this(parent, type, createExtension(type));
+    }
+
+    private static PDFCollectionEntryExtension createExtension(PDFObjectType type) {
+        if (type == PDFObjectType.Reference) {
+            return new PDFReferenceExtension();
+        } else {
+            return new PDFCollectionEntryExtension(type);
+        }
+    }
+
+    public PDFCollectionEntryExtension getExtension() {
+        return extension;
+    }
+
+    @Override
+    public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+        if (parent instanceof PDFDictionaryElement) {
+            String key = attlist.getValue(ATT_KEY);
+            if (key == null) {
+                missingPropertyError(ATT_KEY);
+            } else if (key.length() == 0) {
+                invalidPropertyValueError(ATT_KEY, key, null);
+            } else {
+                extension.setKey(key);
+            }
+        }
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent instanceof PDFDictionaryElement) {
+            if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) {
+                invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
+            }
+        }
+    }
+
+    @Override
+    protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException {
+        if (capturePCData(extension.getType())) {
+            if (characters == null) {
+                characters = new StringBuffer((length < 16) ? 16 : length);
+            }
+            characters.append(data, start, length);
+        }
+    }
+
+    private boolean capturePCData(PDFObjectType type) {
+        if (type == PDFObjectType.Array) {
+            return false;
+        } else if (type == PDFObjectType.Dictionary) {
+            return false;
+        } else {
+            return (type != PDFObjectType.Reference);
+        }
+    }
+
+    @Override
+    public void endOfNode() throws FOPException {
+        if (capturePCData(extension.getType())) {
+            if (extension.getType() == PDFObjectType.Boolean) {
+                String value = (characters != null) ? characters.toString() : "";
+                if (!value.equals("true") && !value.equals("false")) {
+                    invalidPropertyValueError("<value>", value, null);
+                }
+                extension.setValue(Boolean.valueOf(value));
+            } else if (extension.getType() == PDFObjectType.Name) {
+                String value = (characters != null) ? characters.toString() : "";
+                if (value.length() == 0) {
+                    invalidPropertyValueError("<value>", value, null);
+                }
+                extension.setValue(value);
+            } else if (extension.getType() == PDFObjectType.Number) {
+                String value = (characters != null) ? characters.toString() : "";
+                try {
+                    double d = Double.parseDouble(value);
+                    if (Math.abs(Math.floor(d) - d) < 1E-10) {
+                        extension.setValue(Long.valueOf((long) d));
+                    } else {
+                        extension.setValue(Double.valueOf(d));
+                    }
+                } catch (NumberFormatException e) {
+                    invalidPropertyValueError("<value>", value, null);
+                }
+            } else if (extension.getType() == PDFObjectType.String) {
+                String value = (characters != null) ? characters.toString() : "";
+                extension.setValue(value);
+            }
+        }
+        super.endOfNode();
+    }
+
+    @Override
+    public String getLocalName() {
+        return extension.getType().elementName();
+    }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java
new file mode 100644 (file)
index 0000000..d28f160
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFCollectionEntryExtension extends PDFObjectExtension {
+
+    public static final String PROPERTY_KEY = "key";
+
+    /* Non-empty key if used as dictionary entry, otherwise must be null. */
+    private String key;
+
+    PDFCollectionEntryExtension(PDFObjectType type) {
+        super(type);
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java
new file mode 100644 (file)
index 0000000..228d69c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public abstract class PDFCollectionExtension extends PDFCollectionEntryExtension {
+
+    protected PDFCollectionExtension(PDFObjectType type) {
+        super(type);
+    }
+
+    public abstract void addEntry(PDFCollectionEntryExtension entry);
+
+    public abstract PDFCollectionEntryExtension getLastEntry();
+
+}
index 19e5ce07aad15fd728462e509f92ac4a0e6e7c29..3832619ba98db101cb6a6c969b13738506a0f727 100644 (file)
@@ -43,14 +43,13 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment {
     }
 
     public void toSAX(ContentHandler handler) throws SAXException {
-        PDFDictionaryType dictionaryType = extension.getDictionaryType();
         int pageNumber = 0;
-        if (dictionaryType == PDFDictionaryType.Page) {
+        if (extension instanceof PDFPageExtension) {
             if (handler instanceof GenerationHelperContentHandler) {
                 Object context = ((GenerationHelperContentHandler) handler).getContentHandlerContext();
                 if (context instanceof IFContext) {
                     int pageIndex = ((IFContext) context).getPageIndex();
-                    if ((pageIndex >= 0) && extension.matchesPageNumber(pageIndex + 1)) {
+                    if ((pageIndex >= 0) && ((PDFPageExtension) extension).matchesPageNumber(pageIndex + 1)) {
                         pageNumber = pageIndex + 1;
                     } else {
                         pageNumber = -1;
@@ -69,24 +68,40 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment {
         String qn = PREFIX + ":" + ln;
         attributes = extractIFAttributes(attributes, dictionary);
         handler.startElement(CATEGORY, ln, qn, attributes);
-        for (PDFDictionaryEntryExtension entry : dictionary.getEntries()) {
+        for (PDFCollectionEntryExtension entry : dictionary.getEntries()) {
             toSAX(handler, entry);
         }
         handler.endElement(CATEGORY, ln, qn);
     }
 
-    private void toSAX(ContentHandler handler, PDFDictionaryEntryExtension entry) throws SAXException {
+    private void toSAX(ContentHandler handler, PDFArrayExtension array) throws SAXException {
+        AttributesImpl attributes = new AttributesImpl();
+        String ln = array.getElementName();
+        String qn = PREFIX + ":" + ln;
+        attributes = extractIFAttributes(attributes, array);
+        handler.startElement(CATEGORY, ln, qn, attributes);
+        for (PDFCollectionEntryExtension entry : array.getEntries()) {
+            toSAX(handler, entry);
+        }
+        handler.endElement(CATEGORY, ln, qn);
+    }
+
+    private void toSAX(ContentHandler handler, PDFCollectionEntryExtension entry) throws SAXException {
         if (entry instanceof PDFDictionaryExtension) {
             toSAX(handler, (PDFDictionaryExtension) entry);
+        } else if (entry instanceof PDFArrayExtension) {
+            toSAX(handler, (PDFArrayExtension) entry);
         } else {
             AttributesImpl attributes = new AttributesImpl();
             String ln = entry.getElementName();
             String qn = PREFIX + ":" + ln;
             attributes = extractIFAttributes(attributes, entry);
             handler.startElement(CATEGORY, ln, qn, attributes);
-            char[] characters = entry.getValueAsXMLEscapedString().toCharArray();
-            if (characters.length > 0) {
-                handler.characters(characters, 0, characters.length);
+            if (!(entry instanceof PDFReferenceExtension)) {
+                char[] characters = entry.getValueAsXMLEscapedString().toCharArray();
+                if (characters.length > 0) {
+                    handler.characters(characters, 0, characters.length);
+                }
             }
             handler.endElement(CATEGORY, ln, qn);
         }
@@ -94,16 +109,27 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment {
 
     private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryExtension dictionary) {
         PDFDictionaryType type = dictionary.getDictionaryType();
-        if (type == PDFDictionaryType.Catalog) {
-            // no specific attriburtes
+        if (dictionary.usesIDAttribute()) {
+            String idName = PDFDictionaryElement.ATT_ID;
+            String id = dictionary.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+            if (id != null) {
+                attributes.addAttribute(null, idName, idName, "ID", id);
+            }
+        }
+        if (type == PDFDictionaryType.Action) {
+            String actionTypeName = PDFActionElement.ATT_TYPE;
+            String actionType = dictionary.getProperty(PDFActionExtension.PROPERTY_TYPE);
+            if (actionType != null) {
+                attributes.addAttribute(null, actionTypeName, actionTypeName, "CDATA", actionType);
+            }
         } else if (type == PDFDictionaryType.Page) {
-            String pageNumbersName = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS;
+            String pageNumbersName = PDFPageExtension.PROPERTY_PAGE_NUMBERS;
             String pageNumbers = dictionary.getProperty(pageNumbersName);
             if (pageNumbers != null) {
                 attributes.addAttribute(null, pageNumbersName, pageNumbersName, "CDATA", pageNumbers);
             }
         } else if (type == PDFDictionaryType.Dictionary) {
-            String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY;
+            String keyName = PDFCollectionEntryElement.ATT_KEY;
             String key = dictionary.getKey();
             if (key != null) {
                 attributes.addAttribute(null, keyName, keyName, "CDATA", key);
@@ -112,12 +138,28 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment {
         return attributes;
     }
 
-    private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryEntryExtension entry) {
-        String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY;
+    private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFArrayExtension array) {
+        String keyName = PDFCollectionEntryExtension.PROPERTY_KEY;
+        String key = array.getKey();
+        if (key != null) {
+            attributes.addAttribute(null, keyName, keyName, "CDATA", key);
+        }
+        return attributes;
+    }
+
+    private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFCollectionEntryExtension entry) {
+        String keyName = PDFCollectionEntryElement.ATT_KEY;
         String key = entry.getKey();
         if (key != null) {
             attributes.addAttribute(null, keyName, keyName, "CDATA", key);
         }
+        if (entry instanceof PDFReferenceExtension) {
+            String refid = ((PDFReferenceExtension) entry).getReferenceId();
+            if (refid != null) {
+                String refidName = PDFReferenceElement.ATT_REFID;
+                attributes.addAttribute(null, refidName, refidName, "IDREF", refid);
+            }
+        }
         return attributes;
     }
 
index 0920f3a78d226171dced74bf788c8e0e77795361..9dc127da6179f9571b3f73cfdb7a1f3514227d8b 100644 (file)
@@ -23,7 +23,6 @@ import org.xml.sax.Attributes;
 import org.xml.sax.Locator;
 
 import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.extensions.ExtensionAttachment;
@@ -34,38 +33,54 @@ import org.apache.fop.fo.extensions.ExtensionAttachment;
  * Extension element for dictionaries: pdf:{catalog,page,dictionary}. The specific type
  * of dictionary is established at construction type.
  */
-public class PDFDictionaryElement extends AbstractPDFDictionaryElement {
+public class PDFDictionaryElement extends PDFCollectionEntryElement {
 
-    public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS;
-
-    private PDFDictionaryExtension extension;
+    public static final String ATT_ID = PDFDictionaryExtension.PROPERTY_ID;
 
     /**
      * Main constructor
      * @param parent parent FO node
      */
     PDFDictionaryElement(FONode parent, PDFDictionaryType type) {
-        super(parent);
-        this.extension = new PDFDictionaryExtension(type);
+        super(parent, PDFObjectType.Dictionary, createExtension(type));
+    }
+
+    private static PDFDictionaryExtension createExtension(PDFDictionaryType type) {
+        if (type == PDFDictionaryType.Action) {
+            return new PDFActionExtension();
+        } else if (type == PDFDictionaryType.Catalog) {
+            return new PDFCatalogExtension();
+        } else if (type == PDFDictionaryType.Layer) {
+            return new PDFLayerExtension();
+        } else if (type == PDFDictionaryType.Navigator) {
+            return new PDFNavigatorExtension();
+        } else if (type == PDFDictionaryType.Page) {
+            return new PDFPageExtension();
+        } else {
+            return new PDFDictionaryExtension(type);
+        }
     }
 
-    public PDFDictionaryExtension getExtension() {
-        return extension;
+    public PDFDictionaryExtension getDictionaryExtension() {
+        assert getExtension() instanceof PDFDictionaryExtension;
+        return (PDFDictionaryExtension) getExtension();
     }
 
     @Override
     public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
-        if (extension.getDictionaryType() == PDFDictionaryType.Catalog) {
-            // no specific properties
-        } else if (extension.getDictionaryType() == PDFDictionaryType.Page) {
-            String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS);
-            if (pageNumbers != null) {
-                extension.setProperty(ATT_PAGE_NUMBERS, pageNumbers);
+        PDFDictionaryExtension extension = getDictionaryExtension();
+        if (extension.usesIDAttribute()) {
+            String id = attlist.getValue(ATT_ID);
+            if (id != null) {
+                extension.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
             }
-        } else if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) {
+        }
+        if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) {
             String key = attlist.getValue(ATT_KEY);
             if (key == null) {
-                missingPropertyError(ATT_KEY);
+                if (parent instanceof PDFDictionaryElement) {
+                    missingPropertyError(ATT_KEY);
+                }
             } else if (key.length() == 0) {
                 invalidPropertyValueError(ATT_KEY, key, null);
             } else {
@@ -78,16 +93,18 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement {
     public void startOfNode() throws FOPException {
         super.startOfNode();
         String localName = getLocalName();
-        if (localName.equals("catalog")) {
-            if (parent.getNameId() != Constants.FO_DECLARATIONS) {
-                invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
-            }
+        if (localName.equals("action")) {
+            // handled in PDFActionElement subclass
+        } else if (localName.equals("catalog")) {
+            // handled in PDFCatalogElement subclass
+        } else if (localName.equals("layer")) {
+            // handled in PDFLayerElement subclass
+        } else if (localName.equals("navigator")) {
+            // handled in PDFNavigattorElement subclass
         } else if (localName.equals("page")) {
-            if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
-                invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM");
-            }
+            // handled in PDFPageElement subclass
         } else if (localName.equals("dictionary")) {
-            if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) {
+            if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName()) && !PDFObjectType.Array.elementName().equals(parent.getLocalName())) {
                 invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
             }
         } else {
@@ -97,14 +114,15 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement {
 
     @Override
     protected void addChildNode(FONode child) throws FOPException {
+        PDFDictionaryExtension extension = getDictionaryExtension();
         if (child instanceof PDFDictionaryElement) {
-            PDFDictionaryExtension extension = ((PDFDictionaryElement) child).getExtension();
-            if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) {
-                this.extension.addEntry(extension);
+            PDFDictionaryExtension entry = ((PDFDictionaryElement) child).getDictionaryExtension();
+            if (entry.getDictionaryType() == PDFDictionaryType.Dictionary) {
+                extension.addEntry(entry);
             }
-        } else if (child instanceof PDFDictionaryEntryElement) {
-            PDFDictionaryEntryExtension extension = ((PDFDictionaryEntryElement) child).getExtension();
-            this.extension.addEntry(extension);
+        } else if (child instanceof PDFCollectionEntryElement) {
+            PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension();
+            extension.addEntry(entry);
         }
     }
 
@@ -115,12 +133,13 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement {
 
     @Override
     public String getLocalName() {
+        PDFDictionaryExtension extension = getDictionaryExtension();
         return extension.getDictionaryType().elementName();
     }
 
     @Override
     protected ExtensionAttachment instantiateExtensionAttachment() {
-        return new PDFDictionaryAttachment(extension);
+        return new PDFDictionaryAttachment(getDictionaryExtension());
     }
 
 }
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java
deleted file mode 100644 (file)
index bcdb90c..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-
-/* $Id$ */
-
-package org.apache.fop.render.pdf.extensions;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
-
-import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.PropertyList;
-
-// CSOFF: LineLengthCheck
-
-/**
- * Extension element for dictionary entries: pdf:{boolean,name,number,string}. The specific type
- * of entry is established at construction type.
- */
-public class PDFDictionaryEntryElement extends AbstractPDFDictionaryElement {
-
-    private PDFDictionaryEntryExtension extension;
-    private StringBuffer characters;
-
-    /**
-     * Main constructor
-     * @param parent parent FO node
-     */
-    PDFDictionaryEntryElement(FONode parent, PDFDictionaryEntryType type) {
-        super(parent);
-        this.extension = new PDFDictionaryEntryExtension(type);
-    }
-
-    public PDFDictionaryEntryExtension getExtension() {
-        return extension;
-    }
-
-    @Override
-    public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
-        String key = attlist.getValue("key");
-        if (key == null) {
-            missingPropertyError("key");
-        } else if (key.length() == 0) {
-            invalidPropertyValueError("key", key, null);
-        } else {
-            extension.setKey(key);
-        }
-    }
-
-    @Override
-    public void startOfNode() throws FOPException {
-        super.startOfNode();
-        if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) {
-            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
-        }
-    }
-
-    @Override
-    protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException {
-        if (characters == null) {
-            characters = new StringBuffer((length < 16) ? 16 : length);
-        }
-        characters.append(data, start, length);
-    }
-
-    @Override
-    public void endOfNode() throws FOPException {
-        String value = (characters != null) ? characters.toString() : "";
-        if (extension.getType() == PDFDictionaryEntryType.Boolean) {
-            if (!value.equals("true") && !value.equals("false")) {
-                invalidPropertyValueError("<value>", value, null);
-            }
-        } else if (extension.getType() == PDFDictionaryEntryType.Name) {
-            if (value.length() == 0) {
-                invalidPropertyValueError("<value>", value, null);
-            }
-        } else if (extension.getType() == PDFDictionaryEntryType.Number) {
-            try {
-                Double.valueOf(value);
-            } catch (NumberFormatException e) {
-                invalidPropertyValueError("<value>", value, null);
-            }
-        } else if (extension.getType() != PDFDictionaryEntryType.String) {
-            throw new IllegalStateException();
-        }
-        extension.setValue(value);
-        super.endOfNode();
-    }
-
-    @Override
-    public String getLocalName() {
-        return extension.getType().elementName();
-    }
-}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java
deleted file mode 100644 (file)
index 94d6b5d..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-
-/* $Id$ */
-
-package org.apache.fop.render.pdf.extensions;
-
-import org.apache.fop.util.XMLUtil;
-
-// CSOFF: LineLengthCheck
-
-public class PDFDictionaryEntryExtension {
-
-    public static final String PROPERTY_KEY = "key";
-
-    private PDFDictionaryEntryType type;
-    private String key = "";
-    private Object value;
-
-    PDFDictionaryEntryExtension() {
-    }
-
-    PDFDictionaryEntryExtension(PDFDictionaryEntryType type) {
-        this.type = type;
-    }
-
-    public PDFDictionaryEntryType getType() {
-        return type;
-    }
-
-    public String getKey() {
-        return key;
-    }
-
-    public void setKey(String key) {
-        this.key = key;
-    }
-
-    public void setValue(String value) {
-        this.value = value;
-    }
-
-    public Object getValue() {
-        return value;
-    }
-
-    /**
-     * Obtain entry value as Boolean.
-     * @return entry value
-     */
-    public Boolean getValueAsBoolean() {
-        if (value instanceof String) {
-            return Boolean.valueOf((String)value);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Obtain entry value as Number.
-     * @return entry value
-     */
-    public Number getValueAsNumber() {
-        if (value instanceof String) {
-            double d = Double.parseDouble((String) value);
-            if (Math.floor(d) == d) {
-                return Long.valueOf((long) d);
-            } else {
-                return Double.valueOf(d);
-            }
-        } else {
-            return Integer.valueOf(0);
-        }
-    }
-
-    public String getValueAsString() {
-        if (value instanceof String) {
-            return (String) value;
-        } else {
-            return "";
-        }
-    }
-
-    public String getValueAsXMLEscapedString() {
-        return XMLUtil.escape(getValueAsString());
-    }
-
-    public void setValue(Object value) {
-        this.value = value;
-    }
-
-    public String getElementName() {
-        return type.elementName();
-    }
-
-}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java
deleted file mode 100644 (file)
index e4b2581..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-/* $Id$ */
-
-package org.apache.fop.render.pdf.extensions;
-
-// CSOFF: LineLengthCheck
-
-/**
- * Enumeration type for leaf PDF dictionary entry extension elements.
- */
-public enum PDFDictionaryEntryType {
-    Boolean("boolean"),         // boolean valued entry
-    Dictionary("dictionary"),   // dictionary valued entry
-    Name("name"),               // name valued entry
-    Number("number"),           // number valued entry
-    String("string");           // string valued entry
-
-    private String elementName;
-    PDFDictionaryEntryType(String elementName) {
-        this.elementName = elementName;
-    }
-    public String elementName() {
-        return elementName;
-    }
-    static PDFDictionaryEntryType valueOfElementName(String elementName) {
-        for (PDFDictionaryEntryType type : values()) {
-            if (type.elementName.equals(elementName)) {
-                return type;
-            }
-        }
-        throw new IllegalArgumentException();
-    }
-    static boolean hasValueOfElementName(String elementName) {
-        try {
-            return valueOfElementName(elementName) != null;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-}
index d4b11cdb4dca34252c24c94a0bc12fde0cb0d0df..50b6f3a830c69b01a2f2bae1dee5931a2fe30d35 100644 (file)
@@ -24,24 +24,36 @@ import java.util.Map;
 
 // CSOFF: LineLengthCheck
 
-public class PDFDictionaryExtension extends PDFDictionaryEntryExtension {
+public class PDFDictionaryExtension extends PDFCollectionExtension {
 
+    public static final String PROPERTY_ID = "id";
     public static final String PROPERTY_PAGE_NUMBERS = "page-numbers";
 
     private static final long serialVersionUID = -1L;
 
     private PDFDictionaryType dictionaryType;
     private Map<String, String> properties;
-    private List<PDFDictionaryEntryExtension> entries;
+    private List<PDFCollectionEntryExtension> entries;
 
     PDFDictionaryExtension() {
+        this(PDFDictionaryType.Dictionary);
     }
 
     PDFDictionaryExtension(PDFDictionaryType dictionaryType) {
-        super(PDFDictionaryEntryType.Dictionary);
+        super(PDFObjectType.Dictionary);
         this.dictionaryType = dictionaryType;
         this.properties = new java.util.HashMap<String, String>();
-        this.entries = new java.util.ArrayList<PDFDictionaryEntryExtension>();
+        this.entries = new java.util.ArrayList<PDFCollectionEntryExtension>();
+    }
+
+    @Override
+    public void setValue(Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue() {
+        return getEntries();
     }
 
     public PDFDictionaryType getDictionaryType() {
@@ -56,15 +68,40 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension {
         return properties.get(name);
     }
 
-    public void addEntry(PDFDictionaryEntryExtension entry) {
-        entries.add(entry);
+    @Override
+    public void addEntry(PDFCollectionEntryExtension entry) {
+        if ((entry.getKey() == null) || (entry.getKey().length() == 0)) {
+            throw new IllegalArgumentException();
+        } else {
+            entries.add(entry);
+        }
     }
 
-    public List<PDFDictionaryEntryExtension> getEntries() {
+    public List<PDFCollectionEntryExtension> getEntries() {
         return entries;
     }
 
-    public PDFDictionaryEntryExtension getLastEntry() {
+    public PDFCollectionEntryExtension findEntry(String key) {
+        for (PDFCollectionEntryExtension entry : entries) {
+            String entryKey = entry.getKey();
+            if ((entryKey != null) && entryKey.equals(key)) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
+    public Object findEntryValue(String key) {
+        for (PDFCollectionEntryExtension entry : entries) {
+            String entryKey = entry.getKey();
+            if ((entryKey != null) && entryKey.equals(key)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    public PDFCollectionEntryExtension getLastEntry() {
         if (entries.size() > 0) {
             return entries.get(entries.size() - 1);
         } else {
@@ -72,48 +109,8 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension {
         }
     }
 
-    /**
-     * Determine if page dictionary and page number matches.
-     * @param pageNumber page number, where first page number is 1
-     * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property
-     */
-    public boolean matchesPageNumber(int pageNumber) {
-        if (dictionaryType != PDFDictionaryType.Page) {
-            return false;
-        }
-        String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS);
-        if ((pageNumbers == null) || (pageNumbers.length() == 0)) {
-            return false;
-        } else if (pageNumbers.equals("*")) {
-            return true;
-        } else {
-            for (String interval : pageNumbers.split("\\s*,\\s*")) {
-                String[] components = interval.split("\\s*-\\s*");
-                if (components.length < 1) {
-                    continue;
-                } else {
-                    try {
-                        int start = Integer.parseInt(components[0]);
-                        int end = 0;
-                        if (components.length > 1) {
-                            if (!components[1].equals("LAST")) {
-                                end = Integer.parseInt(components[1]);
-                            }
-                        }
-                        if ((end == 0) && (pageNumber == start)) {
-                            return true;
-                        } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) {
-                            return true;
-                        } else {
-                            continue;
-                        }
-                    } catch (NumberFormatException e) {
-                        continue;
-                    }
-                }
-            }
-        }
-        return false;
+    public boolean usesIDAttribute() {
+        return dictionaryType.usesIDAttribute();
     }
 
     @Override
index edd95160a00b44d72bc268c5e6d83693c0fde9c5..a49a5fc8c6a724b2ad385cce0031cdf9ea2abadd 100644 (file)
@@ -25,17 +25,28 @@ package org.apache.fop.render.pdf.extensions;
  * Enumeration type for PDF dictionary extension elements.
  */
 public enum PDFDictionaryType {
-    Dictionary("dictionary"),   // generic (nested) dictionary element
-    Catalog("catalog"),         // catalog dictionary element
-    Page("page");               // page dictionary element
+    Action("action", true),             // action dictionary element
+    Catalog("catalog"),                 // catalog dictionary element
+    Dictionary("dictionary"),           // generic (nested) dictionary element
+    Layer("layer", true),               // optional content group dictionary element
+    Navigator("navigator", true),       // navigation node dictionary element
+    Page("page");                       // page dictionary element
 
     private String elementName;
-    PDFDictionaryType(String elementName) {
+    private boolean usesIDAttribute;
+    PDFDictionaryType(String elementName, boolean usesIDAttribute) {
         this.elementName = elementName;
+        this.usesIDAttribute = usesIDAttribute;
+    }
+    PDFDictionaryType(String elementName) {
+        this(elementName, false);
     }
     public String elementName() {
         return elementName;
     }
+    public boolean usesIDAttribute() {
+        return usesIDAttribute;
+    }
     static PDFDictionaryType valueOfElementName(String elementName) {
         for (PDFDictionaryType type : values()) {
             if (type.elementName.equals(elementName)) {
index 3e063e24b14795c40d65c64506ceed3bd5113265..1fba8079649e03a8930150b859b532654721f7cb 100644 (file)
@@ -41,17 +41,56 @@ public class PDFElementMapping extends ElementMapping {
     protected void initialize() {
         if (foObjs == null) {
             foObjs = new java.util.HashMap<String, Maker>();
+            // pdf:action
+            foObjs.put(PDFDictionaryType.Action.elementName(), new PDFActionElementMaker());
+            // pdf:array
+            foObjs.put(PDFObjectType.Array.elementName(), new PDFArrayElementMaker());
+            // pdf:boolean
+            foObjs.put(PDFObjectType.Boolean.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Boolean));
+            // pdf:catalog
+            foObjs.put(PDFDictionaryType.Catalog.elementName(), new PDFCatalogElementMaker());
+            // pdf:dictionary
+            foObjs.put(PDFDictionaryType.Dictionary.elementName(), new PDFDictionaryElementMaker());
             // pdf:embedded-file
             foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileElementMaker());
-            // pdf:{catalog,page} et al.
-            for (PDFDictionaryType type : PDFDictionaryType.values()) {
-                foObjs.put(type.elementName(), new PDFDictionaryElementMaker(type));
-            }
-            for (PDFDictionaryEntryType type : PDFDictionaryEntryType.values()) {
-                if (type != PDFDictionaryEntryType.Dictionary) {
-                    foObjs.put(type.elementName(), new PDFDictionaryEntryElementMaker(type));
-                }
-            }
+            // pdf:name
+            foObjs.put(PDFObjectType.Name.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Name));
+            // pdf:number
+            foObjs.put(PDFObjectType.Number.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Number));
+            // pdf:navigator
+            foObjs.put(PDFDictionaryType.Navigator.elementName(), new PDFNavigatorElementMaker());
+            // pdf:layer
+            foObjs.put(PDFDictionaryType.Layer.elementName(), new PDFLayerElementMaker());
+            // pdf:page
+            foObjs.put(PDFDictionaryType.Page.elementName(), new PDFPageElementMaker());
+            // pdf:reference
+            foObjs.put(PDFObjectType.Reference.elementName(), new PDFReferenceElementMaker());
+            // pdf:string
+            foObjs.put(PDFObjectType.String.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.String));
+        }
+    }
+
+    static class PDFActionElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFActionElement(parent);
+        }
+    }
+
+    static class PDFArrayElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFArrayElement(parent);
+        }
+    }
+
+    static class PDFCatalogElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFCatalogElement(parent);
+        }
+    }
+
+    static class PDFDictionaryElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFDictionaryElement(parent, PDFDictionaryType.Dictionary);
         }
     }
 
@@ -61,23 +100,38 @@ public class PDFElementMapping extends ElementMapping {
         }
     }
 
-    static class PDFDictionaryElementMaker extends ElementMapping.Maker {
-        private PDFDictionaryType dictionaryType;
-        PDFDictionaryElementMaker(PDFDictionaryType dictionaryType) {
-            this.dictionaryType = dictionaryType;
+    static class PDFLayerElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFLayerElement(parent);
         }
+    }
+
+    static class PDFNavigatorElementMaker extends ElementMapping.Maker {
         public FONode make(FONode parent) {
-            return new PDFDictionaryElement(parent, dictionaryType);
+            return new PDFNavigatorElement(parent);
         }
     }
 
-    static class PDFDictionaryEntryElementMaker extends ElementMapping.Maker {
-        private PDFDictionaryEntryType entryType;
-        PDFDictionaryEntryElementMaker(PDFDictionaryEntryType entryType) {
+    static class PDFPageElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFPageElement(parent);
+        }
+    }
+
+    static class PDFCollectionEntryElementMaker extends ElementMapping.Maker {
+        private PDFObjectType entryType;
+        PDFCollectionEntryElementMaker(PDFObjectType entryType) {
             this.entryType = entryType;
         }
         public FONode make(FONode parent) {
-            return new PDFDictionaryEntryElement(parent, entryType);
+            return new PDFCollectionEntryElement(parent, entryType);
         }
     }
+
+    static class PDFReferenceElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFReferenceElement(parent);
+        }
+    }
+
 }
index f14f1e7d6ea82535d3bddb633ad8504d85d5a7ce..2fd14058efa47ddda174263ea0315fb65fb9ec66 100644 (file)
@@ -49,7 +49,7 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
     private Attributes lastAttributes;
 
     // PDFDictionaryAttachment related
-    private Stack<PDFDictionaryExtension> dictionaries = new Stack<PDFDictionaryExtension>();
+    private Stack<PDFCollectionExtension> collections = new Stack<PDFCollectionExtension>();
     private boolean captureContent;
     private StringBuffer characters;
 
@@ -58,29 +58,78 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
         if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
             if (localName.equals(PDFEmbeddedFileAttachment.ELEMENT)) {
                 lastAttributes = new AttributesImpl(attributes);
-            } else if (PDFDictionaryType.hasValueOfElementName(localName)) {
-                PDFDictionaryExtension dictionary = new PDFDictionaryExtension(PDFDictionaryType.valueOfElementName(localName));
-                String key = attributes.getValue(PDFDictionaryEntryExtension.PROPERTY_KEY);
+            } else if (PDFDictionaryType.Action.elementName().equals(localName)) {
+                PDFActionExtension action = new PDFActionExtension();
+                String id = attributes.getValue(PDFDictionaryElement.ATT_ID);
+                if (id != null) {
+                    action.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+                }
+                String type = attributes.getValue(PDFActionElement.ATT_TYPE);
+                if (type != null) {
+                    action.setProperty(PDFActionExtension.PROPERTY_TYPE, type);
+                }
+                collections.push(action);
+            } else if (PDFObjectType.Array.elementName().equals(localName)) {
+                PDFArrayExtension array = new PDFArrayExtension();
+                String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY);
+                if (key != null) {
+                    array.setKey(key);
+                }
+                collections.push(array);
+            } else if (PDFDictionaryType.Catalog.elementName().equals(localName)) {
+                PDFCatalogExtension catalog = new PDFCatalogExtension();
+                collections.push(catalog);
+            } else if (PDFDictionaryType.Dictionary.elementName().equals(localName)) {
+                PDFDictionaryExtension dictionary = new PDFDictionaryExtension();
+                String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY);
                 if (key != null) {
                     dictionary.setKey(key);
                 }
-                if (dictionary.getDictionaryType() == PDFDictionaryType.Page) {
-                    String pageNumbers = attributes.getValue(PDFDictionaryElement.ATT_PAGE_NUMBERS);
-                    if (pageNumbers != null) {
-                        dictionary.setProperty(PDFDictionaryElement.ATT_PAGE_NUMBERS, pageNumbers);
-                    }
+                collections.push(dictionary);
+            } else if (PDFDictionaryType.Layer.elementName().equals(localName)) {
+                PDFLayerExtension layer = new PDFLayerExtension();
+                String id = attributes.getValue(PDFDictionaryElement.ATT_ID);
+                if (id != null) {
+                    layer.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+                }
+                collections.push(layer);
+            } else if (PDFDictionaryType.Navigator.elementName().equals(localName)) {
+                PDFNavigatorExtension navigator = new PDFNavigatorExtension();
+                String id = attributes.getValue(PDFDictionaryElement.ATT_ID);
+                if (id != null) {
+                    navigator.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+                }
+                collections.push(navigator);
+            } else if (PDFDictionaryType.Page.elementName().equals(localName)) {
+                PDFPageExtension page = new PDFPageExtension();
+                String pageNumbers = attributes.getValue(PDFPageElement.ATT_PAGE_NUMBERS);
+                if (pageNumbers != null) {
+                    page.setProperty(PDFPageExtension.PROPERTY_PAGE_NUMBERS, pageNumbers);
+                }
+                collections.push(page);
+            } else if (PDFObjectType.hasValueOfElementName(localName)) {
+                PDFCollectionEntryExtension entry;
+                if (PDFObjectType.Reference.elementName().equals(localName)) {
+                    entry = new PDFReferenceExtension();
+                } else {
+                    entry = new PDFCollectionEntryExtension(PDFObjectType.valueOfElementName(localName));
                 }
-                dictionaries.push(dictionary);
-            } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) {
-                PDFDictionaryEntryExtension entry = new PDFDictionaryEntryExtension(PDFDictionaryEntryType.valueOfElementName(localName));
-                String key = attributes.getValue(PDFDictionaryEntryElement.ATT_KEY);
+                String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY);
                 if (key != null) {
                     entry.setKey(key);
                 }
-                if (!dictionaries.empty()) {
-                    PDFDictionaryExtension dictionary = dictionaries.peek();
-                    dictionary.addEntry(entry);
-                    captureContent = true;
+                if (entry instanceof PDFReferenceExtension) {
+                    String refid = attributes.getValue(PDFReferenceElement.ATT_REFID);
+                    if (refid != null) {
+                        ((PDFReferenceExtension) entry).setReferenceId(refid);
+                    }
+                }
+                if (!collections.empty()) {
+                    PDFCollectionExtension collection = collections.peek();
+                    collection.addEntry(entry);
+                    if (!(entry instanceof PDFReferenceExtension)) {
+                        captureContent = true;
+                    }
                 }
             } else {
                 throw new SAXException("Unhandled element " + localName + " in namespace: " + uri);
@@ -107,33 +156,48 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle
                 String name = lastAttributes.getValue("name");
                 String src = lastAttributes.getValue("src");
                 String desc = lastAttributes.getValue("description");
+                this.lastAttributes = null;
                 this.returnedObject = new PDFEmbeddedFileAttachment(name, src, desc);
             } else if (PDFDictionaryType.hasValueOfElementName(localName)) {
-                if (!dictionaries.empty()) {
-                    PDFDictionaryExtension dictionary = dictionaries.pop();
-                    if ((dictionary.getDictionaryType() == PDFDictionaryType.Catalog) || (dictionary.getDictionaryType() == PDFDictionaryType.Page)) {
+                if (!collections.empty() && (collections.peek() instanceof PDFDictionaryExtension)) {
+                    PDFDictionaryExtension dictionary = (PDFDictionaryExtension) collections.pop();
+                    if (!collections.empty()) {
+                        PDFCollectionExtension collectionOuter = collections.peek();
+                        collectionOuter.addEntry(dictionary);
+                    } else if (dictionary.getDictionaryType() != PDFDictionaryType.Dictionary) {
                         this.returnedObject = new PDFDictionaryAttachment(dictionary);
-                    } else if (!dictionaries.empty()) {
-                        PDFDictionaryExtension dictionaryOuter = dictionaries.peek();
-                        dictionaryOuter.addEntry(dictionary);
+                    } else {
+                        throw new SAXException(new IllegalStateException("generic dictionary not permitted at outer level"));
+                    }
+                } else {
+                    throw new SAXException(new IllegalStateException("collections stack is empty or not a dictionary"));
+                }
+            } else if (PDFObjectType.Array.elementName().equals(localName)) {
+                if (!collections.empty() && (collections.peek() instanceof PDFArrayExtension)) {
+                    PDFArrayExtension array = (PDFArrayExtension) collections.pop();
+                    if (!collections.empty()) {
+                        PDFCollectionExtension collectionOuter = collections.peek();
+                        collectionOuter.addEntry(array);
+                    } else {
+                        throw new SAXException(new IllegalStateException("array not permitted at outer level"));
                     }
                 } else {
-                    throw new SAXException(new IllegalStateException("no active dictionary"));
+                    throw new SAXException(new IllegalStateException("collections stack is empty or not an array"));
                 }
-            } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) {
-                if (!dictionaries.empty()) {
-                    PDFDictionaryExtension dictionary = dictionaries.peek();
-                    PDFDictionaryEntryExtension entry = dictionary.getLastEntry();
+            } else if (PDFObjectType.hasValueOfElementName(localName)) {
+                if (!collections.empty()) {
+                    PDFCollectionExtension collection = collections.peek();
+                    PDFCollectionEntryExtension entry = collection.getLastEntry();
                     if (entry != null) {
                         if (characters != null) {
                             entry.setValue(characters.toString());
                             characters = null;
                         }
                     } else {
-                        throw new SAXException(new IllegalStateException("no active entry"));
+                        throw new SAXException(new IllegalStateException("no current entry"));
                     }
                 } else {
-                    throw new SAXException(new IllegalStateException("no active dictionary"));
+                    throw new SAXException(new IllegalStateException("entry not permitted at outer level"));
                 }
             }
         }
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java
new file mode 100644 (file)
index 0000000..dab0ecf
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:layer.
+ */
+public class PDFLayerElement extends PDFDictionaryElement {
+
+    PDFLayerElement(FONode parent) {
+        super(parent, PDFDictionaryType.Layer);
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java
new file mode 100644 (file)
index 0000000..d6cc27a
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFLayerExtension extends PDFDictionaryExtension {
+
+    PDFLayerExtension() {
+        super(PDFDictionaryType.Layer);
+    }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java
new file mode 100644 (file)
index 0000000..5c5b779
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:navigator.
+ */
+public class PDFNavigatorElement extends PDFDictionaryElement {
+
+    PDFNavigatorElement(FONode parent) {
+        super(parent, PDFDictionaryType.Navigator);
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java
new file mode 100644 (file)
index 0000000..ee9dfa7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFNavigatorExtension extends PDFDictionaryExtension {
+
+    PDFNavigatorExtension() {
+        super(PDFDictionaryType.Navigator);
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java
new file mode 100644 (file)
index 0000000..5447d87
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.apache.fop.util.XMLUtil;
+
+// CSOFF: LineLengthCheck
+
+public class PDFObjectExtension {
+
+    private PDFObjectType type;
+    private Object value;
+
+    PDFObjectExtension(PDFObjectType type) {
+        this.type = type;
+    }
+
+    public PDFObjectType getType() {
+        return type;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Obtain entry value as Boolean.
+     * @return entry value
+     */
+    public Boolean getValueAsBoolean() {
+        Object value = getValue();
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        } else if (value instanceof String) {
+            return Boolean.valueOf((String)value);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Obtain entry value as Number.
+     * @return entry value
+     */
+    public Number getValueAsNumber() {
+        Object value = getValue();
+        if (value instanceof Number) {
+            return (Number) value;
+        } else if (value instanceof String) {
+            double d = Double.parseDouble((String) value);
+            if (Math.abs(Math.floor(d) - d) < 1E-10) {
+                return Long.valueOf((long) d);
+            } else {
+                return Double.valueOf(d);
+            }
+        } else {
+            return Integer.valueOf(0);
+        }
+    }
+
+    public String getValueAsString() {
+        Object value = getValue();
+        if (value == null) {
+            return null;
+        } else if (value instanceof String) {
+            return (String) value;
+        } else {
+            return value.toString();
+        }
+    }
+
+    public String getValueAsXMLEscapedString() {
+        return XMLUtil.escape(getValueAsString());
+    }
+
+    public String getElementName() {
+        return type.elementName();
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java
new file mode 100644 (file)
index 0000000..c193a3b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Enumeration type for leaf PDF object extension types used as singletons,
+ * dictionary entries, or array entries.
+ */
+public enum PDFObjectType {
+    Array("array"),                     // array valued entry
+    Boolean("boolean"),                 // boolean valued entry
+    Dictionary("dictionary"),           // dictionary valued entry
+    Name("name"),                       // name valued entry
+    Number("number"),                   // number valued entry
+    Reference("reference"),             // indirect object reference entry
+    String("string");                   // string valued entry
+
+    private String elementName;
+    PDFObjectType(String elementName) {
+        this.elementName = elementName;
+    }
+    public String elementName() {
+        return elementName;
+    }
+    static PDFObjectType valueOfElementName(String elementName) {
+        for (PDFObjectType type : values()) {
+            if (type.elementName.equals(elementName)) {
+                return type;
+            }
+        }
+        throw new IllegalArgumentException();
+    }
+    static boolean hasValueOfElementName(String elementName) {
+        try {
+            valueOfElementName(elementName);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java
new file mode 100644 (file)
index 0000000..28a966e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:page.
+ */
+public class PDFPageElement extends PDFDictionaryElement {
+
+    public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS;
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    PDFPageElement(FONode parent) {
+        super(parent, PDFDictionaryType.Page);
+    }
+
+    @Override
+    public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+        super.processNode(elementName, locator, attlist, propertyList);
+        String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS);
+        if (pageNumbers != null) {
+            getDictionaryExtension().setProperty(PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS, pageNumbers);
+        }
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
+            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM");
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java
new file mode 100644 (file)
index 0000000..0726a4e
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFPageExtension extends PDFDictionaryExtension {
+
+    public static final String PROPERTY_PAGE_NUMBERS = "page-numbers";
+
+    PDFPageExtension() {
+        super(PDFDictionaryType.Page);
+    }
+
+    /**
+     * Determine if page dictionary and page number matches.
+     * @param pageNumber page number, where first page number is 1
+     * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property
+     */
+    public boolean matchesPageNumber(int pageNumber) {
+        String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS);
+        if ((pageNumbers == null) || (pageNumbers.length() == 0)) {
+            return false;
+        } else if (pageNumbers.equals("*")) {
+            return true;
+        } else {
+            for (String interval : pageNumbers.split("\\s*,\\s*")) {
+                String[] components = interval.split("\\s*-\\s*");
+                if (components.length < 1) {
+                    continue;
+                } else {
+                    try {
+                        int start = Integer.parseInt(components[0]);
+                        int end = 0;
+                        if (components.length > 1) {
+                            if (!components[1].equals("LAST")) {
+                                end = Integer.parseInt(components[1]);
+                            }
+                        }
+                        if ((end == 0) && (pageNumber == start)) {
+                            return true;
+                        } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) {
+                            return true;
+                        } else {
+                            continue;
+                        }
+                    } catch (NumberFormatException e) {
+                        continue;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java
new file mode 100644 (file)
index 0000000..37aeeb8
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:reference.
+ */
+public class PDFReferenceElement extends PDFCollectionEntryElement {
+
+    public static final String ATT_REFID = PDFReferenceExtension.PROPERTY_REFID;
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    PDFReferenceElement(FONode parent) {
+        super(parent, PDFObjectType.Reference);
+    }
+
+    @Override
+    public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+        super.processNode(elementName, locator, attlist, propertyList);
+        String refid = attlist.getValue(ATT_REFID);
+        if (refid == null) {
+            missingPropertyError(ATT_REFID);
+        } else if (refid.length() == 0) {
+            invalidPropertyValueError(ATT_REFID, refid, null);
+        } else {
+            ((PDFReferenceExtension) getExtension()).setReferenceId(refid);
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java
new file mode 100644 (file)
index 0000000..09621c0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFReferenceExtension extends PDFCollectionEntryExtension {
+
+    public static final String PROPERTY_REFID = "refid";
+
+    private String refid;
+    private Object resolvedReference;
+
+    PDFReferenceExtension() {
+        super(PDFObjectType.Reference);
+    }
+
+    @Override
+    public void setValue(Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue() {
+        return this;
+    }
+
+    public String getReferenceId() {
+        return refid;
+    }
+
+    public void setReferenceId(String refid) {
+        this.refid = refid;
+    }
+
+    public Object getResolvedReference() {
+        return resolvedReference;
+    }
+
+    public void setResolvedReference(Object resolvedReference) {
+        this.resolvedReference = resolvedReference;
+    }
+
+}
index 3d1887f2dec659d6efa3e304f78f588647ae09a4..547662c47812c5085bf5fe2d638961273d875d5e 100644 (file)
@@ -116,7 +116,7 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
         try {
             PSGenerator generator = getGenerator();
             saveGraphicsState();
index bf4a46e1936737349e05bb233c440b9e8e1fc92f..5b398711f16207e6c6882ea9c7d4cd0773cd1992 100644 (file)
@@ -582,6 +582,14 @@ public class TXTRenderer extends AbstractPathOrientedRenderer {
         currentState.pop();
     }
 
+    /** {@inheritDoc} */
+    protected void startLayer(String layer) {
+    }
+
+    /** {@inheritDoc} */
+    protected void endLayer() {
+    }
+
     /** {@inheritDoc} */
     protected void concatenateTransformationMatrix(AffineTransform at) {
         currentState.push(new CTM(UnitConv.ptToMpt(at)));
index 2212da4347ff6bd3b678eb0bc5d14a17803c4f9a..68e2e3c62bcffebb3f03d7c51f7fbd00afe7c299 100644 (file)
@@ -545,6 +545,16 @@ public class XMLRenderer extends AbstractXMLRenderer {
         //only necessary for graphical output
     }
 
+    /** {@inheritDoc} */
+    protected void startLayer(String layer) {
+        //only necessary for graphical output
+    }
+
+    /** {@inheritDoc} */
+    protected void endLayer() {
+        //only necessary for graphical output
+    }
+
     /**
      * {@inheritDoc}
      *          org.apache.fop.area.inline.InlineArea)
index 96c3633e669a3c44c0f9602383e5854994a76762..3e966d202a6485bdcc3ca5f7710315f7304fcc3d 100644 (file)
@@ -24,7 +24,6 @@ import java.awt.geom.AffineTransform;
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Stack;
 
@@ -36,10 +35,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
     private static final long serialVersionUID = 5998356138437094188L;
 
     /** current state data */
-    private AbstractData data = null;
+    private AbstractData data;
 
     /** the state stack */
-    private StateStack/*<AbstractData>*/ stateStack = new StateStack/*<AbstractData>*/();
+    private StateStack<AbstractData> stateStack = new StateStack<AbstractData>();
 
     /**
      * Instantiates a new state data object
@@ -216,8 +215,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
      */
     public AffineTransform getTransform() {
        AffineTransform at = new AffineTransform();
-       for (Iterator iter = stateStack.iterator(); iter.hasNext();) {
-           AbstractData data = (AbstractData)iter.next();
+       for (AbstractData data : stateStack) {
            AffineTransform stackTrans = data.getTransform();
            at.concatenate(stackTrans);
        }
@@ -249,7 +247,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
        if (stateStack.isEmpty()) {
            return null;
        } else {
-           AbstractData baseData = (AbstractData)stateStack.get(0);
+           AbstractData baseData = stateStack.get(0);
            return (AffineTransform) baseData.getTransform().clone();
        }
     }
@@ -297,7 +295,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
      */
     public AbstractData restore() {
         if (!stateStack.isEmpty()) {
-            setData((AbstractData)stateStack.pop());
+            setData(stateStack.pop());
             return this.data;
         } else {
             return null;
@@ -310,12 +308,11 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
      *
      * @param dataList a state data list
      */
-    public void saveAll(List/*<AbstractData>*/ dataList) {
-        Iterator it = dataList.iterator();
-        while (it.hasNext()) {
+    public void saveAll(List<AbstractData> dataList) {
+        for (AbstractData data : dataList) {
             // save current data on stack
             save();
-            setData((AbstractData)it.next());
+            setData(data);
         }
     }
 
@@ -325,8 +322,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
      *
      * @return a list of state data popped from the stack
      */
-    public List/*<AbstractData>*/ restoreAll() {
-        List/*<AbstractData>*/ dataList = new java.util.ArrayList/*<AbstractData>*/();
+    public List<AbstractData> restoreAll() {
+        List<AbstractData> dataList = new java.util.ArrayList<AbstractData>();
         AbstractData data;
         while (true) {
             data = getData();
@@ -361,7 +358,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
      *
      * @return the state stack
      */
-    protected Stack/*<AbstractData>*/ getStateStack() {
+    protected Stack<AbstractData> getStateStack() {
         return this.stateStack;
     }
 
@@ -369,8 +366,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
     @Override
     public Object clone() {
         AbstractPaintingState state = instantiate();
-        state.stateStack = new StateStack(this.stateStack);
-        state.data = (AbstractData)this.data.clone();
+        state.stateStack = new StateStack<AbstractData>(this.stateStack);
+        if (this.data != null) {
+            state.data = (AbstractData)this.data.clone();
+        }
         return state;
     }
 
@@ -385,7 +384,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
     /**
      * A stack implementation which holds state objects
      */
-    public class StateStack extends java.util.Stack {
+    public class StateStack<E> extends java.util.Stack<E> {
 
         private static final long serialVersionUID = 4897178211223823041L;
 
@@ -393,7 +392,6 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
          * Default constructor
          */
         public StateStack() {
-            super();
         }
 
         /**
@@ -419,25 +417,28 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
         private static final long serialVersionUID = 5208418041189828624L;
 
         /** The current color */
-        protected Color color = null;
+        protected Color color;
 
         /** The current background color */
-        protected Color backColor = null;
+        protected Color backColor;
 
         /** The current font name */
-        protected String fontName = null;
+        protected String fontName;
 
         /** The current font size */
-        protected int fontSize = 0;
+        protected int fontSize;
 
         /** The current line width */
-        protected float lineWidth = 0;
+        protected float lineWidth;
 
         /** The dash array for the current basic stroke (line type) */
-        protected float[] dashArray = null;
+        protected float[] dashArray;
 
         /** The current transform */
-        protected AffineTransform transform = null;
+        protected AffineTransform transform;
+
+        /** The current (optional content group) layer. */
+        protected String layer;
 
         /**
          * Returns a newly create data object
@@ -485,6 +486,18 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
             transform = new AffineTransform();
         }
 
+        public void setLayer(String layer) {
+            if (layer != null) {
+                this.layer = layer;
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        public String getLayer() {
+            return this.layer;
+        }
+
         /**
          * Returns the derived rotation from the current transform
          *
@@ -523,6 +536,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
                 this.transform = new AffineTransform();
             }
             data.transform = new AffineTransform(this.transform);
+            data.layer = this.layer;
             return data;
         }
 
@@ -535,7 +549,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
                 + ", fontSize=" + fontSize
                 + ", lineWidth=" + lineWidth
                 + ", dashArray=" + dashArray
-                + ", transform=" + transform;
+                + ", transform=" + transform
+                + ", layer=" + layer;
         }
     }
 }
index ebd143a69f0a1ab7bfd3ab05805e126ec413e7a3..fb7ce26777da3e55e7cb9cb643d9740e32226959 100644 (file)
@@ -146,16 +146,16 @@ public class SVGPainter extends AbstractIFPainter<AbstractSVGDocumentHandler>
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform[] transforms) throws IFException {
-        startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms));
+    public void startGroup(AffineTransform[] transforms, String layer) throws IFException {
+        startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms), layer);
     }
 
     /** {@inheritDoc} */
-    public void startGroup(AffineTransform transform) throws IFException {
-        startGroup(SVGUtil.formatAffineTransformMptToPt(transform));
+    public void startGroup(AffineTransform transform, String layer) throws IFException {
+        startGroup(SVGUtil.formatAffineTransformMptToPt(transform), layer);
     }
 
-    private void startGroup(String transform) throws IFException {
+    private void startGroup(String transform, String layer) throws IFException {
         try {
             AttributesImpl atts = new AttributesImpl();
             if (transform != null && transform.length() > 0) {
index 352f487c31bd6e36c73307c246b575c72b7267b9..91b9c78d3b871bcd13f4bc84ee0227e4993d69d3 100644 (file)
       users, i.e. when the behaviour changes and could affect the layout of existing
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
-    <release version="FOP Trunk" date="TBD">
+    <release version="FOP Trunk" date="01 November 2013">
+      <action context="Renderers" dev="GA" type="add" fixes-bug="FOP-2301">
+          Enable support for PDF sub-page transitions.
+      </action>
       <action context="Layout" dev="GA" type="fix" fixes-bug="FOP-2310">
           Fix misplaced table cell border in WM RTL context.
       </action>
index d156b908cfc32ba8d0aec4e519bfb8bc9b026363..ee3498a0358941e0eedd78a9cbd57e2fbc4b77de 100644 (file)
@@ -35,6 +35,8 @@ import static org.junit.Assert.assertTrue;
  */
 public class EmbedFontInfoTestCase {
 
+    public EmbedFontInfoTestCase() {}
+
     private EmbedFontInfo sut;
 
     private final URI metricsURI = URI.create("test/resources/fonts/ttf/glb12.ttf.xml");
index ad48c46c47933d4bacd132fd27e7997fb2e08327..1baba49dfbe5725bf76d2da768ce865d1bd5e454 100644 (file)
@@ -29,7 +29,7 @@ import org.junit.runners.Suite.SuiteClasses;
 @RunWith(Suite.class)
 @SuiteClasses({
         FontManagerConfiguratorTestCase.class,
-        EmbedFontInfo.class,
+        EmbedFontInfoTestCase.class,
         FontEventProcessingTestCase.class,
         FontManagerConfiguratorTestCase.class
 })
index 08d841edeaa2b21262e8ab4a7dc0204f5427e042..49c1e6dab2811f435061ef115e26b5aaed8754a3 100644 (file)
@@ -32,7 +32,7 @@ import java.util.List;
 public class PDFDestsTestCase extends PDFObjectTestCase {
 
     private PDFDests dests = new PDFDests();
-    private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>\n";
+    private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>";
 
     @Before
     public void setUp() {
index 3f84fac2e5236410e88676a0b43714f8f5a1d80d..00224e93e4705399b750beed8c205fb2ac35cd31 100644 (file)
@@ -47,7 +47,7 @@ public class PDFDictionaryTestCase extends PDFObjectTestCase {
                                   + "  /array [1 (two) 20]\n"
                                   + "  /number 20\n"
                                   + "  /null null\n"
-                                  + ">>\n";
+                                  + ">>";
 
     @Before
     public void setUp() {
index e982bf2464f4d9de85311820f84d4aa082b1aacd..a8be7ee74b684494c35bf203d840dc1c18983030 100644 (file)
@@ -37,11 +37,11 @@ public class PDFPageLabelsTestCase {
         int index = 0;
         StringBuilder expected = new StringBuilder();
         expected.append("[");
-        expected.append(index + " << /S /r >>\n");
+        expected.append(index + " << /S /r >>");
         pageLabels.addPageLabel(index++, "i");
         pageLabels.addPageLabel(index++, "ii");
         pageLabels.addPageLabel(index++, "iii");
-        expected.append(" " + index + " << /S /D >>\n");
+        expected.append(" " + index + " << /S /D >>");
         pageLabels.addPageLabel(index++, "1");
         pageLabels.addPageLabel(index++, "2");
         pageLabels.addPageLabel(index++, "3");
@@ -52,33 +52,33 @@ public class PDFPageLabelsTestCase {
         pageLabels.addPageLabel(index++, "8");
         pageLabels.addPageLabel(index++, "9");
         pageLabels.addPageLabel(index++, "10");
-        expected.append(" " + index + " << /S /A >>\n");
+        expected.append(" " + index + " << /S /A >>");
         pageLabels.addPageLabel(index++, "A");
         pageLabels.addPageLabel(index++, "B");
-        expected.append(" " + index + " << /S /R /St 100 >>\n");
+        expected.append(" " + index + " << /S /R /St 100 >>");
         pageLabels.addPageLabel(index++, "C");
-        expected.append(" " + index + " << /S /R /St 500 >>\n");
+        expected.append(" " + index + " << /S /R /St 500 >>");
         pageLabels.addPageLabel(index++, "D");
-        expected.append(" " + index + " << /S /A /St 5 >>\n");
+        expected.append(" " + index + " << /S /A /St 5 >>");
         pageLabels.addPageLabel(index++, "E");
         pageLabels.addPageLabel(index++, "F");
         pageLabels.addPageLabel(index++, "G");
-        expected.append(" " + index + " << /P (aa) >>\n");
+        expected.append(" " + index + " << /P (aa) >>");
         pageLabels.addPageLabel(index++, "aa");
-        expected.append(" " + index + " << /P (ab) >>\n");
+        expected.append(" " + index + " << /P (ab) >>");
         pageLabels.addPageLabel(index++, "ab");
-        expected.append(" " + index + " << /P (ac) >>\n");
+        expected.append(" " + index + " << /P (ac) >>");
         pageLabels.addPageLabel(index++, "ac");
-        expected.append(" " + index + " << /S /a >>\n");
+        expected.append(" " + index + " << /S /a >>");
         pageLabels.addPageLabel(index++, "a");
         pageLabels.addPageLabel(index++, "b");
-        expected.append(" " + index + " << /S /R /St 2 >>\n");
+        expected.append(" " + index + " << /S /R /St 2 >>");
         pageLabels.addPageLabel(index++, "II");
-        expected.append(" " + index + " << /S /R /St 12 >>\n");
+        expected.append(" " + index + " << /S /R /St 12 >>");
         pageLabels.addPageLabel(index++, "XII");
-        expected.append(" " + index + " <<\n  /P (00)\n  /S /D\n  /St 9\n>>\n");
+        expected.append(" " + index + " <<\n  /P (00)\n  /S /D\n  /St 9\n>>");
         pageLabels.addPageLabel(index++, "009");
-        expected.append(" " + index + " <<\n  /P (0)\n  /S /D\n  /St 10\n>>\n");
+        expected.append(" " + index + " <<\n  /P (0)\n  /S /D\n  /St 10\n>>");
         pageLabels.addPageLabel(index++, "010");
         pageLabels.addPageLabel(index++, "011");
         expected.append("]");
index 93dcea5119e944b71b748b6a9559ca5e70f478c4..20e38a6000d864d8437aa3dfe2b1953c67454a65 100644 (file)
@@ -118,7 +118,7 @@ public class PDFStreamTestCase {
 
     private byte[] createSampleStreamData() throws IOException {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        stream.write("stream\n".getBytes("US-ASCII"));
+        stream.write("\nstream\n".getBytes("US-ASCII"));
         stream.write(createSampleData());
         stream.write("\nendstream".getBytes("US-ASCII"));
         return stream.toByteArray();
index ceff96a91c697d3513d24a4fabcdca12c74e7159..12f6e3c1b310480bb5091eb7364f13e3cb80edd1 100644 (file)
@@ -73,7 +73,7 @@ public class CrossReferenceTableTestCase extends CrossReferenceObjectTest {
                 .append("  /Info 2 0 R\n")
                 .append("  /ID [<0123456789ABCDEF> <0123456789ABCDEF>]\n")
                 .append("  /Size ").append(Integer.toString(offsets.size() + 1)).append('\n')
-                .append(">>\n");
+                .append(">>");
         return getBytes(expected);
     }
 
index 592335648f030aa86b444d7a865dd9f56f82e042..be8b5d718aba013826874d4c1ce8c621bcb37d15 100644 (file)
@@ -51,7 +51,7 @@ public class AbstractIFPainterTestCase {
             public void endViewport() throws IFException {
             }
 
-            public void startGroup(AffineTransform transform) throws IFException {
+            public void startGroup(AffineTransform transform, String layer) throws IFException {
             }
 
             public void endGroup() throws IFException {
diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml
new file mode 100644 (file)
index 0000000..486b860
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks the PDF dictionary extensions related to optional content groups (layers).
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf"
+      xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="simple">
+          <fo:region-body/>
+          <fo:region-before/>
+          <fo:region-after/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:declarations>
+        <!-- Optional Content Group Layers -->
+        <pdf:layer id="layer1">
+          <pdf:string key="Name">Bullet 1</pdf:string>
+        </pdf:layer>
+        <pdf:layer id="layer2">
+          <pdf:string key="Name">Bullet 2</pdf:string>
+        </pdf:layer>
+        <!-- Document Catalog's Optional Content (Layers) Directory and Default State -->
+        <pdf:catalog>
+          <pdf:dictionary key="OCProperties">
+            <!-- Directory of OCGs (layers) in Document -->
+            <pdf:array key="OCGs">
+              <pdf:reference refid="layer1"/>
+              <pdf:reference refid="layer2"/>
+            </pdf:array>
+            <!-- Default State for OCGs -->
+            <pdf:dictionary key="D">
+              <pdf:string key="Name">Default</pdf:string>
+              <pdf:name key="BaseState">OFF</pdf:name>
+            </pdf:dictionary>
+          </pdf:dictionary>
+        </pdf:catalog>
+      </fo:declarations>
+      <fo:page-sequence master-reference="simple">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block fox:layer="layer1">
+            <fo:block>BULLET 1A</fo:block>
+            <fo:block>BULLET 1B</fo:block>
+          </fo:block>
+          <fo:block fox:layer="layer2">BULLET 2</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks xmlns:pdf="apache:fop:extensions:pdf">
+    <eval expected="layer1" xpath="//flow/block[1]/@layer"/>
+    <eval expected="layer2" xpath="//flow/block[2]/@layer"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml
new file mode 100644 (file)
index 0000000..ee50614
--- /dev/null
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks the PDF dictionary extensions related to optional content groups (layers),
+      including navigator and action dictionaries.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf"
+             xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="simple">
+          <fo:region-body/>
+          <fo:region-before/>
+          <fo:region-after/>
+          <!-- Initial Layers Navigation Node for Page 1 -->
+          <pdf:page page-numbers="1">
+            <pdf:reference key="PresSteps" refid="navInitial"/>
+          </pdf:page>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:declarations>
+        <!-- Optional Content Group Layers -->
+        <pdf:layer id="layer1">
+          <pdf:string key="Name">Bullet 1</pdf:string>
+        </pdf:layer>
+        <pdf:layer id="layer2">
+          <pdf:string key="Name">Bullet 2</pdf:string>
+        </pdf:layer>
+        <!-- Navigator Actions -->
+        <pdf:action type="SetOCGState" id="setStateInitial">
+          <pdf:array key="State">
+            <pdf:name>OFF</pdf:name>
+            <pdf:reference refid="layer1"/>
+            <pdf:reference refid="layer2"/>
+          </pdf:array>
+        </pdf:action>
+        <pdf:action type="SetOCGState" id="setStateBullet1">
+          <pdf:array key="State">
+            <pdf:name>OFF</pdf:name>
+            <pdf:reference refid="layer2"/>
+            <pdf:name>ON</pdf:name>
+            <pdf:reference refid="layer1"/>
+          </pdf:array>
+        </pdf:action>
+        <pdf:action type="SetOCGState" id="setStateBullet2">
+          <pdf:array key="State">
+            <pdf:name>OFF</pdf:name>
+            <pdf:reference refid="layer1"/>
+            <pdf:name>ON</pdf:name>
+            <pdf:reference refid="layer2"/>
+          </pdf:array>
+        </pdf:action>
+        <!-- Navigators -->
+        <pdf:navigator id="navInitial">
+          <pdf:reference key="NA" refid="setStateBullet1"/>
+          <pdf:reference key="Next" refid="navBullet1"/>
+          <pdf:reference key="PA" refid="setStateInitial"/>
+          <pdf:reference key="Prev" refid="navInitial"/>
+        </pdf:navigator>
+        <pdf:navigator id="navBullet1">
+          <pdf:reference key="NA" refid="setStateBullet2"/>
+          <pdf:reference key="Next" refid="navBullet2"/>
+          <pdf:reference key="PA" refid="setStateInitial"/>
+          <pdf:reference key="Prev" refid="navInitial"/>
+        </pdf:navigator>
+        <pdf:navigator id="navBullet2">
+          <pdf:reference key="NA" refid="setStateBullet2"/>
+          <pdf:reference key="Next" refid="navBullet2"/>
+          <pdf:reference key="PA" refid="setStateBullet1"/>
+          <pdf:reference key="Prev" refid="navBullet1"/>
+        </pdf:navigator>
+        <!-- Document Catalog's Optional Content (Layers) Directory and Default State -->
+        <pdf:catalog>
+          <pdf:dictionary key="OCProperties">
+            <!-- Directory of OCGs (layers) in Document -->
+            <pdf:array key="OCGs">
+              <pdf:reference refid="layer1"/>
+              <pdf:reference refid="layer2"/>
+            </pdf:array>
+            <!-- Default State for OCGs -->
+            <pdf:dictionary key="D">
+              <pdf:string key="Name">Default</pdf:string>
+              <pdf:name key="BaseState">OFF</pdf:name>
+            </pdf:dictionary>
+          </pdf:dictionary>
+        </pdf:catalog>
+      </fo:declarations>
+      <fo:page-sequence master-reference="simple">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block fox:layer="layer1">
+            <fo:block>BULLET 1A</fo:block>
+            <fo:block>BULLET 1B</fo:block>
+          </fo:block>
+          <fo:block fox:layer="layer2">BULLET 2</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks xmlns:pdf="apache:fop:extensions:pdf">
+    <eval expected="layer1" xpath="//flow/block[1]/@layer"/>
+    <eval expected="layer2" xpath="//flow/block[2]/@layer"/>
+  </checks>
+</testcase>