]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2298: Enable support for PDF Transitions by defining low-level mechanism to augme...
authorGlenn Adams <gadams@apache.org>
Fri, 13 Sep 2013 13:56:18 +0000 (13:56 +0000)
committerGlenn Adams <gadams@apache.org>
Fri, 13 Sep 2013 13:56:18 +0000 (13:56 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1522934 13f79535-47bb-0310-9956-ffa450edef68

32 files changed:
findbugs-exclude.xml
src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
src/java/org/apache/fop/fo/extensions/ExtensionObj.java [deleted file]
src/java/org/apache/fop/pdf/PDFNumber.java
src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
src/java/org/apache/fop/render/intermediate/IFContext.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/pdf/PDFDocumentHandler.java
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java [deleted file]
src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java [new file with mode: 0644]
src/java/org/apache/fop/tools/fontlist/FontListMain.java
src/java/org/apache/fop/util/GenerationHelperContentHandler.java
src/java/org/apache/fop/util/XMLUtil.java
src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
status.xml
test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml [new file with mode: 0644]

index 2becac909f645758332d1500d28c5ad94afccab4..46e6e167540ce67d3f5d00e80259d92a6f8e7d65 100644 (file)
      </Or>
      <Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED"/>
    </Match>
+   <Match>
+      <Class name="org.apache.fop.render.pdf.extensions.PDFExtensionHandlerFactory"/>
+      <Method name="getSupportedNamespaces"/>
+      <Bug pattern="EI_EXPOSE_REP"/>
+   </Match>
+   <Match>
+      <Class name="org.apache.fop.render.pdf.extensions.PDFDictionaryEntryExtension"/>
+      <Method name="getValueAsNumber"/>
+      <Bug pattern="FE_FLOATING_POINT_EQUALITY"/>
+   </Match>
+   <Match>
+      <Class name="org.apache.fop.render.pdf.extensions.PDFDictionaryType"/>
+      <Method name="hasValueOfElementName"/>
+      <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
+   </Match>
+   <Match>
+      <Class name="org.apache.fop.render.pdf.extensions.PDFDictionaryEntryType"/>
+      <Method name="hasValueOfElementName"/>
+      <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
+   </Match>
 </FindBugsFilter>
index 76d6ebfbc0f79baf4541155205c0f18dec60d409..cabf917ebca5a32bc0af6ac61d3d5d47b42cd98f 100644 (file)
@@ -1,3 +1,4 @@
 org.apache.fop.render.afp.extensions.AFPExtensionHandlerFactory
+org.apache.fop.render.pdf.extensions.PDFExtensionHandlerFactory
 org.apache.fop.render.ps.extensions.PSExtensionHandlerFactory
 org.apache.fop.fo.extensions.xmp.XMPContentHandlerFactory
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionObj.java b/src/java/org/apache/fop/fo/extensions/ExtensionObj.java
deleted file mode 100644 (file)
index c35dcfc..0000000
+++ /dev/null
@@ -1,65 +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.fo.extensions;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
-
-import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.FOEventHandler;
-import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.FObj;
-import org.apache.fop.fo.PropertyList;
-
-/**
- * Base class for pdf bookmark extension objects.
- */
-public abstract class ExtensionObj extends FObj {
-
-    /**
-     * Create a new extension object.
-     *
-     * @param parent the parent formatting object
-     */
-    public ExtensionObj(FONode parent) {
-        super(parent);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void processNode(String elementName, Locator locator,
-                            Attributes attlist, PropertyList pList)
-        throws FOPException {
-    }
-
-    /**
-     * Create a default property list for this element.
-     * @param parent the parent property list
-     * @param foEventHandler an event handler
-     * @return property list
-     * @throws FOPException in case of exception
-     */
-    protected PropertyList createPropertyList(PropertyList parent,
-                FOEventHandler foEventHandler) throws FOPException {
-        return null;
-    }
-}
-
index 1c31f8e9dfb76bf2cec82f06d120afb79dbeaf13..75419488636741540cb965b87badfe22ac630a33 100644 (file)
@@ -29,6 +29,14 @@ public class PDFNumber extends PDFObject {
 
     private Number number;
 
+    public PDFNumber() {
+        this.number = Integer.valueOf(0);
+    }
+
+    public PDFNumber(Number number) {
+        this.number = number;
+    }
+
     /**
      * Returns the number.
      * @return the number
index fcf8554a6d4808e6bfeee8d2c15796898a2c9063..0c3305797fa254b87652af2b2b82c8dbef8b5cc4 100644 (file)
@@ -54,10 +54,10 @@ public abstract class AbstractXMLWritingIFDocumentHandler extends AbstractIFDocu
         if (result instanceof SAXResult) {
             SAXResult saxResult = (SAXResult)result;
             this.handler = new GenerationHelperContentHandler(
-                    saxResult.getHandler(), getMainNamespace());
+                    saxResult.getHandler(), getMainNamespace(), getContext());
         } else {
             this.handler = new GenerationHelperContentHandler(
-                    createContentHandler(result), getMainNamespace());
+                    createContentHandler(result), getMainNamespace(), getContext());
         }
     }
 
index fda0cff3b01867c2b0b004c04e2e2e9029105869..7464e26e0c8581b63ece3fb3f29b447bda406092 100644 (file)
@@ -55,6 +55,8 @@ public class IFContext {
 
     private boolean hyphenated;
 
+    private int pageIndex = -1;
+
     /**
      * Main constructor.
      * @param ua the user agent
@@ -216,4 +218,20 @@ public class IFContext {
         return hyphenated;
     }
 
+    /**
+     * Record current page index.
+     * @param pageIndex a zero based page index or -1 (no page)
+     */
+    public void setPageIndex(int pageIndex) {
+        this.pageIndex = pageIndex;
+    }
+
+    /**
+     * Obtain current page index.
+     * @return a zero based page index or -1 (no page)
+     */
+    public int getPageIndex() {
+        return this.pageIndex;
+    }
+
 }
index 9e6aae700e622c70f1df059d037abcc06dd5bf9d..30ceda10869d444b2336af46062d0b9842776e4c 100644 (file)
@@ -571,6 +571,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
             Dimension dim = new Dimension(viewArea.width, viewArea.height);
 
             establishForeignAttributes(page.getForeignAttributes());
+            documentHandler.getContext().setPageIndex(page.getPageIndex());
             documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(),
                     page.getSimplePageMasterName(), dim);
             resetForeignAttributes();
@@ -598,6 +599,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
 
             establishForeignAttributes(page.getForeignAttributes());
             documentHandler.endPage();
+            documentHandler.getContext().setPageIndex(-1);
             resetForeignAttributes();
         } catch (IFException e) {
             handleIFException(e);
index 62e4cc67d1554fb3fc76be651444696560f8e37d..1ffd428633c6af0ac6b1b6f95c29f8b8614553c0 100644 (file)
@@ -303,6 +303,7 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
             addAttribute(atts, "width", Integer.toString(size.width));
             addAttribute(atts, "height", Integer.toString(size.height));
             addForeignAttributes(atts);
+            getContext().setPageIndex(index);
             handler.startElement(EL_PAGE, atts);
         } catch (SAXException e) {
             throw new IFException("SAX error in startPage()", e);
@@ -379,6 +380,7 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
     public void endPage() throws IFException {
         try {
             handler.endElement(EL_PAGE);
+            getContext().setPageIndex(-1);
         } catch (SAXException e) {
             throw new IFException("SAX error in endPage()", e);
         }
index dd93205716cf01fa713a6f9f3dfcb1d11998cd66..0a3d34ef4afdc893e72b6f0f198ecceb7ac9ead2 100644 (file)
@@ -50,7 +50,8 @@ import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
 import org.apache.fop.render.intermediate.IFException;
 import org.apache.fop.render.intermediate.IFPainter;
 import org.apache.fop.render.pdf.PDFRendererConfig.PDFRendererConfigParser;
-import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
+import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment;
+import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment;
 
 /**
  * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces PDF.
@@ -296,17 +297,22 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
         } else if (extension instanceof Metadata) {
             XMPMetadata wrapper = new XMPMetadata(((Metadata) extension));
             pdfUtil.renderXMPMetadata(wrapper);
-        } else if (extension instanceof PDFEmbeddedFileExtensionAttachment) {
-            PDFEmbeddedFileExtensionAttachment embeddedFile
-                = (PDFEmbeddedFileExtensionAttachment)extension;
+        } else if (extension instanceof PDFEmbeddedFileAttachment) {
+            PDFEmbeddedFileAttachment embeddedFile
+                = (PDFEmbeddedFileAttachment)extension;
             try {
                 pdfUtil.addEmbeddedFile(embeddedFile);
             } catch (IOException ioe) {
                 throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe);
             }
-        } else {
+        } else if (extension instanceof PDFDictionaryAttachment) {
+            PDFDictionaryAttachment dictionaryExtension = (PDFDictionaryAttachment) extension;
+            pdfUtil.renderDictionaryExtension(dictionaryExtension, currentPage);
+        } else if (extension != null) {
             log.debug("Don't know how to handle extension object. Ignoring: "
                     + extension + " (" + extension.getClass().getName() + ")");
+        } else {
+            log.debug("Ignoring null extension object.");
         }
     }
 
index c3b46794db8339531829c4b1e0991515d46f07c5..962cd784727b82bd6dc3c9d0587695d55f4559b8 100644 (file)
@@ -56,15 +56,23 @@ import org.apache.fop.pdf.PDFICCBasedColorSpace;
 import org.apache.fop.pdf.PDFICCStream;
 import org.apache.fop.pdf.PDFInfo;
 import org.apache.fop.pdf.PDFMetadata;
+import org.apache.fop.pdf.PDFName;
 import org.apache.fop.pdf.PDFNames;
+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.PDFText;
 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.PDFEmbeddedFileExtensionAttachment;
+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 static org.apache.fop.render.pdf.PDFEncryptionOption.ENCRYPTION_PARAMS;
 import static org.apache.fop.render.pdf.PDFEncryptionOption.NO_ACCESSCONTENT;
@@ -250,6 +258,46 @@ class PDFRenderingUtil {
         }
     }
 
+    public void renderDictionaryExtension(PDFDictionaryAttachment attachment, PDFPage currentPage) {
+        PDFDictionaryExtension extension = attachment.getExtension();
+        if (extension.getDictionaryType() == PDFDictionaryType.Catalog) {
+            augmentDictionary(pdfDoc.getRoot(), extension);
+        } else if (extension.getDictionaryType() == PDFDictionaryType.Page) {
+            if (extension.matchesPageNumber(currentPage.getPageIndex() + 1)) {
+                augmentDictionary(currentPage, extension);
+            }
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+    private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) {
+        for (PDFDictionaryEntryExtension entry : extension.getEntries()) {
+            if (entry instanceof PDFDictionaryExtension) {
+                dictionary.put(entry.getKey(), augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry));
+            } else {
+                augmentDictionary(dictionary, entry);
+            }
+        }
+        return dictionary;
+    }
+
+    private void augmentDictionary(PDFDictionary dictionary, PDFDictionaryEntryExtension entry) {
+        PDFDictionaryEntryType type = entry.getType();
+        String key = entry.getKey();
+        if (type == PDFDictionaryEntryType.Boolean) {
+            dictionary.put(key, entry.getValueAsBoolean());
+        } else if (type == PDFDictionaryEntryType.Name) {
+            dictionary.put(key, new PDFName(entry.getValueAsString()));
+        } else if (type == PDFDictionaryEntryType.Number) {
+            dictionary.put(key, new PDFNumber(entry.getValueAsNumber()));
+        } else if (type == PDFDictionaryEntryType.String) {
+            dictionary.put(key, entry.getValueAsString());
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
     public PDFDocument setupPDFDocument(OutputStream out) throws IOException {
         if (this.pdfDoc != null) {
             throw new IllegalStateException("PDFDocument already set up");
@@ -315,7 +363,7 @@ class PDFRenderingUtil {
      * @param embeddedFile the object representing the embedded file to be added
      * @throws IOException if an I/O error occurs
      */
-    public void addEmbeddedFile(PDFEmbeddedFileExtensionAttachment embeddedFile)
+    public void addEmbeddedFile(PDFEmbeddedFileAttachment embeddedFile)
             throws IOException {
         this.pdfDoc.getProfile().verifyEmbeddedFilesAllowed();
         PDFNames names = this.pdfDoc.getRoot().getNames();
diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java
new file mode 100644 (file)
index 0000000..9de7e95
--- /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;
+
+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);
+    }
+}
+
index 2b50112c00fe177b0a53854416b8d382b87806f7..b66fe4fad81454e93adc7c4001d54a3518c38059 100644 (file)
 
 package org.apache.fop.render.pdf.extensions;
 
