summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Adams <gadams@apache.org>2013-09-13 13:56:18 +0000
committerGlenn Adams <gadams@apache.org>2013-09-13 13:56:18 +0000
commit54eec84366117b4705a71810f1d1c87529c32ba5 (patch)
tree440e34d3633c5a176337dcd06b2d362e76d8d9e6
parent119c49e73ecda40e92303c03231086eae61295df (diff)
downloadxmlgraphics-fop-54eec84366117b4705a71810f1d1c87529c32ba5.tar.gz
xmlgraphics-fop-54eec84366117b4705a71810f1d1c87529c32ba5.zip
FOP-2298: Enable support for PDF Transitions by defining low-level mechanism to augment /Catalog and /Page dictionaries.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1522934 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--findbugs-exclude.xml20
-rw-r--r--src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory1
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExtensionObj.java65
-rw-r--r--src/java/org/apache/fop/pdf/PDFNumber.java8
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java4
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFContext.java18
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java2
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java2
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java16
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java52
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java43
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java7
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java125
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java126
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java109
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java110
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java56
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java124
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java54
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java36
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java (renamed from src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java)8
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java18
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java35
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java113
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java44
-rw-r--r--src/java/org/apache/fop/tools/fontlist/FontListMain.java2
-rw-r--r--src/java/org/apache/fop/util/GenerationHelperContentHandler.java13
-rw-r--r--src/java/org/apache/fop/util/XMLUtil.java31
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java2
-rw-r--r--status.xml3
-rw-r--r--test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml90
31 files changed, 1190 insertions, 147 deletions
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
index 2becac909..46e6e1675 100644
--- a/findbugs-exclude.xml
+++ b/findbugs-exclude.xml
@@ -5413,4 +5413,24 @@
</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>
diff --git a/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory b/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
index 76d6ebfbc..cabf917eb 100644
--- a/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
+++ b/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
@@ -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
index c35dcfc73..000000000
--- a/src/java/org/apache/fop/fo/extensions/ExtensionObj.java
+++ /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;
- }
-}
-
diff --git a/src/java/org/apache/fop/pdf/PDFNumber.java b/src/java/org/apache/fop/pdf/PDFNumber.java
index 1c31f8e9d..754194886 100644
--- a/src/java/org/apache/fop/pdf/PDFNumber.java
+++ b/src/java/org/apache/fop/pdf/PDFNumber.java
@@ -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
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
index fcf8554a6..0c3305797 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
@@ -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());
}
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java
index fda0cff3b..7464e26e0 100644
--- a/src/java/org/apache/fop/render/intermediate/IFContext.java
+++ b/src/java/org/apache/fop/render/intermediate/IFContext.java
@@ -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;
+ }
+
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index 9e6aae700..30ceda108 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -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);
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 62e4cc67d..1ffd42863 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -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);
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
index dd9320571..0a3d34ef4 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -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.");
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
index c3b46794d..962cd7847 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -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
index 000000000..9de7e95da
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java
@@ -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);
+ }
+}
+
diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
index 2b50112c0..b66fe4fad 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
@@ -19,10 +19,11 @@
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
index 000000000..528268c30
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
@@ -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
index 000000000..5a60f20ca
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
@@ -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
index 000000000..d8ef1f04e
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java
@@ -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
index 000000000..94d6b5dbf
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java
@@ -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
index 000000000..e4b25819a
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java
@@ -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
index 000000000..2b851e0bf
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
@@ -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
index 000000000..edd95160a
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java
@@ -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;
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
index c70ed3635..3e063e24b 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
@@ -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/PDFEmbeddedFileExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java
index 5f3f16dbb..a62b35a4b 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java
@@ -26,7 +26,9 @@ import org.xml.sax.helpers.AttributesImpl;
/**
* This is the pass-through value object for the PDF extension.
*/
-public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
+public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment {
+
+ private static final long serialVersionUID = -1L;
/** element name */
protected static final String ELEMENT = "embedded-file";
@@ -52,7 +54,7 @@ public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
/**
* No-argument contructor.
*/
- public PDFEmbeddedFileExtensionAttachment() {
+ public PDFEmbeddedFileAttachment() {
super();
}
@@ -62,7 +64,7 @@ public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
* @param src the location of the file
* @param desc the description of the file
*/
- public PDFEmbeddedFileExtensionAttachment(String filename, String src, String desc) {
+ public PDFEmbeddedFileAttachment(String filename, String src, String desc) {
super();
this.filename = filename;
this.src = src;
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
index 36d7c18d2..3fef5521e 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
@@ -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/PDFExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
index a4f5b47b7..58664faa0 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
@@ -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();
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
index e7e863a23..2741e64f6 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
@@ -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
index 000000000..6d83cd58f
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java
@@ -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();
+ }
+}
diff --git a/src/java/org/apache/fop/tools/fontlist/FontListMain.java b/src/java/org/apache/fop/tools/fontlist/FontListMain.java
index 659f7ccac..e2f5a651e 100644
--- a/src/java/org/apache/fop/tools/fontlist/FontListMain.java
+++ b/src/java/org/apache/fop/tools/fontlist/FontListMain.java
@@ -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 {
diff --git a/src/java/org/apache/fop/util/GenerationHelperContentHandler.java b/src/java/org/apache/fop/util/GenerationHelperContentHandler.java
index 64fabbc8a..68970a7f2 100644
--- a/src/java/org/apache/fop/util/GenerationHelperContentHandler.java
+++ b/src/java/org/apache/fop/util/GenerationHelperContentHandler.java
@@ -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;
}
/**
@@ -66,6 +69,14 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
}
/**
+ * 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
* @param atts the attributes
diff --git a/src/java/org/apache/fop/util/XMLUtil.java b/src/java/org/apache/fop/util/XMLUtil.java
index 24c75922c..3f815e59e 100644
--- a/src/java/org/apache/fop/util/XMLUtil.java
+++ b/src/java/org/apache/fop/util/XMLUtil.java
@@ -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;
+ }
+ }
+
}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
index 12dd948e5..f7bfbb38e 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
@@ -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() {
diff --git a/status.xml b/status.xml
index d17168936..5f29deefb 100644
--- a/status.xml
+++ b/status.xml
@@ -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
index 000000000..ecbf1bafd
--- /dev/null
+++ b/test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml
@@ -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>