From 54eec84366117b4705a71810f1d1c87529c32ba5 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Fri, 13 Sep 2013 13:56:18 +0000 Subject: [PATCH] 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 --- findbugs-exclude.xml | 20 +++ .../org.apache.fop.util.ContentHandlerFactory | 1 + .../fop/fo/extensions/ExtensionObj.java | 65 --------- src/java/org/apache/fop/pdf/PDFNumber.java | 8 ++ .../AbstractXMLWritingIFDocumentHandler.java | 4 +- .../fop/render/intermediate/IFContext.java | 18 +++ .../fop/render/intermediate/IFRenderer.java | 2 + .../fop/render/intermediate/IFSerializer.java | 2 + .../fop/render/pdf/PDFDocumentHandler.java | 16 ++- .../fop/render/pdf/PDFRenderingUtil.java | 52 +++++++- .../AbstractPDFDictionaryElement.java | 43 ++++++ .../AbstractPDFExtensionElement.java | 7 +- .../extensions/PDFDictionaryAttachment.java | 125 +++++++++++++++++ .../pdf/extensions/PDFDictionaryElement.java | 126 ++++++++++++++++++ .../extensions/PDFDictionaryEntryElement.java | 109 +++++++++++++++ .../PDFDictionaryEntryExtension.java | 110 +++++++++++++++ .../extensions/PDFDictionaryEntryType.java | 56 ++++++++ .../extensions/PDFDictionaryExtension.java | 124 +++++++++++++++++ .../pdf/extensions/PDFDictionaryType.java | 54 ++++++++ .../pdf/extensions/PDFElementMapping.java | 36 ++++- ...nt.java => PDFEmbeddedFileAttachment.java} | 8 +- .../extensions/PDFEmbeddedFileElement.java | 18 +-- .../extensions/PDFExtensionAttachment.java | 35 ++--- .../pdf/extensions/PDFExtensionHandler.java | 113 ++++++++++++---- .../PDFExtensionHandlerFactory.java | 44 ++++++ .../fop/tools/fontlist/FontListMain.java | 2 +- .../util/GenerationHelperContentHandler.java | 13 +- src/java/org/apache/fop/util/XMLUtil.java | 31 +++++ .../fop/render/svg/SVGDocumentHandler.java | 2 +- status.xml | 3 + .../pdf-dictionary-extension_1.xml | 90 +++++++++++++ 31 files changed, 1190 insertions(+), 147 deletions(-) delete mode 100644 src/java/org/apache/fop/fo/extensions/ExtensionObj.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java rename src/java/org/apache/fop/render/pdf/extensions/{PDFEmbeddedFileExtensionAttachment.java => PDFEmbeddedFileAttachment.java} (94%) create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java create mode 100644 test/layoutengine/standard-testcases/pdf-dictionary-extension_1.xml 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 @@ + + + + + + + + + + + + + + + + + + + + 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, null); + } + } else if (extension.getType() == PDFDictionaryEntryType.Name) { + if (value.isEmpty()) { + invalidPropertyValueError("", value, null); + } + } else if (extension.getType() == PDFDictionaryEntryType.Number) { + try { + Double.valueOf(value); + } catch (NumberFormatException e) { + invalidPropertyValueError("", 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 properties; + private List entries; + + PDFDictionaryExtension() { + } + + PDFDictionaryExtension(PDFDictionaryType dictionaryType) { + super(PDFDictionaryEntryType.Dictionary); + this.dictionaryType = dictionaryType; + this.properties = new java.util.HashMap(); + this.entries = new java.util.ArrayList(); + } + + 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 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(); - 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 similarity index 94% rename from src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java rename to 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 dictionaries = new Stack(); + 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; } /** @@ -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 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. --> + + Enable support for PDF page transitions. + Fix and improve the handling of temporary files using the new URI resource resolvers 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 @@ + + + + + +

+ This test checks the PDF dictionary extensions. +

+
+ + + + + + + + + 5 + + + + Trans + 1 + Glitter + 0 + + + + + Trans + 1 + Push + 180 + + + + + Trans + 1 + Cover + 270 + + + + + + + Bar + + + + + SLIDE 1 + SLIDE 2 + SLIDE 3 + + + + + + + + + + + + + + + + + + + +
-- 2.39.5