-// FOP
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.extensions.ExtensionAttachment;
 
+// CSOFF: LineLengthCheck
+
 /**
  * Base class for the PDF-specific extension elements.
  */
@@ -67,7 +68,9 @@ public abstract class AbstractPDFExtensionElement extends FONode {
      * Instantiates extension attachment object.
      * @return extension attachment
      */
-    protected abstract ExtensionAttachment instantiateExtensionAttachment();
+    protected ExtensionAttachment instantiateExtensionAttachment() {
+        return null;
+    }
 
 }
 
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
new file mode 100644 (file)
index 0000000..528268c
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.util.GenerationHelperContentHandler;
+
+// CSOFF: LineLengthCheck
+
+public class PDFDictionaryAttachment extends PDFExtensionAttachment {
+
+    private static final long serialVersionUID = -5576832955238384505L;
+
+    private PDFDictionaryExtension extension;
+
+    public PDFDictionaryAttachment(PDFDictionaryExtension extension) {
+        this.extension = extension;
+    }
+
+    public PDFDictionaryExtension getExtension() {
+        return extension;
+    }
+
+    @Override
+    public void toSAX(ContentHandler handler) throws SAXException {
+        PDFDictionaryType dictionaryType = extension.getDictionaryType();
+        int pageNumber = 0;
+        if (dictionaryType == PDFDictionaryType.Page) {
+            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)) {
+                        pageNumber = pageIndex + 1;
+                    } else {
+                        pageNumber = -1;
+                    }
+                }
+            }
+        }
+        if (pageNumber >= 0) {
+            toSAX(handler, extension);
+        }
+    }
+
+    private void toSAX(ContentHandler handler, PDFDictionaryExtension dictionary) throws SAXException {
+        AttributesImpl attributes = new AttributesImpl();
+        String ln = dictionary.getElementName();
+        String qn = PREFIX + ":" + ln;
+        attributes = extractIFAttributes(attributes, dictionary);
+        handler.startElement(CATEGORY, ln, qn, attributes);
+        for (PDFDictionaryEntryExtension entry : dictionary.getEntries()) {
+            toSAX(handler, entry);
+        }
+        handler.endElement(CATEGORY, ln, qn);
+    }
+
+    private void toSAX(ContentHandler handler, PDFDictionaryEntryExtension entry) throws SAXException {
+        if (entry instanceof PDFDictionaryExtension) {
+            toSAX(handler, (PDFDictionaryExtension) 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);
+            }
+            handler.endElement(CATEGORY, ln, qn);
+        }
+    }
+
+    private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryExtension dictionary) {
+        PDFDictionaryType type = dictionary.getDictionaryType();
+        if (type == PDFDictionaryType.Catalog) {
+            // no specific attriburtes
+        } else if (type == PDFDictionaryType.Page) {
+            String pageNumbersName = PDFDictionaryExtension.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 key = dictionary.getKey();
+            if (key != null) {
+                attributes.addAttribute(null, keyName, keyName, "CDATA", key);
+            }
+        }
+        return attributes;
+    }
+
+    private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryEntryExtension entry) {
+        String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY;
+        String key = entry.getKey();
+        if (key != null) {
+            attributes.addAttribute(null, keyName, keyName, "CDATA", key);
+        }
+        return attributes;
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
new file mode 100644 (file)
index 0000000..5a60f20
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for dictionaries: pdf:{catalog,page,dictionary}. The specific type
+ * of dictionary is established at construction type.
+ */
+public class PDFDictionaryElement extends AbstractPDFDictionaryElement {
+
+    public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS;
+
+    private PDFDictionaryExtension extension;
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    PDFDictionaryElement(FONode parent, PDFDictionaryType type) {
+        super(parent);
+        this.extension = new PDFDictionaryExtension(type);
+    }
+
+    public PDFDictionaryExtension getExtension() {
+        return extension;
+    }
+
+    @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);
+            }
+        } else if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) {
+            String key = attlist.getValue(ATT_KEY);
+            if (key == null) {
+                missingPropertyError(ATT_KEY);
+            } else if (key.isEmpty()) {
+                invalidPropertyValueError(ATT_KEY, key, null);
+            } else {
+                extension.setKey(key);
+            }
+        }
+    }
+
+    @Override
+    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");
+            }
+        } else if (localName.equals("page")) {
+            if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
+                invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM");
+            }
+        } else if (localName.equals("dictionary")) {
+            if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) {
+                invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
+            }
+        } else {
+            throw new IllegalStateException("unknown name: " + localName);
+        }
+    }
+
+    @Override
+    protected void addChildNode(FONode child) throws FOPException {
+        if (child instanceof PDFDictionaryElement) {
+            PDFDictionaryExtension extension = ((PDFDictionaryElement) child).getExtension();
+            if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) {
+                this.extension.addEntry(extension);
+            }
+        } else if (child instanceof PDFDictionaryEntryElement) {
+            PDFDictionaryEntryExtension extension = ((PDFDictionaryEntryElement) child).getExtension();
+            this.extension.addEntry(extension);
+        }
+    }
+
+    @Override
+    public void endOfNode() throws FOPException {
+        super.endOfNode();
+    }
+
+    @Override
+    public String getLocalName() {
+        return extension.getDictionaryType().elementName();
+    }
+
+    @Override
+    protected ExtensionAttachment instantiateExtensionAttachment() {
+        return new PDFDictionaryAttachment(extension);
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java
new file mode 100644 (file)
index 0000000..d8ef1f0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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.isEmpty()) {
+            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.isEmpty()) {
+                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
new file mode 100644 (file)
index 0000000..94d6b5d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..e4b2581
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
new file mode 100644 (file)
index 0000000..2b851e0
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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 PDFDictionaryExtension extends PDFDictionaryEntryExtension {
+
+    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;
+
+    PDFDictionaryExtension() {
+    }
+
+    PDFDictionaryExtension(PDFDictionaryType dictionaryType) {
+        super(PDFDictionaryEntryType.Dictionary);
+        this.dictionaryType = dictionaryType;
+        this.properties = new java.util.HashMap<String, String>();
+        this.entries = new java.util.ArrayList<PDFDictionaryEntryExtension>();
+    }
+
+    public PDFDictionaryType getDictionaryType() {
+        return dictionaryType;
+    }
+
+    public void setProperty(String name, String value) {
+        properties.put(name, value);
+    }
+
+    public String getProperty(String name) {
+        return properties.get(name);
+    }
+
+    public void addEntry(PDFDictionaryEntryExtension entry) {
+        entries.add(entry);
+    }
+
+    public List<PDFDictionaryEntryExtension> getEntries() {
+        return entries;
+    }
+
+    public PDFDictionaryEntryExtension getLastEntry() {
+        if (entries.size() > 0) {
+            return entries.get(entries.size() - 1);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * 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.isEmpty()) {
+            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;
+    }
+
+    @Override
+    public String getElementName() {
+        return dictionaryType.elementName();
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java
new file mode 100644 (file)
index 0000000..edd9516
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 PDF dictionary extension elements.
+ */
+public enum PDFDictionaryType {
+    Dictionary("dictionary"),   // generic (nested) dictionary element
+    Catalog("catalog"),         // catalog dictionary element
+    Page("page");               // page dictionary element
+
+    private String elementName;
+    PDFDictionaryType(String elementName) {
+        this.elementName = elementName;
+    }
+    public String elementName() {
+        return elementName;
+    }
+    static PDFDictionaryType valueOfElementName(String elementName) {
+        for (PDFDictionaryType 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 c70ed36358c3e17c70d3e7ba56279a4670fca8cc..3e063e24b14795c40d65c64506ceed3bd5113265 100644 (file)
@@ -22,6 +22,8 @@ package org.apache.fop.render.pdf.extensions;
 import org.apache.fop.fo.ElementMapping;
 import org.apache.fop.fo.FONode;
 
+// CSOFF: LineLengthCheck
+
 /**
  * This class provides the element mapping for the PDF-specific extensions.
  */
@@ -39,13 +41,43 @@ public class PDFElementMapping extends ElementMapping {
     protected void initialize() {
         if (foObjs == null) {
             foObjs = new java.util.HashMap<String, Maker>();
-            foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileMaker());
+            // 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));
+                }
+            }
         }
     }
 
-    static class PDFEmbeddedFileMaker extends ElementMapping.Maker {
+    static class PDFEmbeddedFileElementMaker extends ElementMapping.Maker {
         public FONode make(FONode parent) {
             return new PDFEmbeddedFileElement(parent);
         }
     }
+
+    static class PDFDictionaryElementMaker extends ElementMapping.Maker {
+        private PDFDictionaryType dictionaryType;
+        PDFDictionaryElementMaker(PDFDictionaryType dictionaryType) {
+            this.dictionaryType = dictionaryType;
+        }
+        public FONode make(FONode parent) {
+            return new PDFDictionaryElement(parent, dictionaryType);
+        }
+    }
+
+    static class PDFDictionaryEntryElementMaker extends ElementMapping.Maker {
+        private PDFDictionaryEntryType entryType;
+        PDFDictionaryEntryElementMaker(PDFDictionaryEntryType entryType) {
+            this.entryType = entryType;
+        }
+        public FONode make(FONode parent) {
+            return new PDFDictionaryEntryElement(parent, entryType);
+        }
+    }
 }
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java
new file mode 100644 (file)
index 0000000..a62b35a
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * This is the pass-through value object for the PDF extension.
+ */
+public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment {
+
+    private static final long serialVersionUID = -1L;
+
+    /** element name */
+    protected static final String ELEMENT = "embedded-file";
+
+    /** name of file to be embedded */
+    private static final String ATT_NAME = "filename";
+
+    /** source of file to be embedded (URI) */
+    private static final String ATT_SRC = "src";
+
+    /** a description of the file to be embedded */
+    private static final String ATT_DESC = "desc";
+
+    /** filename attribute */
+    private String filename = null;
+
+    /** description attribute (optional) */
+    private String desc = null;
+
+    /** source name attribute */
+    private String src = null;
+
+    /**
+     * No-argument contructor.
+     */
+    public PDFEmbeddedFileAttachment() {
+        super();
+    }
+
+    /**
+     * Default constructor.
+     * @param filename the name of the file
+     * @param src the location of the file
+     * @param desc the description of the file
+     */
+    public PDFEmbeddedFileAttachment(String filename, String src, String desc) {
+        super();
+        this.filename = filename;
+        this.src = src;
+        this.desc = desc;
+    }
+
+    /**
+     * Returns the file name.
+     * @return the file name
+     */
+    public String getFilename() {
+        return filename;
+    }
+
+    /**
+     * Sets the file name.
+     * @param name The file name to set.
+     */
+    public void setFilename(String name) {
+        this.filename = name;
+    }
+
+    /**
+     * Returns the file description.
+     * @return the description
+     */
+    public String getDesc() {
+        return desc;
+    }
+
+    /**
+     * Sets the description of the file.
+     * @param desc the description to set
+     */
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    /**
+     * Returns the source URI of the file.
+     * @return the source URI
+     */
+    public String getSrc() {
+        return src;
+    }
+
+    /**
+     * Sets the source URI of the file.
+     * @param src the source URI
+     */
+    public void setSrc(String src) {
+        this.src = src;
+    }
+
+    /** {@inheritDoc} */
+    public String getCategory() {
+        return CATEGORY;
+    }
+
+    /** {@inheritDoc} */
+    public String toString() {
+        return "PDFEmbeddedFile(name=" + getFilename() + ", " + getSrc() + ")";
+    }
+
+    /**
+     * @return the element name
+     */
+    protected String getElement() {
+        return ELEMENT;
+    }
+
+    /** {@inheritDoc} */
+    public void toSAX(ContentHandler handler) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        if (filename != null && filename.length() > 0) {
+            atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", filename);
+        }
+        if (src != null && src.length() > 0) {
+            atts.addAttribute(null, ATT_SRC, ATT_SRC, "CDATA", src);
+        }
+        if (desc != null && desc.length() > 0) {
+            atts.addAttribute(null, ATT_DESC, ATT_DESC, "CDATA", desc);
+        }
+        String element = getElement();
+        handler.startElement(CATEGORY, element, element, atts);
+        handler.endElement(CATEGORY, element, element);
+    }
+
+}
index 36d7c18d26123ec8865b3101a80c2ff16b5ed9b7..3fef5521ee49a66ca27ae175babf08943843c2e7 100644 (file)
@@ -30,7 +30,6 @@ import org.apache.fop.datatypes.URISpecification;
 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;
 
 /**
  * Extension element for pdf:embedded-file.
@@ -44,11 +43,11 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
      * Main constructor
      * @param parent parent FO node
      */
-    protected PDFEmbeddedFileElement(FONode parent) {
+    PDFEmbeddedFileElement(FONode parent) {
         super(parent);
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void startOfNode() throws FOPException {
         super.startOfNode();
         if (parent.getNameId() != Constants.FO_DECLARATIONS) {
@@ -57,12 +56,12 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
         }
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void processNode(String elementName, Locator locator,
                             Attributes attlist, PropertyList propertyList)
                                 throws FOPException {
-        PDFEmbeddedFileExtensionAttachment embeddedFile
-            = (PDFEmbeddedFileExtensionAttachment)getExtensionAttachment();
+        PDFEmbeddedFileAttachment embeddedFile
+            = (PDFEmbeddedFileAttachment)getExtensionAttachment();
         String desc = attlist.getValue("description");
         if (desc != null && desc.length() > 0) {
             embeddedFile.setDesc(desc);
@@ -94,13 +93,8 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
         embeddedFile.setFilename(filename);
     }
 
-    /** {@inheritDoc} */
+    @Override
     public String getLocalName() {
         return ELEMENT;
     }
-
-    /** {@inheritDoc} */
-    protected ExtensionAttachment instantiateExtensionAttachment() {
-        return new PDFEmbeddedFileExtensionAttachment();
-    }
 }
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java
deleted file mode 100644 (file)
index 5f3f16d..0000000
+++ /dev/null
@@ -1,154 +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.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-/**
- * This is the pass-through value object for the PDF extension.
- */
-public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
-
-    /** element name */
-    protected static final String ELEMENT = "embedded-file";
-
-    /** name of file to be embedded */
-    private static final String ATT_NAME = "filename";
-
-    /** source of file to be embedded (URI) */
-    private static final String ATT_SRC = "src";
-
-    /** a description of the file to be embedded */
-    private static final String ATT_DESC = "desc";
-
-    /** filename attribute */
-    private String filename = null;
-
-    /** description attribute (optional) */
-    private String desc = null;
-
-    /** source name attribute */
-    private String src = null;
-
-    /**
-     * No-argument contructor.
-     */
-    public PDFEmbeddedFileExtensionAttachment() {
-        super();
-    }
-
-    /**
-     * Default constructor.
-     * @param filename the name of the file
-     * @param src the location of the file
-     * @param desc the description of the file
-     */
-    public PDFEmbeddedFileExtensionAttachment(String filename, String src, String desc) {
-        super();
-        this.filename = filename;
-        this.src = src;
-        this.desc = desc;
-    }
-
-    /**
-     * Returns the file name.
-     * @return the file name
-     */
-    public String getFilename() {
-        return filename;
-    }
-
-    /**
-     * Sets the file name.
-     * @param name The file name to set.
-     */
-    public void setFilename(String name) {
-        this.filename = name;
-    }
-
-    /**
-     * Returns the file description.
-     * @return the description
-     */
-    public String getDesc() {
-        return desc;
-    }
-
-    /**
-     * Sets the description of the file.
-     * @param desc the description to set
-     */
-    public void setDesc(String desc) {
-        this.desc = desc;
-    }
-
-    /**
-     * Returns the source URI of the file.
-     * @return the source URI
-     */
-    public String getSrc() {
-        return src;
-    }
-
-    /**
-     * Sets the source URI of the file.
-     * @param src the source URI
-     */
-    public void setSrc(String src) {
-        this.src = src;
-    }
-
-    /** {@inheritDoc} */
-    public String getCategory() {
-        return CATEGORY;
-    }
-
-    /** {@inheritDoc} */
-    public String toString() {
-        return "PDFEmbeddedFile(name=" + getFilename() + ", " + getSrc() + ")";
-    }
-
-    /**
-     * @return the element name
-     */
-    protected String getElement() {
-        return ELEMENT;
-    }
-
-    /** {@inheritDoc} */
-    public void toSAX(ContentHandler handler) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        if (filename != null && filename.length() > 0) {
-            atts.addAttribute(null, ATT_NAME, ATT_NAME, "CDATA", filename);
-        }
-        if (src != null && src.length() > 0) {
-            atts.addAttribute(null, ATT_SRC, ATT_SRC, "CDATA", src);
-        }
-        if (desc != null && desc.length() > 0) {
-            atts.addAttribute(null, ATT_DESC, ATT_DESC, "CDATA", desc);
-        }
-        String element = getElement();
-        handler.startElement(CATEGORY, element, element, atts);
-        handler.endElement(CATEGORY, element, element);
-    }
-
-}
index a4f5b47b7f01750ff6995d879579de8462bf8063..58664faa0920ae4c5f05d29cdc3a664e8269138c 100644 (file)
@@ -23,43 +23,28 @@ import org.apache.xmlgraphics.util.XMLizable;
 
 import org.apache.fop.fo.extensions.ExtensionAttachment;
 
-/**
- * This is the pass-through value object for the PDF extension.
- */
+// CSOFF: LineLengthCheck
+
 public abstract class PDFExtensionAttachment implements ExtensionAttachment, XMLizable {
 
     /** The category URI for this extension attachment. */
     public static final String CATEGORY = "apache:fop:extensions:pdf";
 
+    /** The prefix to use with qualified names for this extension attachment. */
+    public static final String PREFIX = "pdf";
+
     /**
      * Default constructor.
      */
     public PDFExtensionAttachment() {
-        //nop
-    }
-
-    /**
-     * @return the category URI
-     * @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory()
-     */
-    public String getCategory() {
-        return CATEGORY;
     }
 
-    /** @return type name */
-    public String getType() {
-        String className = getClass().getName();
-        return className.substring(className.lastIndexOf('.') + 3);
+    public String getPrefix() {
+        return PREFIX;
     }
 
-    /**
-     * @return a string representation of this object
-     * @see java.lang.Object#toString()
-     */
-    public String toString() {
-        return getType();
+    @Override
+    public String getCategory() {
+        return CATEGORY;
     }
-
-    /** @return element */
-    protected abstract String getElement();
 }
index e7e863a23855bc09f759537689bd2b13c6f0ed19..2741e64f683a3d8b56aebecba678d47788c50816 100644 (file)
@@ -19,6 +19,8 @@
 
 package org.apache.fop.render.pdf.extensions;
 
+import java.util.Stack;
+
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
@@ -30,68 +32,127 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.fop.util.ContentHandlerFactory;
 import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
 
+// CSOFF: LineLengthCheck
+
 /**
  * ContentHandler (parser) for restoring PDF extension objects from XML.
  */
-public class PDFExtensionHandler extends DefaultHandler
-            implements ContentHandlerFactory.ObjectSource {
+public class PDFExtensionHandler extends DefaultHandler implements ContentHandlerFactory.ObjectSource {
 
     /** Logger instance */
     protected static final Log log = LogFactory.getLog(PDFExtensionHandler.class);
 
-    private Attributes lastAttributes;
-
     private PDFExtensionAttachment returnedObject;
     private ObjectBuiltListener listener;
 
-    /** {@inheritDoc} */
-    public void startElement(String uri, String localName, String qName, Attributes attributes)
-                throws SAXException {
-        boolean handled = false;
+    // PDFEmbeddedFileAttachment related state
+    private Attributes lastAttributes;
+
+    // PDFDictionaryAttachment related
+    private Stack<PDFDictionaryExtension> dictionaries = new Stack<PDFDictionaryExtension>();
+    private boolean captureContent;
+    private StringBuffer characters;
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
         if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
-            lastAttributes = new AttributesImpl(attributes);
-            handled = false;
-            if (localName.equals(PDFEmbeddedFileExtensionAttachment.ELEMENT)) {
-                //handled in endElement
-                handled = true;
+            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);
+                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);
+                    }
+                }
+                dictionaries.push(dictionary);
+            } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) {
+                PDFDictionaryEntryExtension entry = new PDFDictionaryEntryExtension(PDFDictionaryEntryType.valueOfElementName(localName));
+                String key = attributes.getValue(PDFDictionaryEntryElement.ATT_KEY);
+                if (key != null) {
+                    entry.setKey(key);
+                }
+                if (!dictionaries.empty()) {
+                    PDFDictionaryExtension dictionary = dictionaries.peek();
+                    dictionary.addEntry(entry);
+                    captureContent = true;
+                }
+            } else {
+                throw new SAXException("Unhandled element " + localName + " in namespace: " + uri);
             }
+        } else {
+            log.warn("Unhandled element " + localName + " in namespace: " + uri);
         }
-        if (!handled) {
-            if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
-                throw new SAXException("Unhandled element " + localName
-                        + " in namespace: " + uri);
-            } else {
-                log.warn("Unhandled element " + localName
-                        + " in namespace: " + uri);
+    }
+
+    @Override
+    public void characters(char[] data, int start, int length) throws SAXException {
+        if (captureContent) {
+            if (characters == null) {
+                characters = new StringBuffer((length < 16) ? 16 : length);
             }
+            characters.append(data, start, length);
         }
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void endElement(String uri, String localName, String qName) throws SAXException {
         if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
-            if (PDFEmbeddedFileExtensionAttachment.ELEMENT.equals(localName)) {
+            if (PDFEmbeddedFileAttachment.ELEMENT.equals(localName)) {
                 String name = lastAttributes.getValue("name");
                 String src = lastAttributes.getValue("src");
                 String desc = lastAttributes.getValue("description");
-                this.returnedObject = new PDFEmbeddedFileExtensionAttachment(name, src, desc);
+                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)) {
+                        this.returnedObject = new PDFDictionaryAttachment(dictionary);
+                    } else if (!dictionaries.empty()) {
+                        PDFDictionaryExtension dictionaryOuter = dictionaries.peek();
+                        dictionaryOuter.addEntry(dictionary);
+                    }
+                } else {
+                    throw new SAXException(new IllegalStateException("no active dictionary"));
+                }
+            } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) {
+                if (!dictionaries.empty()) {
+                    PDFDictionaryExtension dictionary = dictionaries.peek();
+                    PDFDictionaryEntryExtension entry = dictionary.getLastEntry();
+                    if (entry != null) {
+                        if (characters != null) {
+                            entry.setValue(characters.toString());
+                            characters = null;
+                        }
+                    } else {
+                        throw new SAXException(new IllegalStateException("no active entry"));
+                    }
+                } else {
+                    throw new SAXException(new IllegalStateException("no active dictionary"));
+                }
             }
         }
+        captureContent = false;
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void endDocument() throws SAXException {
         if (listener != null) {
             listener.notifyObjectBuilt(getObject());
         }
     }
 
-    /** {@inheritDoc} */
+    @Override
     public Object getObject() {
         return returnedObject;
     }
 
-    /** {@inheritDoc} */
+    @Override
     public void setObjectBuiltListener(ObjectBuiltListener listener) {
         this.listener = listener;
     }
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java
new file mode 100644 (file)
index 0000000..6d83cd5
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.ContentHandler;
+
+import org.apache.fop.util.ContentHandlerFactory;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Factory for the ContentHandler that handles serialized PDFExtensionAttachment instances.
+ */
+public class PDFExtensionHandlerFactory implements ContentHandlerFactory {
+
+    private static final String[] NAMESPACES = new String[] {PDFExtensionAttachment.CATEGORY};
+
+    /** {@inheritDoc} */
+    public String[] getSupportedNamespaces() {
+        return NAMESPACES;
+    }
+
+    /** {@inheritDoc} */
+    public ContentHandler createContentHandler() {
+        return new PDFExtensionHandler();
+    }
+}
index 659f7ccac6d18026a6edc41b16072b418d5d3aab..e2f5a651e7040beee2bf08e29b07c2abd5e0cdac 100644 (file)
@@ -117,7 +117,7 @@ public final class FontListMain {
         }
         try {
             GenerationHelperContentHandler helper = new GenerationHelperContentHandler(
-                    handler, null);
+                    handler, null, null);
             FontListSerializer serializer = new FontListSerializer();
             serializer.generateSAX(fontFamilies, singleFamily, helper);
         } finally {
index 64fabbc8ab0e5156ac223e6554d58c75901016cd..68970a7f29bbb5d078e0a9d9df69d725d2d58c8c 100644 (file)
@@ -35,6 +35,7 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
     private static final Attributes EMPTY_ATTS = new AttributesImpl();
 
     private String mainNamespace;
+    private Object contentHandlerContext;
 
     /**
      * Main constructor. If the given handler also implements any of the EntityResolver,
@@ -42,10 +43,12 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
      * @param handler the SAX content handler to delegate all calls to
      * @param mainNamespace the main namespace used for generated XML content when abbreviated
      *                          ContentHandler calls are used.
+     * @param contentHandlerContext additional content handler context state
      */
-    public GenerationHelperContentHandler(ContentHandler handler, String mainNamespace) {
+    public GenerationHelperContentHandler(ContentHandler handler, String mainNamespace, Object contentHandlerContext) {
         super(handler);
         this.mainNamespace = mainNamespace;
+        this.contentHandlerContext = contentHandlerContext;
     }
 
     /**
@@ -65,6 +68,14 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
         this.mainNamespace = namespaceURI;
     }
 
+    /**
+     * Returns the context object (may be null).
+     * @return the context object
+     */
+    public Object getContentHandlerContext() {
+        return this.contentHandlerContext;
+    }
+
     /**
      * Convenience method to generate a startElement SAX event.
      * @param localName the local name of the element
index 24c75922ca92ec7ce4d44b509595e74602eef7db..3f815e59edae40d00809f250c84222c131a7f45c 100644 (file)
@@ -300,4 +300,35 @@ public final class XMLUtil implements XMLConstants {
         }
     }
 
+    /**
+     * Escape '<', '>' and '&' using NCRs.
+     * @param unescaped string
+     * @return escaped string
+     */
+    public static String escape(String unescaped) {
+        int needsEscape = 0;
+        for (int i = 0, n = unescaped.length(); i < n; ++i) {
+            char c = unescaped.charAt(i);
+            if ((c == '<') || (c == '>') || (c == '&')) {
+                ++needsEscape;
+            }
+        }
+        if (needsEscape > 0) {
+            StringBuffer sb = new StringBuffer(unescaped.length() + 6 * needsEscape);
+            for (int i = 0, n = unescaped.length(); i < n; ++i) {
+                char c = unescaped.charAt(i);
+                if ((c == '<') || (c == '>') || (c == '&')) {
+                    sb.append("&#x");
+                    sb.append(Integer.toString(c, 16));
+                    sb.append(';');
+                } else {
+                    sb.append(c);
+                }
+            }
+            return sb.toString();
+        } else {
+            return unescaped;
+        }
+    }
+
 }
index 12dd948e573808599e9d5cc876d47e6b33840027..f7bfbb38ebd8ef3e98f2e3fef1780fa67b28c739 100644 (file)
@@ -229,7 +229,7 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
     }
 
     private GenerationHelperContentHandler decorate(ContentHandler contentHandler) {
-        return new GenerationHelperContentHandler(contentHandler, getMainNamespace());
+        return new GenerationHelperContentHandler(contentHandler, getMainNamespace(), getContext());
     }
 
     private void closeCurrentStream() {
index d17168936ab745005fa1cf403a60ad486c2a888a..5f29deefb091d3c56bddfb4c802aaa27ab15fd40 100644 (file)
@@ -59,6 +59,9 @@
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Renderers" dev="GA" type="add" fixes-bug="FOP-2298">
+          Enable support for PDF page transitions.
+      </action>
       <action context="Code" dev="PH" type="fix" fixes-bug="FOP-2211" due-to="Alexios Giotis, PH">
           Fix and improve the handling of temporary files using the new URI resource resolvers
       </action>
diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml
new file mode 100644 (file)
index 0000000..ecbf1ba
--- /dev/null
@@ -0,0 +1,90 @@
+<?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.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="simple">
+          <fo:region-body/>
+          <fo:region-before/>
+          <fo:region-after/>
+          <pdf:page page-numbers="*">
+            <pdf:number key="Dur">5</pdf:number>
+          </pdf:page>
+          <pdf:page page-numbers="1">
+            <pdf:dictionary key="Trans">
+              <pdf:name key="Type">Trans</pdf:name>
+              <pdf:number key="D">1</pdf:number>
+              <pdf:name key="S">Glitter</pdf:name>
+              <pdf:number key="Di">0</pdf:number>
+            </pdf:dictionary>
+          </pdf:page>
+          <pdf:page page-numbers="2">
+            <pdf:dictionary key="Trans">
+              <pdf:name key="Type">Trans</pdf:name>
+              <pdf:number key="D">1</pdf:number>
+              <pdf:name key="S">Push</pdf:name>
+              <pdf:number key="Di">180</pdf:number>
+            </pdf:dictionary>
+          </pdf:page>
+          <pdf:page page-numbers="3">
+            <pdf:dictionary key="Trans">
+              <pdf:name key="Type">Trans</pdf:name>
+              <pdf:number key="D">1</pdf:number>
+              <pdf:name key="S">Cover</pdf:name>
+              <pdf:number key="Di">270</pdf:number>
+            </pdf:dictionary>
+          </pdf:page>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:declarations>
+        <pdf:catalog>
+          <pdf:name key="Foo">Bar</pdf:name>
+        </pdf:catalog>
+      </fo:declarations>
+      <fo:page-sequence master-reference="simple">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block page-break-before="always">SLIDE 1</fo:block>
+          <fo:block page-break-before="always">SLIDE 2</fo:block>
+          <fo:block page-break-before="always">SLIDE 3</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks xmlns:pdf="apache:fop:extensions:pdf">
+    <eval expected="Foo" xpath="/areaTree/extension-attachments/pdf:catalog/pdf:name/@key"/>
+    <eval expected="Bar" xpath="/areaTree/extension-attachments/pdf:catalog/pdf:name"/>
+    <eval expected="Dur" xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/pdf:page/pdf:number/@key"/>
+    <eval expected="5" xpath="/areaTree/pageSequence/pageViewport[@nr=1]/page/extension-attachments/pdf:page/pdf:number"/>
+  </checks>
+  <if-checks xmlns:if="http://xmlgraphics.apache.org/fop/intermediate" xmlns:pdf="apache:fop:extensions:pdf">
+    <eval expected="Foo" xpath="/if:document/if:header/pdf:catalog/pdf:name/@key"/>
+    <eval expected="Bar" xpath="/if:document/if:header/pdf:catalog/pdf:name"/>
+    <eval expected="Dur" xpath="//if:page[@name=1]/if:page-header/pdf:page/pdf:number/@key"/>
+    <eval expected="5" xpath="//if:page[@name=1]/if:page-header/pdf:page/pdf:number"/>
+    <eval expected="2" xpath="count(//if:page[@name=1]/if:page-header/pdf:page)"/>
+    <eval expected="2" xpath="count(//if:page[@name=2]/if:page-header/pdf:page)"/>
+    <eval expected="2" xpath="count(//if:page[@name=3]/if:page-header/pdf:page)"/>
+  </if-checks>
+</testcase>