From 8841c3bdd571ff02ff4e99dbce355b78127e4190 Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Fri, 1 Nov 2013 14:34:18 +0000 Subject: [PATCH] FOP-2301: Enable support for PDF sub-page transitions. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1537948 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop-intermediate-format-ng-content.xsd | 1 + .../org/apache/fop/area/AreaTreeParser.java | 2 +- src/java/org/apache/fop/area/Trait.java | 5 +- src/java/org/apache/fop/fo/Constants.java | 5 +- .../org/apache/fop/fo/FOPropertyMapping.java | 7 + src/java/org/apache/fop/fo/FObj.java | 14 +- .../extensions/ExtensionElementMapping.java | 2 + .../BlockContainerLayoutManager.java | 1 + .../fop/layoutmgr/BlockLayoutManager.java | 1 + .../org/apache/fop/layoutmgr/TraitSetter.java | 11 + .../layoutmgr/inline/InlineLayoutManager.java | 1 + .../org/apache/fop/pdf/AbstractPDFStream.java | 4 +- .../org/apache/fop/pdf/PDFDictionary.java | 2 +- src/java/org/apache/fop/pdf/PDFDocument.java | 54 +++- src/java/org/apache/fop/pdf/PDFFactory.java | 24 ++ .../fop/pdf/PDFIdentifiedDictionary.java | 42 +++ src/java/org/apache/fop/pdf/PDFLayer.java | 86 ++++++ src/java/org/apache/fop/pdf/PDFNavigator.java | 93 +++++++ .../apache/fop/pdf/PDFNavigatorAction.java | 28 ++ .../org/apache/fop/pdf/PDFPaintingState.java | 34 ++- src/java/org/apache/fop/pdf/PDFResources.java | 34 ++- .../apache/fop/pdf/PDFSetOCGStateAction.java | 82 ++++++ .../apache/fop/pdf/PDFTransitionAction.java | 79 ++++++ .../apache/fop/render/AbstractRenderer.java | 58 ++++ .../org/apache/fop/render/afp/AFPPainter.java | 2 +- .../intermediate/AbstractIFPainter.java | 4 +- .../render/intermediate/IFGraphicContext.java | 23 +- .../fop/render/intermediate/IFPainter.java | 8 +- .../fop/render/intermediate/IFParser.java | 5 +- .../fop/render/intermediate/IFRenderer.java | 32 ++- .../fop/render/intermediate/IFSerializer.java | 13 +- .../fop/render/java2d/Java2DPainter.java | 2 +- .../fop/render/java2d/Java2DRenderer.java | 8 + .../org/apache/fop/render/pcl/PCLPainter.java | 2 +- .../fop/render/pdf/PDFContentGenerator.java | 81 ++++-- .../fop/render/pdf/PDFDocumentHandler.java | 3 +- .../org/apache/fop/render/pdf/PDFPainter.java | 4 +- .../fop/render/pdf/PDFRenderingUtil.java | 261 +++++++++++++++++- .../pdf/extensions/PDFActionElement.java | 64 +++++ .../pdf/extensions/PDFActionExtension.java | 32 +++ .../pdf/extensions/PDFArrayElement.java | 82 ++++++ .../pdf/extensions/PDFArrayExtension.java | 84 ++++++ .../pdf/extensions/PDFCatalogElement.java | 45 +++ .../pdf/extensions/PDFCatalogExtension.java | 29 ++ .../extensions/PDFCollectionEntryElement.java | 146 ++++++++++ ....java => PDFCollectionEntryExtension.java} | 36 +-- .../extensions/PDFCollectionExtension.java | 34 +++ .../extensions/PDFDictionaryAttachment.java | 70 ++++- .../pdf/extensions/PDFDictionaryElement.java | 83 +++--- .../extensions/PDFDictionaryEntryElement.java | 109 -------- .../extensions/PDFDictionaryExtension.java | 97 ++++--- .../pdf/extensions/PDFDictionaryType.java | 19 +- .../pdf/extensions/PDFElementMapping.java | 90 ++++-- .../pdf/extensions/PDFExtensionHandler.java | 124 +++++++-- .../pdf/extensions/PDFLayerElement.java | 45 +++ .../pdf/extensions/PDFLayerExtension.java | 29 ++ .../pdf/extensions/PDFNavigatorElement.java | 45 +++ .../pdf/extensions/PDFNavigatorExtension.java | 30 ++ ...Extension.java => PDFObjectExtension.java} | 47 ++-- ...onaryEntryType.java => PDFObjectType.java} | 26 +- .../render/pdf/extensions/PDFPageElement.java | 64 +++++ .../pdf/extensions/PDFPageExtension.java | 73 +++++ .../pdf/extensions/PDFReferenceElement.java | 58 ++++ .../pdf/extensions/PDFReferenceExtension.java | 61 ++++ .../org/apache/fop/render/ps/PSPainter.java | 2 +- .../apache/fop/render/txt/TXTRenderer.java | 8 + .../apache/fop/render/xml/XMLRenderer.java | 10 + .../fop/util/AbstractPaintingState.java | 67 +++-- .../org/apache/fop/render/svg/SVGPainter.java | 10 +- status.xml | 5 +- .../fop/fonts/EmbedFontInfoTestCase.java | 2 + .../org/apache/fop/fonts/FontsTestSuite.java | 2 +- .../org/apache/fop/pdf/PDFDestsTestCase.java | 2 +- .../apache/fop/pdf/PDFDictionaryTestCase.java | 2 +- .../apache/fop/pdf/PDFPageLabelsTestCase.java | 28 +- .../org/apache/fop/pdf/PDFStreamTestCase.java | 2 +- .../pdf/xref/CrossReferenceTableTestCase.java | 2 +- .../AbstractIFPainterTestCase.java | 2 +- .../pdf-dictionary-extension_2.xml | 74 +++++ .../pdf-dictionary-extension_3.xml | 122 ++++++++ 80 files changed, 2644 insertions(+), 436 deletions(-) create mode 100644 src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java create mode 100644 src/java/org/apache/fop/pdf/PDFLayer.java create mode 100644 src/java/org/apache/fop/pdf/PDFNavigator.java create mode 100644 src/java/org/apache/fop/pdf/PDFNavigatorAction.java create mode 100644 src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java create mode 100644 src/java/org/apache/fop/pdf/PDFTransitionAction.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java rename src/java/org/apache/fop/render/pdf/extensions/{AbstractPDFDictionaryElement.java => PDFCollectionEntryExtension.java} (64%) create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java delete mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java rename src/java/org/apache/fop/render/pdf/extensions/{PDFDictionaryEntryExtension.java => PDFObjectExtension.java} (74%) rename src/java/org/apache/fop/render/pdf/extensions/{PDFDictionaryEntryType.java => PDFObjectType.java} (62%) create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java create mode 100644 src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java create mode 100644 test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml create mode 100644 test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd index d6f0c694c..2e58a7f45 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd @@ -38,6 +38,7 @@ + diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index c79f975c1..f6de99dd0 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -1040,7 +1040,7 @@ public class AreaTreeParser { } private static final Object[] SUBSET_COMMON = new Object[] { - Trait.PROD_ID}; + Trait.PROD_ID, Trait.LAYER}; private static final Object[] SUBSET_LINK = new Object[] { Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK}; private static final Object[] SUBSET_COLOR = new Object[] { diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java index cd0d4becf..eac9d440d 100644 --- a/src/java/org/apache/fop/area/Trait.java +++ b/src/java/org/apache/fop/area/Trait.java @@ -169,9 +169,11 @@ public final class Trait implements Serializable { /** shift direction trait */ public static final Integer SHIFT_DIRECTION = 42; + /** For optional content groups. */ + public static final Integer LAYER = 43; /** Maximum value used by trait keys */ - public static final int MAX_TRAIT_KEY = 42; + public static final int MAX_TRAIT_KEY = 43; private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1]; @@ -243,6 +245,7 @@ public final class Trait implements Serializable { new TraitInfo("block-progression-direction", Direction.class)); put(SHIFT_DIRECTION, new TraitInfo("shift-direction", Direction.class)); + put(LAYER, new TraitInfo("layer", String.class)); } diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 28f02a762..086cbca40 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -816,8 +816,11 @@ public interface Constants { /** Scope for table header */ int PR_X_HEADER_COLUMN = 290; + /** For specifying PDF optional content group (layer) binding. */ + int PR_X_LAYER = 291; + /** Number of property constants defined */ - int PROPERTY_COUNT = 290; + int PROPERTY_COUNT = 291; // compound property constants diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 36e3f21c4..cc4fe9e2c 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -2721,6 +2721,13 @@ public final class FOPropertyMapping implements Constants { m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); m.setDefault("auto"); addPropertyMaker("z-index", m); + + // fox:layer + m = new StringProperty.Maker(PR_X_LAYER); + m.setInherited(false); + m.setDefault(""); + addPropertyMaker("fox:layer", m); + } private void createShorthandProperties() { diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index b6f2ec83e..0d1a2a84b 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -74,7 +74,8 @@ public abstract class FObj extends FONode implements Constants { private int bidiLevel = -1; // The value of properties relevant for all fo objects - private String id = null; + private String id; + private String layer; // End of property values /** @@ -173,6 +174,7 @@ public abstract class FObj extends FONode implements Constants { */ public void bind(PropertyList pList) throws FOPException { id = pList.get(PR_ID).getString(); + layer = pList.get(PR_X_LAYER).getString(); } /** @@ -583,6 +585,16 @@ public abstract class FObj extends FONode implements Constants { return (id != null && id.length() > 0); } + /** @return the "layer" property. */ + public String getLayer() { + return layer; + } + + /** @return whether this object has an layer set */ + public boolean hasLayer() { + return (layer != null && layer.length() > 0); + } + /** {@inheritDoc} */ public String getNamespaceURI() { return FOElementMapping.URI; diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 09b47f02a..82db43e59 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -66,6 +66,8 @@ public class ExtensionElementMapping extends ElementMapping { PROPERTY_ATTRIBUTES.add("border-before-end-radius"); PROPERTY_ATTRIBUTES.add("border-after-start-radius"); PROPERTY_ATTRIBUTES.add("border-after-end-radius"); + //Optional content groups (layers) + PROPERTY_ATTRIBUTES.add("layer"); } /** diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index a23cd28f1..eced48939 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -867,6 +867,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl transferForeignAttributes(viewportBlockArea); TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId()); + TraitSetter.setLayer(viewportBlockArea, getBlockContainerFO().getLayer()); TraitSetter.addBorders(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground(), discardBorderBefore, discardBorderAfter, false, false, this); diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 55b17e953..b29b95cb1 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -381,6 +381,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co getBlockFO().getCommonBorderPaddingBackground(), startIndent, endIndent, this); + TraitSetter.setLayer(curBlockArea, getBlockFO().getLayer()); curBlockArea.setLocale(getBlockFO().getCommonHyphenation().getLocale()); curBlockArea.setLocation(FONode.getLocatorString(getBlockFO().getLocator())); diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 739d535ca..af40f0681 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -617,4 +617,15 @@ public final class TraitSetter { area.addTrait(Trait.PROD_ID, id); } } + + /** + * Sets the optional content group layer as a trait on the area. + * @param area the area to set the traits on + * @param layer the layer ID to set + */ + public static void setLayer(Area area, String layer) { + if (layer != null && layer.length() > 0) { + area.addTrait(Trait.LAYER, layer); + } + } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index dd80db1d1..61dcf45c9 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -213,6 +213,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { } if (fobj instanceof Inline || fobj instanceof BasicLink) { TraitSetter.setProducerID(area, fobj.getId()); + TraitSetter.setLayer(area, fobj.getLayer()); } return area; } diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java index 41eed4885..13bd1bda1 100644 --- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java +++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java @@ -143,7 +143,7 @@ public abstract class AbstractPDFStream extends PDFObject { */ protected int outputStreamData(StreamCache encodedStream, OutputStream out) throws IOException { int length = 0; - byte[] p = encode("stream\n"); + byte[] p = encode("\nstream\n"); out.write(p); length += p.length; @@ -186,7 +186,7 @@ public abstract class AbstractPDFStream extends PDFObject { throws IOException { int bytesWritten = 0; //Stream header - byte[] buf = encode("stream\n"); + byte[] buf = encode("\nstream\n"); out.write(buf); bytesWritten += buf.length; diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java index 6bacd31a3..ae0b950fd 100644 --- a/src/java/org/apache/fop/pdf/PDFDictionary.java +++ b/src/java/org/apache/fop/pdf/PDFDictionary.java @@ -131,7 +131,7 @@ public class PDFDictionary extends PDFObject { } else { textBuffer.append('\n'); } - textBuffer.append(">>\n"); + textBuffer.append(">>"); } } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index ff9f61201..bcd54fcb9 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -155,6 +155,12 @@ public class PDFDocument { private List launches = new ArrayList(); + private List layers; + + private List navigators; + + private List navigatorActions; + private PDFFactory factory; private FileIDGenerator fileIDGenerator; @@ -477,6 +483,24 @@ public class PDFDocument { if (obj instanceof PDFGoToRemote) { this.gotoremotes.add((PDFGoToRemote) obj); } + if (obj instanceof PDFLayer) { + if (this.layers == null) { + this.layers = new ArrayList(); + } + this.layers.add((PDFLayer) obj); + } + if (obj instanceof PDFNavigator) { + if (this.navigators == null) { + this.navigators = new ArrayList(); + } + this.navigators.add((PDFNavigator) obj); + } + if (obj instanceof PDFNavigatorAction) { + if (this.navigatorActions == null) { + this.navigatorActions = new ArrayList(); + } + this.navigatorActions.add((PDFNavigatorAction) obj); + } } /** @@ -889,6 +913,34 @@ public class PDFDocument { this.accessibilityEnabled = enableAccessibility; } + /** + * + */ + public PDFReference resolveExtensionReference(String id) { + if (layers != null) { + for (PDFLayer layer : layers) { + if (layer.hasId(id)) { + return layer.makeReference(); + } + } + } + if (navigators != null) { + for (PDFNavigator navigator : navigators) { + if (navigator.hasId(id)) { + return navigator.makeReference(); + } + } + } + if (navigatorActions != null) { + for (PDFNavigatorAction action : navigatorActions) { + if (action.hasId(id)) { + return action.makeReference(); + } + } + } + return null; + } + /** * Writes out the entire document * @@ -1009,7 +1061,7 @@ public class PDFDocument { streamIndirectObjects(trailerObjects, stream); TrailerDictionary trailerDictionary = createTrailerDictionary(); long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary); - String trailer = "startxref\n" + startxref + "\n%%EOF\n"; + String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n"; stream.write(encode(trailer)); } diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 631499af1..5af33866e 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -1821,4 +1821,28 @@ public class PDFFactory { return obj; } + public PDFLayer makeLayer(String id) { + PDFLayer layer = new PDFLayer(id); + getDocument().registerObject(layer); + return layer; + } + + public PDFSetOCGStateAction makeSetOCGStateAction(String id) { + PDFSetOCGStateAction action = new PDFSetOCGStateAction(id); + getDocument().registerObject(action); + return action; + } + + public PDFTransitionAction makeTransitionAction(String id) { + PDFTransitionAction action = new PDFTransitionAction(id); + getDocument().registerObject(action); + return action; + } + + public PDFNavigator makeNavigator(String id) { + PDFNavigator navigator = new PDFNavigator(id); + getDocument().registerObject(navigator); + return navigator; + } + } diff --git a/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java new file mode 100644 index 000000000..c2d033aec --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +/** + * Identified Dictionary. + */ +public class PDFIdentifiedDictionary extends PDFDictionary { + + private final String id; + + public PDFIdentifiedDictionary(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } + + public boolean hasId(String id) { + return (this.id != null) && (id != null) && this.id.equals(id); + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFLayer.java b/src/java/org/apache/fop/pdf/PDFLayer.java new file mode 100644 index 000000000..f8f434e87 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFLayer.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Optional Content Group Dictionary, which we will call a 'layer'. + */ +public class PDFLayer extends PDFIdentifiedDictionary { + + public abstract static class Resolver { + private boolean resolved; + private PDFLayer layer; + private Object extension; + public Resolver(PDFLayer layer, Object extension) { + this.layer = layer; + this.extension = extension; + } + public PDFLayer getLayer() { + return layer; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFLayer(String id) { + super(id); + put("Type", new PDFName("OCG")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object name, Object intent, Object usage) { + if (name != null) { + put("Name", name); + } + if (intent != null) { + put("Intent", intent); + } + if (usage != null) { + put("Usage", usage); + } + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFNavigator.java b/src/java/org/apache/fop/pdf/PDFNavigator.java new file mode 100644 index 000000000..fdb97469b --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFNavigator.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Navigation Node Dictionary, which we call a 'navigator'. + * This class is used to for sub-page navigation. + */ +public class PDFNavigator extends PDFIdentifiedDictionary { + + public abstract static class Resolver { + private boolean resolved; + private PDFNavigator navigator; + private Object extension; + public Resolver(PDFNavigator navigator, Object extension) { + this.navigator = navigator; + this.extension = extension; + } + public PDFNavigator getNavigator() { + return navigator; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFNavigator(String id) { + super(id); + put("Type", new PDFName("NavNode")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object nextAction, Object nextNode, Object prevAction, Object prevNode, Object duration) { + if (nextAction != null) { + put("NA", nextAction); + } + if (nextNode != null) { + put("Next", nextNode); + } + if (prevAction != null) { + put("PA", prevAction); + } + if (prevNode != null) { + put("Prev", prevNode); + } + if (duration != null) { + put("Dur", duration); + } + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFNavigatorAction.java b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java new file mode 100644 index 000000000..ba32269b5 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +public abstract class PDFNavigatorAction extends PDFIdentifiedDictionary { + + protected PDFNavigatorAction(String id) { + super(id); + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFPaintingState.java b/src/java/org/apache/fop/pdf/PDFPaintingState.java index 29d022f61..f6528a30c 100644 --- a/src/java/org/apache/fop/pdf/PDFPaintingState.java +++ b/src/java/org/apache/fop/pdf/PDFPaintingState.java @@ -44,8 +44,6 @@ import org.apache.fop.util.AbstractPaintingState; * previous state then the necessary values can be overridden. * The current transform behaves differently to other values as the * matrix is combined with the current resolved value. - * It is impossible to optimise the result without analysing the all - * the possible combinations after completing. */ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState { @@ -187,6 +185,36 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState return newState; } + public void setLayer(String layer) { + getPDFData().setLayer(layer); + } + + public String getLayer() { + return getPDFData().getLayer(); + } + + public boolean getLayerChanged() { + String layerCurrent = getLayer(); + if (layerCurrent == null) { + return false; + } else if (getStateStack().isEmpty()) { + return true; + } else { + for (int i = getStackLevel(); i > 0; --i) { + String layerPrev = ((PDFData) getStateStack().get(i - 1)).getLayer(); + if (layerPrev == null) { + continue; + } else { + // Both current and prior are set, so, if same, then we know layer + // didn't change (and can stop search), otherwise it did change. + return !layerCurrent.equals(layerPrev); + } + } + // Current layer set, but no prior saved layer set, so must have changed. + return true; + } + } + /** {@inheritDoc} */ @Override protected AbstractData instantiateData() { @@ -209,7 +237,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState AbstractData data = getData(); AbstractData copy = (AbstractData)data.clone(); data.clearTransform(); - getStateStack().add(copy); + getStateStack().push(copy); } private PDFData getPDFData() { diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java index cded7c00a..6d09d5738 100644 --- a/src/java/org/apache/fop/pdf/PDFResources.java +++ b/src/java/org/apache/fop/pdf/PDFResources.java @@ -37,8 +37,8 @@ import org.apache.fop.fonts.base14.ZapfDingbats; /** * Class representing a /Resources object. * - * /Resources object contain a list of references to the fonts for the - * document + * /Resources object contain a list of references to the fonts, patterns, + * shadings, etc., for the document. */ public class PDFResources extends PDFDictionary { @@ -73,6 +73,9 @@ public class PDFResources extends PDFDictionary { /** Map of ICC color spaces (key: ICC profile description) */ protected Map iccColorSpaces = new LinkedHashMap(); + /** Named properties */ + protected Map properties = new LinkedHashMap(); + /** * create a /Resources object. * @@ -191,6 +194,25 @@ public class PDFResources extends PDFDictionary { return cs; } + /** + * Add a named property. + * + * @param name name of property + * @param property reference to property value + */ + public void addProperty(String name, PDFReference property) { + this.properties.put(name, property); + } + + /** + * Get a named property. + * + * @param name name of property + */ + public PDFReference getProperty(String name) { + return this.properties.get(name); + } + @Override public int output(OutputStream stream) throws IOException { populateDictionary(); @@ -253,6 +275,14 @@ public class PDFResources extends PDFDictionary { } put("ColorSpace", dict); } + + if (!properties.isEmpty()) { + PDFDictionary dict = new PDFDictionary(this); + for (String name : properties.keySet()) { + dict.put(name, properties.get(name)); + } + put("Properties", dict); + } } } diff --git a/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java new file mode 100644 index 000000000..a47c5cd59 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +public class PDFSetOCGStateAction extends PDFNavigatorAction { + + public abstract static class Resolver { + private boolean resolved; + private PDFSetOCGStateAction action; + private Object extension; + public Resolver(PDFSetOCGStateAction action, Object extension) { + this.action = action; + this.extension = extension; + } + public PDFSetOCGStateAction getAction() { + return action; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFSetOCGStateAction(String id) { + super(id); + put("Type", new PDFName("Action")); + put("S", new PDFName("SetOCGState")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object state, Object preserveRB, Object nextAction) { + if (state != null) { + put("State", state); + } + if (preserveRB != null) { + put("PreserveRB", preserveRB); + } + if (nextAction != null) { + put("Next", nextAction); + } + } +} diff --git a/src/java/org/apache/fop/pdf/PDFTransitionAction.java b/src/java/org/apache/fop/pdf/PDFTransitionAction.java new file mode 100644 index 000000000..01f8fcf21 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFTransitionAction.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; + +public class PDFTransitionAction extends PDFNavigatorAction { + + public abstract static class Resolver { + private boolean resolved; + private PDFTransitionAction action; + private Object extension; + public Resolver(PDFTransitionAction action, Object extension) { + this.action = action; + this.extension = extension; + } + public PDFTransitionAction getAction() { + return action; + } + public Object getExtension() { + return extension; + } + public void resolve() { + if (!resolved) { + performResolution(); + resolved = true; + } + } + protected void performResolution() { + } + } + + private Resolver resolver; + + public PDFTransitionAction(String id) { + super(id); + put("Type", new PDFName("Action")); + put("S", new PDFName("Trans")); + } + + @Override + public int output(OutputStream stream) throws IOException { + if (resolver != null) { + resolver.resolve(); + } + return super.output(stream); + } + + public void setResolver(Resolver resolver) { + this.resolver = resolver; + } + + public void populate(Object transition, Object nextAction) { + if (transition != null) { + put("Trans", transition); + } + if (nextAction != null) { + put("Next", nextAction); + } + } +} diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index e274e5c4b..9a94e0cc6 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -28,6 +28,7 @@ import java.io.OutputStream; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.Stack; import org.w3c.dom.Document; @@ -112,8 +113,12 @@ public abstract class AbstractRenderer /** the currently active PageViewport */ protected PageViewport currentPageViewport; + /* warned XML handlers */ private Set warnedXMLHandlers; + /* layers stack */ + private Stack layers; + /** {@inheritDoc} */ public abstract void setupFontInfo(FontInfo fontInfo) throws FOPException; @@ -471,6 +476,10 @@ public abstract class AbstractRenderer * @param children The children to render within the block viewport */ protected void renderBlockViewport(BlockViewport bv, List children) { + boolean inNewLayer = false; + if (maybeStartLayer(bv)) { + inNewLayer = true; + } // clip and position viewport if necessary if (bv.getPositioning() == Block.ABSOLUTE) { // save positions @@ -506,6 +515,7 @@ public abstract class AbstractRenderer currentIPPosition = saveIP; currentBPPosition = saveBP + bv.getAllocBPD(); } + maybeEndLayer(bv, inNewLayer); } /** @@ -573,6 +583,10 @@ public abstract class AbstractRenderer protected void renderBlock(Block block) { assert block != null; List children = block.getChildAreas(); + boolean inNewLayer = false; + if (maybeStartLayer(block)) { + inNewLayer = true; + } if (block instanceof BlockViewport) { if (children != null) { renderBlockViewport((BlockViewport) block, children); @@ -607,6 +621,45 @@ public abstract class AbstractRenderer currentBPPosition = saveBP + block.getAllocBPD(); } } + maybeEndLayer(block, inNewLayer); + } + + /** + * Establish new optional content group layer. + * @param layer name of layer + */ + protected abstract void startLayer(String layer); + + /** + * Finish current optional content group layer. + */ + protected abstract void endLayer(); + + protected boolean maybeStartLayer(Area area) { + String layer = (String) area.getTrait(Trait.LAYER); + if (layer != null) { + if (layers == null) { + layers = new Stack(); + } + if (layers.empty() || !layers.peek().equals(layer)) { + layers.push(layer); + startLayer(layer); + return true; + } + } + return false; + } + + protected void maybeEndLayer(Area area, boolean inNewLayer) { + if (inNewLayer) { + assert layers != null; + assert !layers.empty(); + String layer = (String) area.getTrait(Trait.LAYER); + assert layer != null; + assert layers.peek().equals(layer); + endLayer(); + layers.pop(); + } } /** @@ -746,6 +799,10 @@ public abstract class AbstractRenderer * @param ip the inline parent to render */ protected void renderInlineParent(InlineParent ip) { + boolean inNewLayer = false; + if (maybeStartLayer(ip)) { + inNewLayer = true; + } int level = ip.getBidiLevel(); List children = ip.getChildAreas(); renderInlineAreaBackAndBorders(ip); @@ -782,6 +839,7 @@ public abstract class AbstractRenderer } currentIPPosition = saveIP + ip.getAllocIPD(); currentBPPosition = saveBP; + maybeEndLayer(ip, inNewLayer); } /** diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index 12713bb24..aaa702afa 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -164,7 +164,7 @@ public class AFPPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { try { saveGraphicsState(); concatenateTransformationMatrix(transform); diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 2dd046fa9..f69fe2091 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -126,8 +126,8 @@ public abstract class AbstractIFPainter implements } /** {@inheritDoc} */ - public void startGroup(AffineTransform[] transforms) throws IFException { - startGroup(combine(transforms)); + public void startGroup(AffineTransform[] transforms, String layer) throws IFException { + startGroup(combine(transforms), layer); } /** diff --git a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java index 868615360..c1742be1f 100644 --- a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java @@ -89,6 +89,7 @@ public class IFGraphicContext extends GraphicContext { public static class Group { private AffineTransform[] transforms; + private String layer; /** * Construct a Group. @@ -106,6 +107,16 @@ public class IFGraphicContext extends GraphicContext { this(new AffineTransform[] {transform}); } + /** + * Construct a layer Group, i.e., a Group with no transforms + * but with a optional content group layer label. + * @param layer a layer label + */ + public Group(String layer) { + this(); + this.layer = layer; + } + /** Default constructor. */ public Group() { this(EMPTY_TRANSFORM_ARRAY); @@ -116,12 +127,17 @@ public class IFGraphicContext extends GraphicContext { return this.transforms; } + /** @return layer */ + public String getLayer() { + return this.layer; + } + /** * @param painter a painter * @throws IFException in not caught */ public void start(IFPainter painter) throws IFException { - painter.startGroup(transforms); + painter.startGroup(transforms, layer); } /** @@ -136,6 +152,11 @@ public class IFGraphicContext extends GraphicContext { public String toString() { StringBuffer sb = new StringBuffer("group: "); IFUtil.toString(getTransforms(), sb); + if ((layer != null) && (layer.length() > 0)) { + sb.append(" layer("); + sb.append(layer); + sb.append(')'); + } return sb.toString(); } diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java index 599292287..d43b5cb85 100644 --- a/src/java/org/apache/fop/render/intermediate/IFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java @@ -113,19 +113,21 @@ public interface IFPainter { /** * Starts a new group of graphical elements. Corresponds to SVG's g element. * @param transforms a series of transformation matrices establishing the new coordinate system + * @param layer an optional layer label (or null if none) * @throws IFException if an error occurs while handling this element */ - void startGroup(AffineTransform[] transforms) throws IFException; + void startGroup(AffineTransform[] transforms, String layer) throws IFException; /** * Starts a new group of graphical elements. Corresponds to SVG's g element. * @param transform the transformation matrix establishing the new coordinate system + * @param layer an optional layer label (or null if none) * @throws IFException if an error occurs while handling this element */ - void startGroup(AffineTransform transform) throws IFException; + void startGroup(AffineTransform transform, String layer) throws IFException; /** - * Ends the current group and restores the previous coordinate system. + * Ends the current group and restores the previous coordinate system (and layer). * @throws IFException if an error occurs while handling this element */ void endGroup() throws IFException; diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 1af1d4a06..519726291 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -592,7 +592,8 @@ public class IFParser implements IFConstants { String transform = attributes.getValue("transform"); AffineTransform[] transforms = AffineTransformArrayParser.createAffineTransform(transform); - painter.startGroup(transforms); + String layer = attributes.getValue("layer"); + painter.startGroup(transforms, layer); } public void endElement() throws IFException { @@ -800,8 +801,8 @@ public class IFParser implements IFConstants { } painter.drawImage(uri, new Rectangle(x, y, width, height)); } - resetForeignAttributes(); resetStructureTreeElement(); + resetForeignAttributes(); inForeignObject = false; } diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 30ceda108..e40a8f6b3 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -742,6 +742,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { protected void renderBlockViewport(BlockViewport bv, List children) { //Essentially the same code as in the super class but optimized for the IF + // Handle new layer. + boolean inNewLayer = false; + if (maybeStartLayer(bv)) { + inNewLayer = true; + } + //This is the content-rect Dimension dim = new Dimension(bv.getIPD(), bv.getBPD()); viewportDimensionStack.push(dim); @@ -842,6 +848,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { currentBPPosition += bv.getAllocBPD(); } viewportDimensionStack.pop(); + maybeEndLayer(bv, inNewLayer); } /** {@inheritDoc} */ @@ -849,7 +856,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { StructureTreeElement structElem = (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT); establishStructureTreeElement(structElem); - pushdID(viewport); + pushID(viewport); Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); viewportDimensionStack.push(dim); super.renderInlineViewport(viewport); @@ -895,10 +902,27 @@ public class IFRenderer extends AbstractPathOrientedRenderer { restoreGraphicsState(); } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + if (log.isTraceEnabled()) { + log.trace("startLayer() layer=" + layer); + } + saveGraphicsState(); + pushGroup(new IFGraphicContext.Group(layer)); + } + + /** {@inheritDoc} */ + protected void endLayer() { + if (log.isTraceEnabled()) { + log.trace("endLayer()"); + } + restoreGraphicsState(); + } + /** {@inheritDoc} */ protected void renderInlineArea(InlineArea inlineArea) { saveInlinePosIfTargetable(inlineArea); - pushdID(inlineArea); + pushID(inlineArea); super.renderInlineArea(inlineArea); popID(inlineArea); } @@ -965,7 +989,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { log.trace("renderBlock() " + block); } saveBlockPosIfTargetable(block); - pushdID(block); + pushID(block); IFContext context = documentHandler.getContext(); Locale oldLocale = context.getLanguage(); context.setLanguage(block.getLocale()); @@ -977,7 +1001,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { popID(block); } - private void pushdID(Area area) { + private void pushID(Area area) { String prodID = (String) area.getTrait(Trait.PROD_ID); if (prodID != null) { ids.push(prodID); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 1ffd42863..395e79719 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -428,21 +428,24 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler { } /** {@inheritDoc} */ - public void startGroup(AffineTransform[] transforms) throws IFException { - startGroup(IFUtil.toString(transforms)); + public void startGroup(AffineTransform[] transforms, String layer) throws IFException { + startGroup(IFUtil.toString(transforms), layer); } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - startGroup(IFUtil.toString(transform)); + public void startGroup(AffineTransform transform, String layer) throws IFException { + startGroup(IFUtil.toString(transform), layer); } - private void startGroup(String transform) throws IFException { + private void startGroup(String transform, String layer) throws IFException { try { AttributesImpl atts = new AttributesImpl(); if (transform != null && transform.length() > 0) { addAttribute(atts, "transform", transform); } + if (layer != null && layer.length() > 0) { + addAttribute(atts, "layer", layer); + } handler.startElement(EL_GROUP, atts); } catch (SAXException e) { throw new IFException("SAX error in startGroup()", e); diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 07440ff0b..328d1a4f8 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -143,7 +143,7 @@ public class Java2DPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { saveGraphicsState(); try { concatenateTransformationMatrix(transform); diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java index a98aff353..d343c55d8 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java +++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java @@ -475,6 +475,14 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem restoreGraphicsState(); } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + } + + /** {@inheritDoc} */ + protected void endLayer() { + } + /** {@inheritDoc} */ protected List breakOutOfStateStack() { log.debug("Block.FIXED --> break out"); diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index c51ee834f..f934eed8c 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -126,7 +126,7 @@ public class PCLPainter extends AbstractIFPainter implements } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { saveGraphicsState(); try { concatenateTransformationMatrix(transform); diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java index dde6b0ef3..ac7b1d905 100644 --- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java +++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@ -30,6 +30,7 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFFilterList; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFPaintingState; +import org.apache.fop.pdf.PDFReference; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFStream; import org.apache.fop.pdf.PDFText; @@ -55,7 +56,7 @@ public class PDFContentGenerator { private PDFColorHandler colorHandler; /** drawing state */ - protected PDFPaintingState currentState = null; + protected PDFPaintingState currentState; /** Text generation utility holding the current font status */ protected PDFTextUtil textutil; @@ -156,15 +157,23 @@ public class PDFContentGenerator { */ protected void comment(String text) { if (WRITE_COMMENTS) { - currentStream.add("% " + text + "\n"); + getStream().add("% " + text + "\n"); } } /** Save graphics state. */ protected void saveGraphicsState() { endTextObject(); - currentState.save(); - currentStream.add("q\n"); + getState().save(); + getStream().add("q\n"); + } + + /** Save graphics state with optional layer. */ + protected void saveGraphicsState(String layer) { + endTextObject(); + getState().save(); + maybeBeginLayer(layer); + getStream().add("q\n"); } /** @@ -174,9 +183,9 @@ public class PDFContentGenerator { */ protected void saveGraphicsState(String structElemType, int sequenceNum) { endTextObject(); - currentState.save(); + getState().save(); beginMarkedContentSequence(structElemType, sequenceNum); - currentStream.add("q\n"); + getStream().add("q\n"); } /** @@ -208,18 +217,18 @@ public class PDFContentGenerator { if (structElemType != null) { String actualTextProperty = actualText == null ? "" : " /ActualText " + PDFText.escapeText(actualText); - currentStream.add(structElemType + " <>\n" + "BDC\n"); } else { - currentStream.add("/Artifact\nBMC\n"); + getStream().add("/Artifact\nBMC\n"); this.inArtifactMode = true; } this.inMarkedContentSequence = true; } void endMarkedContentSequence() { - currentStream.add("EMC\n"); + getStream().add("EMC\n"); this.inMarkedContentSequence = false; this.inArtifactMode = false; } @@ -231,9 +240,10 @@ public class PDFContentGenerator { */ protected void restoreGraphicsState(boolean popState) { endTextObject(); - currentStream.add("Q\n"); + getStream().add("Q\n"); + maybeEndLayer(); if (popState) { - currentState.restore(); + getState().restore(); } } @@ -251,11 +261,42 @@ public class PDFContentGenerator { */ protected void restoreGraphicsStateAccess() { endTextObject(); - currentStream.add("Q\n"); + getStream().add("Q\n"); if (this.inMarkedContentSequence) { endMarkedContentSequence(); } - currentState.restore(); + getState().restore(); + } + + private void maybeBeginLayer(String layer) { + if ((layer != null) && (layer.length() > 0)) { + getState().setLayer(layer); + beginOptionalContent(layer); + } + } + + private void maybeEndLayer() { + if (getState().getLayerChanged()) { + endOptionalContent(); + } + } + + private int ocNameIndex = 0; + + private void beginOptionalContent(String layerId) { + String name; + PDFReference layer = document.resolveExtensionReference(layerId); + if (layer != null) { + name = "oc" + ++ocNameIndex; + document.getResources().addProperty(name, layer); + } else { + name = "unknown"; + } + getStream().add("/OC /" + name + " BDC\n"); + } + + private void endOptionalContent() { + getStream().add("EMC\n"); } /** Indicates the beginning of a text object. */ @@ -310,8 +351,8 @@ public class PDFContentGenerator { public void concatenate(AffineTransform transform) { this.transform = transform; if (!transform.isIdentity()) { - currentState.concatenate(transform); - currentStream.add(CTMHelper.toPDFString(transform, false) + " cm\n"); + getState().concatenate(transform); + getStream().add(CTMHelper.toPDFString(transform, false) + " cm\n"); } } @@ -333,7 +374,7 @@ public class PDFContentGenerator { * @param content the PDF content */ public void add(String content) { - currentStream.add(content); + getStream().add(content); } /** @@ -350,9 +391,9 @@ public class PDFContentGenerator { * @param width line width in points */ public void updateLineWidth(float width) { - if (currentState.setLineWidth(width)) { + if (getState().setLineWidth(width)) { //Only write if value has changed WRT the current line width - currentStream.add(format(width) + " w\n"); + getStream().add(format(width) + " w\n"); } } @@ -362,7 +403,7 @@ public class PDFContentGenerator { */ public void updateCharacterSpacing(float value) { if (getState().setCharacterSpacing(value)) { - currentStream.add(format(value) + " Tc\n"); + getStream().add(format(value) + " Tc\n"); } } @@ -400,7 +441,7 @@ public class PDFContentGenerator { if (pdf != null) { colorHandler.establishColor(pdf, col, fill); } else { - setColor(col, fill, this.currentStream); + setColor(col, fill, getStream()); } } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 0a3d34ef4..648cdce7a 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -306,8 +306,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe); } } else if (extension instanceof PDFDictionaryAttachment) { - PDFDictionaryAttachment dictionaryExtension = (PDFDictionaryAttachment) extension; - pdfUtil.renderDictionaryExtension(dictionaryExtension, currentPage); + pdfUtil.renderDictionaryExtension((PDFDictionaryAttachment) extension, currentPage); } else if (extension != null) { log.debug("Don't know how to handle extension object. Ignoring: " + extension + " (" + extension.getClass().getName() + ")"); diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index d9fdd399c..f85328b8b 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -143,8 +143,8 @@ public class PDFPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - generator.saveGraphicsState(); + public void startGroup(AffineTransform transform, String layer) throws IFException { + generator.saveGraphicsState(layer); generator.concatenate(toPoints(transform)); } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index 962cd7847..d27fead12 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -27,6 +27,7 @@ import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.EnumMap; +import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; @@ -55,24 +56,33 @@ import org.apache.fop.pdf.PDFFileSpec; import org.apache.fop.pdf.PDFICCBasedColorSpace; import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFInfo; +import org.apache.fop.pdf.PDFLayer; import org.apache.fop.pdf.PDFMetadata; import org.apache.fop.pdf.PDFName; import org.apache.fop.pdf.PDFNames; +import org.apache.fop.pdf.PDFNavigator; +import org.apache.fop.pdf.PDFNull; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFOutputIntent; import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFPageLabels; import org.apache.fop.pdf.PDFReference; +import org.apache.fop.pdf.PDFSetOCGStateAction; import org.apache.fop.pdf.PDFText; +import org.apache.fop.pdf.PDFTransitionAction; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.Version; import org.apache.fop.pdf.VersionController; +import org.apache.fop.render.pdf.extensions.PDFActionExtension; +import org.apache.fop.render.pdf.extensions.PDFArrayExtension; +import org.apache.fop.render.pdf.extensions.PDFCollectionEntryExtension; import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment; -import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryExtension; -import org.apache.fop.render.pdf.extensions.PDFDictionaryEntryType; import org.apache.fop.render.pdf.extensions.PDFDictionaryExtension; import org.apache.fop.render.pdf.extensions.PDFDictionaryType; import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment; +import org.apache.fop.render.pdf.extensions.PDFObjectType; +import org.apache.fop.render.pdf.extensions.PDFPageExtension; +import org.apache.fop.render.pdf.extensions.PDFReferenceExtension; import static org.apache.fop.render.pdf.PDFEncryptionOption.ENCRYPTION_PARAMS; import static org.apache.fop.render.pdf.PDFEncryptionOption.NO_ACCESSCONTENT; @@ -260,10 +270,189 @@ class PDFRenderingUtil { public void renderDictionaryExtension(PDFDictionaryAttachment attachment, PDFPage currentPage) { PDFDictionaryExtension extension = attachment.getExtension(); - if (extension.getDictionaryType() == PDFDictionaryType.Catalog) { + PDFDictionaryType type = extension.getDictionaryType(); + if (type == PDFDictionaryType.Action) { + addNavigatorAction(extension); + } else if (type == PDFDictionaryType.Layer) { + addLayer(extension); + } else if (type == PDFDictionaryType.Navigator) { + addNavigator(extension); + } else { + renderDictionaryExtension(extension, currentPage); + } + } + + public void addLayer(PDFDictionaryExtension extension) { + assert extension.getDictionaryType() == PDFDictionaryType.Layer; + String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if ((id != null) && (id.length() > 0)) { + PDFLayer layer = pdfDoc.getFactory().makeLayer(id); + layer.setResolver(new PDFLayer.Resolver(layer, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object name = extension.findEntryValue("Name"); + Object intent = extension.findEntryValue("Intent"); + Object usage = makeDictionary(extension.findEntryValue("Usage")); + getLayer().populate(name, intent, usage); + } + }); + } + } + + public void addNavigatorAction(PDFDictionaryExtension extension) { + assert extension.getDictionaryType() == PDFDictionaryType.Action; + String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if ((id != null) && (id.length() > 0)) { + String type = extension.getProperty(PDFActionExtension.PROPERTY_TYPE); + if (type != null) { + if (type.equals("SetOCGState")) { + PDFSetOCGStateAction action = pdfDoc.getFactory().makeSetOCGStateAction(id); + action.setResolver(new PDFSetOCGStateAction.Resolver(action, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object state = makeArray(extension.findEntryValue("State")); + Object preserveRB = extension.findEntryValue("PreserveRB"); + Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next")); + getAction().populate(state, preserveRB, nextAction); + } + }); + } else if (type.equals("Trans")) { + PDFTransitionAction action = pdfDoc.getFactory().makeTransitionAction(id); + action.setResolver(new PDFTransitionAction.Resolver(action, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object transition = makeDictionary(extension.findEntryValue("Trans")); + Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next")); + getAction().populate(transition, nextAction); + } + }); + } else { + throw new UnsupportedOperationException(); + } + } + } + } + + public void addNavigator(PDFDictionaryExtension extension) { + assert extension.getDictionaryType() == PDFDictionaryType.Navigator; + String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if ((id != null) && (id.length() > 0)) { + PDFNavigator navigator = pdfDoc.getFactory().makeNavigator(id); + navigator.setResolver(new PDFNavigator.Resolver(navigator, extension) { + public void performResolution() { + PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension(); + Object nextAction = makeDictionary(extension.findEntryValue("NA")); + Object next = makeDictionary(extension.findEntryValue("Next")); + Object prevAction = makeDictionary(extension.findEntryValue("PA")); + Object prev = makeDictionary(extension.findEntryValue("Prev")); + Object duration = extension.findEntryValue("Dur"); + getNavigator().populate(nextAction, next, prevAction, prev, duration); + } + }); + } + } + + private Object makeArray(Object value) { + if (value == null) { + return null; + } else if (value instanceof PDFReferenceExtension) { + return resolveReference((PDFReferenceExtension) value); + } else if (value instanceof List) { + return populateArray(new PDFArray(), (List) value); + } else { + throw new IllegalArgumentException(); + } + } + + private Object populateArray(PDFArray array, List entries) { + for (PDFCollectionEntryExtension entry : (List) entries) { + PDFObjectType type = entry.getType(); + if (type == PDFObjectType.Array) { + array.add(makeArray(entry.getValue())); + } else if (type == PDFObjectType.Boolean) { + array.add(entry.getValueAsBoolean()); + } else if (type == PDFObjectType.Dictionary) { + array.add(makeDictionary(entry.getValue())); + } else if (type == PDFObjectType.Name) { + array.add(new PDFName(entry.getValueAsString())); + } else if (type == PDFObjectType.Number) { + array.add(new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFObjectType.Reference) { + array.add(resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { + array.add(entry.getValue()); + } + } + return array; + } + + private Object makeDictionary(Object value) { + if (value == null) { + return null; + } else if (value instanceof PDFReferenceExtension) { + return resolveReference((PDFReferenceExtension) value); + } else if (value instanceof List) { + return populateDictionary(new PDFDictionary(), (List) value); + } else { + throw new IllegalArgumentException(); + } + } + + private Object populateDictionary(PDFDictionary dictionary, List entries) { + for (PDFCollectionEntryExtension entry : (List) entries) { + PDFObjectType type = entry.getType(); + String key = entry.getKey(); + if (type == PDFObjectType.Array) { + dictionary.put(key, makeArray(entry.getValue())); + } else if (type == PDFObjectType.Boolean) { + dictionary.put(key, entry.getValueAsBoolean()); + } else if (type == PDFObjectType.Dictionary) { + dictionary.put(key, makeDictionary(entry.getValue())); + } else if (type == PDFObjectType.Name) { + dictionary.put(key, new PDFName(entry.getValueAsString())); + } else if (type == PDFObjectType.Number) { + dictionary.put(key, new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFObjectType.Reference) { + dictionary.put(key, resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { + dictionary.put(key, entry.getValue()); + } + } + return dictionary; + } + + private Object makeDictionaryOrArray(Object value) { + if (value == null) { + return null; + } else if (value instanceof PDFReferenceExtension) { + return resolveReference((PDFReferenceExtension) value); + } else if (value instanceof List) { + if (hasKeyedEntry((List) value)) { + return populateDictionary(new PDFDictionary(), (List) value); + } else { + return populateArray(new PDFArray(), (List) value); + } + } else { + throw new IllegalArgumentException(); + } + } + + private boolean hasKeyedEntry(List entries) { + for (PDFCollectionEntryExtension entry : (List) entries) { + if (entry.getKey() != null) { + return true; + } + } + return false; + } + + public void renderDictionaryExtension(PDFDictionaryExtension extension, PDFPage currentPage) { + PDFDictionaryType type = extension.getDictionaryType(); + if (type == PDFDictionaryType.Catalog) { augmentDictionary(pdfDoc.getRoot(), extension); - } else if (extension.getDictionaryType() == PDFDictionaryType.Page) { - if (extension.matchesPageNumber(currentPage.getPageIndex() + 1)) { + } else if (type == PDFDictionaryType.Page) { + assert extension instanceof PDFPageExtension; + if (((PDFPageExtension) extension).matchesPageNumber(currentPage.getPageIndex() + 1)) { augmentDictionary(currentPage, extension); } } else { @@ -272,9 +461,11 @@ class PDFRenderingUtil { } private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) { - for (PDFDictionaryEntryExtension entry : extension.getEntries()) { + for (PDFCollectionEntryExtension entry : extension.getEntries()) { if (entry instanceof PDFDictionaryExtension) { dictionary.put(entry.getKey(), augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); + } else if (entry instanceof PDFArrayExtension) { + dictionary.put(entry.getKey(), augmentArray(new PDFArray(dictionary), (PDFArrayExtension) entry)); } else { augmentDictionary(dictionary, entry); } @@ -282,22 +473,68 @@ class PDFRenderingUtil { return dictionary; } - private void augmentDictionary(PDFDictionary dictionary, PDFDictionaryEntryExtension entry) { - PDFDictionaryEntryType type = entry.getType(); + private void augmentDictionary(PDFDictionary dictionary, PDFCollectionEntryExtension entry) { + PDFObjectType type = entry.getType(); String key = entry.getKey(); - if (type == PDFDictionaryEntryType.Boolean) { + if (type == PDFObjectType.Boolean) { dictionary.put(key, entry.getValueAsBoolean()); - } else if (type == PDFDictionaryEntryType.Name) { + } else if (type == PDFObjectType.Name) { dictionary.put(key, new PDFName(entry.getValueAsString())); - } else if (type == PDFDictionaryEntryType.Number) { + } else if (type == PDFObjectType.Number) { dictionary.put(key, new PDFNumber(entry.getValueAsNumber())); - } else if (type == PDFDictionaryEntryType.String) { + } else if (type == PDFObjectType.Reference) { + assert entry instanceof PDFReferenceExtension; + dictionary.put(key, resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { dictionary.put(key, entry.getValueAsString()); } else { throw new IllegalStateException(); } } + private Object resolveReference(PDFReferenceExtension entry) { + PDFReference reference = (PDFReference) entry.getResolvedReference(); + if (reference == null) { + reference = pdfDoc.resolveExtensionReference(entry.getReferenceId()); + if (reference != null) { + entry.setResolvedReference(reference); + } + return reference; + } + return PDFNull.INSTANCE; + } + + private PDFArray augmentArray(PDFArray array, PDFArrayExtension extension) { + for (PDFCollectionEntryExtension entry : extension.getEntries()) { + if (entry instanceof PDFDictionaryExtension) { + array.add(augmentDictionary(new PDFDictionary(array), (PDFDictionaryExtension) entry)); + } else if (entry instanceof PDFArrayExtension) { + array.add(augmentArray(new PDFArray(array), (PDFArrayExtension) entry)); + } else { + augmentArray(array, entry); + } + } + return array; + } + + private void augmentArray(PDFArray array, PDFCollectionEntryExtension entry) { + PDFObjectType type = entry.getType(); + if (type == PDFObjectType.Boolean) { + array.add(entry.getValueAsBoolean()); + } else if (type == PDFObjectType.Name) { + array.add(new PDFName(entry.getValueAsString())); + } else if (type == PDFObjectType.Number) { + array.add(new PDFNumber(entry.getValueAsNumber())); + } else if (type == PDFObjectType.Reference) { + assert entry instanceof PDFReferenceExtension; + array.add(resolveReference((PDFReferenceExtension) entry)); + } else if (type == PDFObjectType.String) { + array.add(entry.getValueAsString()); + } else { + throw new IllegalStateException(); + } + } + public PDFDocument setupPDFDocument(OutputStream out) throws IOException { if (this.pdfDoc != null) { throw new IllegalStateException("PDFDocument already set up"); diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java new file mode 100644 index 000000000..e4a5747e6 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:action. + */ +public class PDFActionElement extends PDFDictionaryElement { + + public static final String ATT_TYPE = PDFActionExtension.PROPERTY_TYPE; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFActionElement(FONode parent) { + super(parent, PDFDictionaryType.Action); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + String type = attlist.getValue(ATT_TYPE); + if (type != null) { + getDictionaryExtension().setProperty(PDFActionExtension.PROPERTY_TYPE, type); + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java new file mode 100644 index 000000000..778b8a203 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFActionExtension extends PDFDictionaryExtension { + + public static final String PROPERTY_TYPE = "type"; + + PDFActionExtension() { + super(PDFDictionaryType.Action); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java new file mode 100644 index 000000000..1f3ba22b2 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:array. + */ +public class PDFArrayElement extends PDFCollectionEntryElement { + + private PDFArrayExtension extension; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFArrayElement(FONode parent) { + super(parent, PDFObjectType.Array, new PDFArrayExtension()); + } + + public PDFArrayExtension getArrayExtension() { + assert getExtension() instanceof PDFArrayExtension; + return (PDFArrayExtension) getExtension(); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + } + + @Override + protected void addChildNode(FONode child) throws FOPException { + PDFArrayExtension extension = getArrayExtension(); + if (child instanceof PDFCollectionEntryElement) { + PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension(); + if (entry.getKey() == null) { + extension.addEntry(entry); + } + } + } + + @Override + public void endOfNode() throws FOPException { + super.endOfNode(); + } + + @Override + public String getLocalName() { + return PDFObjectType.Array.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java new file mode 100644 index 000000000..80c6c94e4 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import java.util.List; +import java.util.Map; + +// CSOFF: LineLengthCheck + +public class PDFArrayExtension extends PDFCollectionExtension { + + private static final long serialVersionUID = -1L; + + private Map properties; + private List entries; + + PDFArrayExtension() { + super(PDFObjectType.Array); + this.properties = new java.util.HashMap(); + this.entries = new java.util.ArrayList(); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue() { + return getEntries(); + } + + public void setProperty(String name, String value) { + properties.put(name, value); + } + + public String getProperty(String name) { + return properties.get(name); + } + + @Override + public void addEntry(PDFCollectionEntryExtension entry) { + if (entry.getKey() != null) { + throw new IllegalArgumentException(); + } else { + entries.add(entry); + } + } + + public List getEntries() { + return entries; + } + + public PDFCollectionEntryExtension getLastEntry() { + if (entries.size() > 0) { + return entries.get(entries.size() - 1); + } else { + return null; + } + } + + @Override + public String getElementName() { + return PDFObjectType.Array.elementName(); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java new file mode 100644 index 000000000..029357d22 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:catalog. + */ +public class PDFCatalogElement extends PDFDictionaryElement { + + PDFCatalogElement(FONode parent) { + super(parent, PDFDictionaryType.Catalog); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java new file mode 100644 index 000000000..381f6fbe8 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFCatalogExtension extends PDFDictionaryExtension { + + PDFCatalogExtension() { + super(PDFDictionaryType.Catalog); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java new file mode 100644 index 000000000..4185ceef0 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for collection entries: pdf:{array,boolean,dictionary,name,number,reference,string}. The specific type + * of entry is established at construction type. + */ +public class PDFCollectionEntryElement extends AbstractPDFExtensionElement { + + public static final String ATT_KEY = PDFCollectionEntryExtension.PROPERTY_KEY; + + private PDFCollectionEntryExtension extension; + private StringBuffer characters; + + PDFCollectionEntryElement(FONode parent, PDFObjectType type, PDFCollectionEntryExtension extension) { + super(parent); + this.extension = extension; + } + + PDFCollectionEntryElement(FONode parent, PDFObjectType type) { + this(parent, type, createExtension(type)); + } + + private static PDFCollectionEntryExtension createExtension(PDFObjectType type) { + if (type == PDFObjectType.Reference) { + return new PDFReferenceExtension(); + } else { + return new PDFCollectionEntryExtension(type); + } + } + + public PDFCollectionEntryExtension getExtension() { + return extension; + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + if (parent instanceof PDFDictionaryElement) { + String key = attlist.getValue(ATT_KEY); + if (key == null) { + missingPropertyError(ATT_KEY); + } else if (key.length() == 0) { + invalidPropertyValueError(ATT_KEY, key, null); + } else { + extension.setKey(key); + } + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent instanceof PDFDictionaryElement) { + if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); + } + } + } + + @Override + protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException { + if (capturePCData(extension.getType())) { + if (characters == null) { + characters = new StringBuffer((length < 16) ? 16 : length); + } + characters.append(data, start, length); + } + } + + private boolean capturePCData(PDFObjectType type) { + if (type == PDFObjectType.Array) { + return false; + } else if (type == PDFObjectType.Dictionary) { + return false; + } else { + return (type != PDFObjectType.Reference); + } + } + + @Override + public void endOfNode() throws FOPException { + if (capturePCData(extension.getType())) { + if (extension.getType() == PDFObjectType.Boolean) { + String value = (characters != null) ? characters.toString() : ""; + if (!value.equals("true") && !value.equals("false")) { + invalidPropertyValueError("", value, null); + } + extension.setValue(Boolean.valueOf(value)); + } else if (extension.getType() == PDFObjectType.Name) { + String value = (characters != null) ? characters.toString() : ""; + if (value.length() == 0) { + invalidPropertyValueError("", value, null); + } + extension.setValue(value); + } else if (extension.getType() == PDFObjectType.Number) { + String value = (characters != null) ? characters.toString() : ""; + try { + double d = Double.parseDouble(value); + if (Math.abs(Math.floor(d) - d) < 1E-10) { + extension.setValue(Long.valueOf((long) d)); + } else { + extension.setValue(Double.valueOf(d)); + } + } catch (NumberFormatException e) { + invalidPropertyValueError("", value, null); + } + } else if (extension.getType() == PDFObjectType.String) { + String value = (characters != null) ? characters.toString() : ""; + extension.setValue(value); + } + } + super.endOfNode(); + } + + @Override + public String getLocalName() { + return extension.getType().elementName(); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java similarity index 64% rename from src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java rename to src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java index 9de7e95da..d28f1602f 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFDictionaryElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java @@ -19,25 +19,25 @@ 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); +public class PDFCollectionEntryExtension extends PDFObjectExtension { + + public static final String PROPERTY_KEY = "key"; + + /* Non-empty key if used as dictionary entry, otherwise must be null. */ + private String key; + + PDFCollectionEntryExtension(PDFObjectType type) { + super(type); } -} + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java new file mode 100644 index 000000000..228d69c79 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public abstract class PDFCollectionExtension extends PDFCollectionEntryExtension { + + protected PDFCollectionExtension(PDFObjectType type) { + super(type); + } + + public abstract void addEntry(PDFCollectionEntryExtension entry); + + public abstract PDFCollectionEntryExtension getLastEntry(); + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java index 19e5ce07a..3832619ba 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java @@ -43,14 +43,13 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { } public void toSAX(ContentHandler handler) throws SAXException { - PDFDictionaryType dictionaryType = extension.getDictionaryType(); int pageNumber = 0; - if (dictionaryType == PDFDictionaryType.Page) { + if (extension instanceof PDFPageExtension) { if (handler instanceof GenerationHelperContentHandler) { Object context = ((GenerationHelperContentHandler) handler).getContentHandlerContext(); if (context instanceof IFContext) { int pageIndex = ((IFContext) context).getPageIndex(); - if ((pageIndex >= 0) && extension.matchesPageNumber(pageIndex + 1)) { + if ((pageIndex >= 0) && ((PDFPageExtension) extension).matchesPageNumber(pageIndex + 1)) { pageNumber = pageIndex + 1; } else { pageNumber = -1; @@ -69,24 +68,40 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { String qn = PREFIX + ":" + ln; attributes = extractIFAttributes(attributes, dictionary); handler.startElement(CATEGORY, ln, qn, attributes); - for (PDFDictionaryEntryExtension entry : dictionary.getEntries()) { + for (PDFCollectionEntryExtension entry : dictionary.getEntries()) { toSAX(handler, entry); } handler.endElement(CATEGORY, ln, qn); } - private void toSAX(ContentHandler handler, PDFDictionaryEntryExtension entry) throws SAXException { + private void toSAX(ContentHandler handler, PDFArrayExtension array) throws SAXException { + AttributesImpl attributes = new AttributesImpl(); + String ln = array.getElementName(); + String qn = PREFIX + ":" + ln; + attributes = extractIFAttributes(attributes, array); + handler.startElement(CATEGORY, ln, qn, attributes); + for (PDFCollectionEntryExtension entry : array.getEntries()) { + toSAX(handler, entry); + } + handler.endElement(CATEGORY, ln, qn); + } + + private void toSAX(ContentHandler handler, PDFCollectionEntryExtension entry) throws SAXException { if (entry instanceof PDFDictionaryExtension) { toSAX(handler, (PDFDictionaryExtension) entry); + } else if (entry instanceof PDFArrayExtension) { + toSAX(handler, (PDFArrayExtension) entry); } else { AttributesImpl attributes = new AttributesImpl(); String ln = entry.getElementName(); String qn = PREFIX + ":" + ln; attributes = extractIFAttributes(attributes, entry); handler.startElement(CATEGORY, ln, qn, attributes); - char[] characters = entry.getValueAsXMLEscapedString().toCharArray(); - if (characters.length > 0) { - handler.characters(characters, 0, characters.length); + if (!(entry instanceof PDFReferenceExtension)) { + char[] characters = entry.getValueAsXMLEscapedString().toCharArray(); + if (characters.length > 0) { + handler.characters(characters, 0, characters.length); + } } handler.endElement(CATEGORY, ln, qn); } @@ -94,16 +109,27 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryExtension dictionary) { PDFDictionaryType type = dictionary.getDictionaryType(); - if (type == PDFDictionaryType.Catalog) { - // no specific attriburtes + if (dictionary.usesIDAttribute()) { + String idName = PDFDictionaryElement.ATT_ID; + String id = dictionary.getProperty(PDFDictionaryExtension.PROPERTY_ID); + if (id != null) { + attributes.addAttribute(null, idName, idName, "ID", id); + } + } + if (type == PDFDictionaryType.Action) { + String actionTypeName = PDFActionElement.ATT_TYPE; + String actionType = dictionary.getProperty(PDFActionExtension.PROPERTY_TYPE); + if (actionType != null) { + attributes.addAttribute(null, actionTypeName, actionTypeName, "CDATA", actionType); + } } else if (type == PDFDictionaryType.Page) { - String pageNumbersName = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; + String pageNumbersName = PDFPageExtension.PROPERTY_PAGE_NUMBERS; String pageNumbers = dictionary.getProperty(pageNumbersName); if (pageNumbers != null) { attributes.addAttribute(null, pageNumbersName, pageNumbersName, "CDATA", pageNumbers); } } else if (type == PDFDictionaryType.Dictionary) { - String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY; + String keyName = PDFCollectionEntryElement.ATT_KEY; String key = dictionary.getKey(); if (key != null) { attributes.addAttribute(null, keyName, keyName, "CDATA", key); @@ -112,12 +138,28 @@ public class PDFDictionaryAttachment extends PDFExtensionAttachment { return attributes; } - private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryEntryExtension entry) { - String keyName = PDFDictionaryEntryExtension.PROPERTY_KEY; + private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFArrayExtension array) { + String keyName = PDFCollectionEntryExtension.PROPERTY_KEY; + String key = array.getKey(); + if (key != null) { + attributes.addAttribute(null, keyName, keyName, "CDATA", key); + } + return attributes; + } + + private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFCollectionEntryExtension entry) { + String keyName = PDFCollectionEntryElement.ATT_KEY; String key = entry.getKey(); if (key != null) { attributes.addAttribute(null, keyName, keyName, "CDATA", key); } + if (entry instanceof PDFReferenceExtension) { + String refid = ((PDFReferenceExtension) entry).getReferenceId(); + if (refid != null) { + String refidName = PDFReferenceElement.ATT_REFID; + attributes.addAttribute(null, refidName, refidName, "IDREF", refid); + } + } return attributes; } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java index 0920f3a78..9dc127da6 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java @@ -23,7 +23,6 @@ import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.extensions.ExtensionAttachment; @@ -34,38 +33,54 @@ import org.apache.fop.fo.extensions.ExtensionAttachment; * Extension element for dictionaries: pdf:{catalog,page,dictionary}. The specific type * of dictionary is established at construction type. */ -public class PDFDictionaryElement extends AbstractPDFDictionaryElement { +public class PDFDictionaryElement extends PDFCollectionEntryElement { - public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; - - private PDFDictionaryExtension extension; + public static final String ATT_ID = PDFDictionaryExtension.PROPERTY_ID; /** * Main constructor * @param parent parent FO node */ PDFDictionaryElement(FONode parent, PDFDictionaryType type) { - super(parent); - this.extension = new PDFDictionaryExtension(type); + super(parent, PDFObjectType.Dictionary, createExtension(type)); + } + + private static PDFDictionaryExtension createExtension(PDFDictionaryType type) { + if (type == PDFDictionaryType.Action) { + return new PDFActionExtension(); + } else if (type == PDFDictionaryType.Catalog) { + return new PDFCatalogExtension(); + } else if (type == PDFDictionaryType.Layer) { + return new PDFLayerExtension(); + } else if (type == PDFDictionaryType.Navigator) { + return new PDFNavigatorExtension(); + } else if (type == PDFDictionaryType.Page) { + return new PDFPageExtension(); + } else { + return new PDFDictionaryExtension(type); + } } - public PDFDictionaryExtension getExtension() { - return extension; + public PDFDictionaryExtension getDictionaryExtension() { + assert getExtension() instanceof PDFDictionaryExtension; + return (PDFDictionaryExtension) getExtension(); } @Override public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { - if (extension.getDictionaryType() == PDFDictionaryType.Catalog) { - // no specific properties - } else if (extension.getDictionaryType() == PDFDictionaryType.Page) { - String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS); - if (pageNumbers != null) { - extension.setProperty(ATT_PAGE_NUMBERS, pageNumbers); + PDFDictionaryExtension extension = getDictionaryExtension(); + if (extension.usesIDAttribute()) { + String id = attlist.getValue(ATT_ID); + if (id != null) { + extension.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); } - } else if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { + } + if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { String key = attlist.getValue(ATT_KEY); if (key == null) { - missingPropertyError(ATT_KEY); + if (parent instanceof PDFDictionaryElement) { + missingPropertyError(ATT_KEY); + } } else if (key.length() == 0) { invalidPropertyValueError(ATT_KEY, key, null); } else { @@ -78,16 +93,18 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { public void startOfNode() throws FOPException { super.startOfNode(); String localName = getLocalName(); - if (localName.equals("catalog")) { - if (parent.getNameId() != Constants.FO_DECLARATIONS) { - invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); - } + if (localName.equals("action")) { + // handled in PDFActionElement subclass + } else if (localName.equals("catalog")) { + // handled in PDFCatalogElement subclass + } else if (localName.equals("layer")) { + // handled in PDFLayerElement subclass + } else if (localName.equals("navigator")) { + // handled in PDFNavigattorElement subclass } else if (localName.equals("page")) { - if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { - invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM"); - } + // handled in PDFPageElement subclass } else if (localName.equals("dictionary")) { - if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { + if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName()) && !PDFObjectType.Array.elementName().equals(parent.getLocalName())) { invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); } } else { @@ -97,14 +114,15 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { @Override protected void addChildNode(FONode child) throws FOPException { + PDFDictionaryExtension extension = getDictionaryExtension(); if (child instanceof PDFDictionaryElement) { - PDFDictionaryExtension extension = ((PDFDictionaryElement) child).getExtension(); - if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) { - this.extension.addEntry(extension); + PDFDictionaryExtension entry = ((PDFDictionaryElement) child).getDictionaryExtension(); + if (entry.getDictionaryType() == PDFDictionaryType.Dictionary) { + extension.addEntry(entry); } - } else if (child instanceof PDFDictionaryEntryElement) { - PDFDictionaryEntryExtension extension = ((PDFDictionaryEntryElement) child).getExtension(); - this.extension.addEntry(extension); + } else if (child instanceof PDFCollectionEntryElement) { + PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension(); + extension.addEntry(entry); } } @@ -115,12 +133,13 @@ public class PDFDictionaryElement extends AbstractPDFDictionaryElement { @Override public String getLocalName() { + PDFDictionaryExtension extension = getDictionaryExtension(); return extension.getDictionaryType().elementName(); } @Override protected ExtensionAttachment instantiateExtensionAttachment() { - return new PDFDictionaryAttachment(extension); + return new PDFDictionaryAttachment(getDictionaryExtension()); } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java deleted file mode 100644 index bcdb90c2c..000000000 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryElement.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.render.pdf.extensions; - -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.FONode; -import org.apache.fop.fo.PropertyList; - -// CSOFF: LineLengthCheck - -/** - * Extension element for dictionary entries: pdf:{boolean,name,number,string}. The specific type - * of entry is established at construction type. - */ -public class PDFDictionaryEntryElement extends AbstractPDFDictionaryElement { - - private PDFDictionaryEntryExtension extension; - private StringBuffer characters; - - /** - * Main constructor - * @param parent parent FO node - */ - PDFDictionaryEntryElement(FONode parent, PDFDictionaryEntryType type) { - super(parent); - this.extension = new PDFDictionaryEntryExtension(type); - } - - public PDFDictionaryEntryExtension getExtension() { - return extension; - } - - @Override - public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { - String key = attlist.getValue("key"); - if (key == null) { - missingPropertyError("key"); - } else if (key.length() == 0) { - invalidPropertyValueError("key", key, null); - } else { - extension.setKey(key); - } - } - - @Override - public void startOfNode() throws FOPException { - super.startOfNode(); - if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) { - invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); - } - } - - @Override - protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException { - if (characters == null) { - characters = new StringBuffer((length < 16) ? 16 : length); - } - characters.append(data, start, length); - } - - @Override - public void endOfNode() throws FOPException { - String value = (characters != null) ? characters.toString() : ""; - if (extension.getType() == PDFDictionaryEntryType.Boolean) { - if (!value.equals("true") && !value.equals("false")) { - invalidPropertyValueError("", value, null); - } - } else if (extension.getType() == PDFDictionaryEntryType.Name) { - if (value.length() == 0) { - 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/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java index d4b11cdb4..50b6f3a83 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java @@ -24,24 +24,36 @@ import java.util.Map; // CSOFF: LineLengthCheck -public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { +public class PDFDictionaryExtension extends PDFCollectionExtension { + public static final String PROPERTY_ID = "id"; public static final String PROPERTY_PAGE_NUMBERS = "page-numbers"; private static final long serialVersionUID = -1L; private PDFDictionaryType dictionaryType; private Map properties; - private List entries; + private List entries; PDFDictionaryExtension() { + this(PDFDictionaryType.Dictionary); } PDFDictionaryExtension(PDFDictionaryType dictionaryType) { - super(PDFDictionaryEntryType.Dictionary); + super(PDFObjectType.Dictionary); this.dictionaryType = dictionaryType; this.properties = new java.util.HashMap(); - this.entries = new java.util.ArrayList(); + this.entries = new java.util.ArrayList(); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue() { + return getEntries(); } public PDFDictionaryType getDictionaryType() { @@ -56,15 +68,40 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { return properties.get(name); } - public void addEntry(PDFDictionaryEntryExtension entry) { - entries.add(entry); + @Override + public void addEntry(PDFCollectionEntryExtension entry) { + if ((entry.getKey() == null) || (entry.getKey().length() == 0)) { + throw new IllegalArgumentException(); + } else { + entries.add(entry); + } } - public List getEntries() { + public List getEntries() { return entries; } - public PDFDictionaryEntryExtension getLastEntry() { + public PDFCollectionEntryExtension findEntry(String key) { + for (PDFCollectionEntryExtension entry : entries) { + String entryKey = entry.getKey(); + if ((entryKey != null) && entryKey.equals(key)) { + return entry; + } + } + return null; + } + + public Object findEntryValue(String key) { + for (PDFCollectionEntryExtension entry : entries) { + String entryKey = entry.getKey(); + if ((entryKey != null) && entryKey.equals(key)) { + return entry.getValue(); + } + } + return null; + } + + public PDFCollectionEntryExtension getLastEntry() { if (entries.size() > 0) { return entries.get(entries.size() - 1); } else { @@ -72,48 +109,8 @@ public class PDFDictionaryExtension extends PDFDictionaryEntryExtension { } } - /** - * Determine if page dictionary and page number matches. - * @param pageNumber page number, where first page number is 1 - * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property - */ - public boolean matchesPageNumber(int pageNumber) { - if (dictionaryType != PDFDictionaryType.Page) { - return false; - } - String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS); - if ((pageNumbers == null) || (pageNumbers.length() == 0)) { - return false; - } else if (pageNumbers.equals("*")) { - return true; - } else { - for (String interval : pageNumbers.split("\\s*,\\s*")) { - String[] components = interval.split("\\s*-\\s*"); - if (components.length < 1) { - continue; - } else { - try { - int start = Integer.parseInt(components[0]); - int end = 0; - if (components.length > 1) { - if (!components[1].equals("LAST")) { - end = Integer.parseInt(components[1]); - } - } - if ((end == 0) && (pageNumber == start)) { - return true; - } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) { - return true; - } else { - continue; - } - } catch (NumberFormatException e) { - continue; - } - } - } - } - return false; + public boolean usesIDAttribute() { + return dictionaryType.usesIDAttribute(); } @Override diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java index edd95160a..a49a5fc8c 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java @@ -25,17 +25,28 @@ package org.apache.fop.render.pdf.extensions; * Enumeration type for PDF dictionary extension elements. */ public enum PDFDictionaryType { - Dictionary("dictionary"), // generic (nested) dictionary element - Catalog("catalog"), // catalog dictionary element - Page("page"); // page dictionary element + Action("action", true), // action dictionary element + Catalog("catalog"), // catalog dictionary element + Dictionary("dictionary"), // generic (nested) dictionary element + Layer("layer", true), // optional content group dictionary element + Navigator("navigator", true), // navigation node dictionary element + Page("page"); // page dictionary element private String elementName; - PDFDictionaryType(String elementName) { + private boolean usesIDAttribute; + PDFDictionaryType(String elementName, boolean usesIDAttribute) { this.elementName = elementName; + this.usesIDAttribute = usesIDAttribute; + } + PDFDictionaryType(String elementName) { + this(elementName, false); } public String elementName() { return elementName; } + public boolean usesIDAttribute() { + return usesIDAttribute; + } static PDFDictionaryType valueOfElementName(String elementName) { for (PDFDictionaryType type : values()) { if (type.elementName.equals(elementName)) { 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 3e063e24b..1fba80796 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java @@ -41,17 +41,56 @@ public class PDFElementMapping extends ElementMapping { protected void initialize() { if (foObjs == null) { foObjs = new java.util.HashMap(); + // pdf:action + foObjs.put(PDFDictionaryType.Action.elementName(), new PDFActionElementMaker()); + // pdf:array + foObjs.put(PDFObjectType.Array.elementName(), new PDFArrayElementMaker()); + // pdf:boolean + foObjs.put(PDFObjectType.Boolean.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Boolean)); + // pdf:catalog + foObjs.put(PDFDictionaryType.Catalog.elementName(), new PDFCatalogElementMaker()); + // pdf:dictionary + foObjs.put(PDFDictionaryType.Dictionary.elementName(), new PDFDictionaryElementMaker()); // pdf:embedded-file foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileElementMaker()); - // pdf:{catalog,page} et al. - for (PDFDictionaryType type : PDFDictionaryType.values()) { - foObjs.put(type.elementName(), new PDFDictionaryElementMaker(type)); - } - for (PDFDictionaryEntryType type : PDFDictionaryEntryType.values()) { - if (type != PDFDictionaryEntryType.Dictionary) { - foObjs.put(type.elementName(), new PDFDictionaryEntryElementMaker(type)); - } - } + // pdf:name + foObjs.put(PDFObjectType.Name.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Name)); + // pdf:number + foObjs.put(PDFObjectType.Number.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Number)); + // pdf:navigator + foObjs.put(PDFDictionaryType.Navigator.elementName(), new PDFNavigatorElementMaker()); + // pdf:layer + foObjs.put(PDFDictionaryType.Layer.elementName(), new PDFLayerElementMaker()); + // pdf:page + foObjs.put(PDFDictionaryType.Page.elementName(), new PDFPageElementMaker()); + // pdf:reference + foObjs.put(PDFObjectType.Reference.elementName(), new PDFReferenceElementMaker()); + // pdf:string + foObjs.put(PDFObjectType.String.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.String)); + } + } + + static class PDFActionElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFActionElement(parent); + } + } + + static class PDFArrayElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFArrayElement(parent); + } + } + + static class PDFCatalogElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFCatalogElement(parent); + } + } + + static class PDFDictionaryElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFDictionaryElement(parent, PDFDictionaryType.Dictionary); } } @@ -61,23 +100,38 @@ public class PDFElementMapping extends ElementMapping { } } - static class PDFDictionaryElementMaker extends ElementMapping.Maker { - private PDFDictionaryType dictionaryType; - PDFDictionaryElementMaker(PDFDictionaryType dictionaryType) { - this.dictionaryType = dictionaryType; + static class PDFLayerElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFLayerElement(parent); } + } + + static class PDFNavigatorElementMaker extends ElementMapping.Maker { public FONode make(FONode parent) { - return new PDFDictionaryElement(parent, dictionaryType); + return new PDFNavigatorElement(parent); } } - static class PDFDictionaryEntryElementMaker extends ElementMapping.Maker { - private PDFDictionaryEntryType entryType; - PDFDictionaryEntryElementMaker(PDFDictionaryEntryType entryType) { + static class PDFPageElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFPageElement(parent); + } + } + + static class PDFCollectionEntryElementMaker extends ElementMapping.Maker { + private PDFObjectType entryType; + PDFCollectionEntryElementMaker(PDFObjectType entryType) { this.entryType = entryType; } public FONode make(FONode parent) { - return new PDFDictionaryEntryElement(parent, entryType); + return new PDFCollectionEntryElement(parent, entryType); } } + + static class PDFReferenceElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFReferenceElement(parent); + } + } + } 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 f14f1e7d6..2fd14058e 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java @@ -49,7 +49,7 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle private Attributes lastAttributes; // PDFDictionaryAttachment related - private Stack dictionaries = new Stack(); + private Stack collections = new Stack(); private boolean captureContent; private StringBuffer characters; @@ -58,29 +58,78 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle if (PDFExtensionAttachment.CATEGORY.equals(uri)) { if (localName.equals(PDFEmbeddedFileAttachment.ELEMENT)) { lastAttributes = new AttributesImpl(attributes); - } else if (PDFDictionaryType.hasValueOfElementName(localName)) { - PDFDictionaryExtension dictionary = new PDFDictionaryExtension(PDFDictionaryType.valueOfElementName(localName)); - String key = attributes.getValue(PDFDictionaryEntryExtension.PROPERTY_KEY); + } else if (PDFDictionaryType.Action.elementName().equals(localName)) { + PDFActionExtension action = new PDFActionExtension(); + String id = attributes.getValue(PDFDictionaryElement.ATT_ID); + if (id != null) { + action.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); + } + String type = attributes.getValue(PDFActionElement.ATT_TYPE); + if (type != null) { + action.setProperty(PDFActionExtension.PROPERTY_TYPE, type); + } + collections.push(action); + } else if (PDFObjectType.Array.elementName().equals(localName)) { + PDFArrayExtension array = new PDFArrayExtension(); + String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); + if (key != null) { + array.setKey(key); + } + collections.push(array); + } else if (PDFDictionaryType.Catalog.elementName().equals(localName)) { + PDFCatalogExtension catalog = new PDFCatalogExtension(); + collections.push(catalog); + } else if (PDFDictionaryType.Dictionary.elementName().equals(localName)) { + PDFDictionaryExtension dictionary = new PDFDictionaryExtension(); + String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); if (key != null) { dictionary.setKey(key); } - if (dictionary.getDictionaryType() == PDFDictionaryType.Page) { - String pageNumbers = attributes.getValue(PDFDictionaryElement.ATT_PAGE_NUMBERS); - if (pageNumbers != null) { - dictionary.setProperty(PDFDictionaryElement.ATT_PAGE_NUMBERS, pageNumbers); - } + collections.push(dictionary); + } else if (PDFDictionaryType.Layer.elementName().equals(localName)) { + PDFLayerExtension layer = new PDFLayerExtension(); + String id = attributes.getValue(PDFDictionaryElement.ATT_ID); + if (id != null) { + layer.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); + } + collections.push(layer); + } else if (PDFDictionaryType.Navigator.elementName().equals(localName)) { + PDFNavigatorExtension navigator = new PDFNavigatorExtension(); + String id = attributes.getValue(PDFDictionaryElement.ATT_ID); + if (id != null) { + navigator.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); + } + collections.push(navigator); + } else if (PDFDictionaryType.Page.elementName().equals(localName)) { + PDFPageExtension page = new PDFPageExtension(); + String pageNumbers = attributes.getValue(PDFPageElement.ATT_PAGE_NUMBERS); + if (pageNumbers != null) { + page.setProperty(PDFPageExtension.PROPERTY_PAGE_NUMBERS, pageNumbers); + } + collections.push(page); + } else if (PDFObjectType.hasValueOfElementName(localName)) { + PDFCollectionEntryExtension entry; + if (PDFObjectType.Reference.elementName().equals(localName)) { + entry = new PDFReferenceExtension(); + } else { + entry = new PDFCollectionEntryExtension(PDFObjectType.valueOfElementName(localName)); } - dictionaries.push(dictionary); - } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) { - PDFDictionaryEntryExtension entry = new PDFDictionaryEntryExtension(PDFDictionaryEntryType.valueOfElementName(localName)); - String key = attributes.getValue(PDFDictionaryEntryElement.ATT_KEY); + String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); if (key != null) { entry.setKey(key); } - if (!dictionaries.empty()) { - PDFDictionaryExtension dictionary = dictionaries.peek(); - dictionary.addEntry(entry); - captureContent = true; + if (entry instanceof PDFReferenceExtension) { + String refid = attributes.getValue(PDFReferenceElement.ATT_REFID); + if (refid != null) { + ((PDFReferenceExtension) entry).setReferenceId(refid); + } + } + if (!collections.empty()) { + PDFCollectionExtension collection = collections.peek(); + collection.addEntry(entry); + if (!(entry instanceof PDFReferenceExtension)) { + captureContent = true; + } } } else { throw new SAXException("Unhandled element " + localName + " in namespace: " + uri); @@ -107,33 +156,48 @@ public class PDFExtensionHandler extends DefaultHandler implements ContentHandle String name = lastAttributes.getValue("name"); String src = lastAttributes.getValue("src"); String desc = lastAttributes.getValue("description"); + this.lastAttributes = null; this.returnedObject = new PDFEmbeddedFileAttachment(name, src, desc); } else if (PDFDictionaryType.hasValueOfElementName(localName)) { - if (!dictionaries.empty()) { - PDFDictionaryExtension dictionary = dictionaries.pop(); - if ((dictionary.getDictionaryType() == PDFDictionaryType.Catalog) || (dictionary.getDictionaryType() == PDFDictionaryType.Page)) { + if (!collections.empty() && (collections.peek() instanceof PDFDictionaryExtension)) { + PDFDictionaryExtension dictionary = (PDFDictionaryExtension) collections.pop(); + if (!collections.empty()) { + PDFCollectionExtension collectionOuter = collections.peek(); + collectionOuter.addEntry(dictionary); + } else if (dictionary.getDictionaryType() != PDFDictionaryType.Dictionary) { this.returnedObject = new PDFDictionaryAttachment(dictionary); - } else if (!dictionaries.empty()) { - PDFDictionaryExtension dictionaryOuter = dictionaries.peek(); - dictionaryOuter.addEntry(dictionary); + } else { + throw new SAXException(new IllegalStateException("generic dictionary not permitted at outer level")); + } + } else { + throw new SAXException(new IllegalStateException("collections stack is empty or not a dictionary")); + } + } else if (PDFObjectType.Array.elementName().equals(localName)) { + if (!collections.empty() && (collections.peek() instanceof PDFArrayExtension)) { + PDFArrayExtension array = (PDFArrayExtension) collections.pop(); + if (!collections.empty()) { + PDFCollectionExtension collectionOuter = collections.peek(); + collectionOuter.addEntry(array); + } else { + throw new SAXException(new IllegalStateException("array not permitted at outer level")); } } else { - throw new SAXException(new IllegalStateException("no active dictionary")); + throw new SAXException(new IllegalStateException("collections stack is empty or not an array")); } - } else if (PDFDictionaryEntryType.hasValueOfElementName(localName)) { - if (!dictionaries.empty()) { - PDFDictionaryExtension dictionary = dictionaries.peek(); - PDFDictionaryEntryExtension entry = dictionary.getLastEntry(); + } else if (PDFObjectType.hasValueOfElementName(localName)) { + if (!collections.empty()) { + PDFCollectionExtension collection = collections.peek(); + PDFCollectionEntryExtension entry = collection.getLastEntry(); if (entry != null) { if (characters != null) { entry.setValue(characters.toString()); characters = null; } } else { - throw new SAXException(new IllegalStateException("no active entry")); + throw new SAXException(new IllegalStateException("no current entry")); } } else { - throw new SAXException(new IllegalStateException("no active dictionary")); + throw new SAXException(new IllegalStateException("entry not permitted at outer level")); } } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java new file mode 100644 index 000000000..dab0ecf78 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:layer. + */ +public class PDFLayerElement extends PDFDictionaryElement { + + PDFLayerElement(FONode parent) { + super(parent, PDFDictionaryType.Layer); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java new file mode 100644 index 000000000..d6cc27ab8 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFLayerExtension extends PDFDictionaryExtension { + + PDFLayerExtension() { + super(PDFDictionaryType.Layer); + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java new file mode 100644 index 000000000..5c5b779b5 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:navigator. + */ +public class PDFNavigatorElement extends PDFDictionaryElement { + + PDFNavigatorElement(FONode parent) { + super(parent, PDFDictionaryType.Navigator); + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_DECLARATIONS) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java new file mode 100644 index 000000000..ee9dfa7c8 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFNavigatorExtension extends PDFDictionaryExtension { + + PDFNavigatorExtension() { + super(PDFDictionaryType.Navigator); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java similarity index 74% rename from src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java rename to src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java index 94d6b5dbf..5447d87f2 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryExtension.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java @@ -23,34 +23,20 @@ import org.apache.fop.util.XMLUtil; // CSOFF: LineLengthCheck -public class PDFDictionaryEntryExtension { +public class PDFObjectExtension { - public static final String PROPERTY_KEY = "key"; - - private PDFDictionaryEntryType type; - private String key = ""; + private PDFObjectType type; private Object value; - PDFDictionaryEntryExtension() { - } - - PDFDictionaryEntryExtension(PDFDictionaryEntryType type) { + PDFObjectExtension(PDFObjectType type) { this.type = type; } - public PDFDictionaryEntryType getType() { + public PDFObjectType getType() { return type; } - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public void setValue(String value) { + public void setValue(Object value) { this.value = value; } @@ -63,7 +49,10 @@ public class PDFDictionaryEntryExtension { * @return entry value */ public Boolean getValueAsBoolean() { - if (value instanceof String) { + Object value = getValue(); + if (value instanceof Boolean) { + return (Boolean) value; + } else if (value instanceof String) { return Boolean.valueOf((String)value); } else { return false; @@ -75,9 +64,12 @@ public class PDFDictionaryEntryExtension { * @return entry value */ public Number getValueAsNumber() { - if (value instanceof String) { + Object value = getValue(); + if (value instanceof Number) { + return (Number) value; + } else if (value instanceof String) { double d = Double.parseDouble((String) value); - if (Math.floor(d) == d) { + if (Math.abs(Math.floor(d) - d) < 1E-10) { return Long.valueOf((long) d); } else { return Double.valueOf(d); @@ -88,10 +80,13 @@ public class PDFDictionaryEntryExtension { } public String getValueAsString() { - if (value instanceof String) { + Object value = getValue(); + if (value == null) { + return null; + } else if (value instanceof String) { return (String) value; } else { - return ""; + return value.toString(); } } @@ -99,10 +94,6 @@ public class PDFDictionaryEntryExtension { 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/PDFObjectType.java similarity index 62% rename from src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java rename to src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java index e4b25819a..c193a3b7b 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryEntryType.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java @@ -22,24 +22,27 @@ package org.apache.fop.render.pdf.extensions; // CSOFF: LineLengthCheck /** - * Enumeration type for leaf PDF dictionary entry extension elements. + * Enumeration type for leaf PDF object extension types used as singletons, + * dictionary entries, or array entries. */ -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 +public enum PDFObjectType { + Array("array"), // array valued entry + Boolean("boolean"), // boolean valued entry + Dictionary("dictionary"), // dictionary valued entry + Name("name"), // name valued entry + Number("number"), // number valued entry + Reference("reference"), // indirect object reference entry + String("string"); // string valued entry private String elementName; - PDFDictionaryEntryType(String elementName) { + PDFObjectType(String elementName) { this.elementName = elementName; } public String elementName() { return elementName; } - static PDFDictionaryEntryType valueOfElementName(String elementName) { - for (PDFDictionaryEntryType type : values()) { + static PDFObjectType valueOfElementName(String elementName) { + for (PDFObjectType type : values()) { if (type.elementName.equals(elementName)) { return type; } @@ -48,7 +51,8 @@ public enum PDFDictionaryEntryType { } static boolean hasValueOfElementName(String elementName) { try { - return valueOfElementName(elementName) != null; + valueOfElementName(elementName); + return true; } catch (Exception e) { return false; } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java new file mode 100644 index 000000000..28a966ead --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:page. + */ +public class PDFPageElement extends PDFDictionaryElement { + + public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFPageElement(FONode parent) { + super(parent, PDFDictionaryType.Page); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS); + if (pageNumbers != null) { + getDictionaryExtension().setProperty(PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS, pageNumbers); + } + } + + @Override + public void startOfNode() throws FOPException { + super.startOfNode(); + if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) { + invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM"); + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java new file mode 100644 index 000000000..0726a4e55 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFPageExtension extends PDFDictionaryExtension { + + public static final String PROPERTY_PAGE_NUMBERS = "page-numbers"; + + PDFPageExtension() { + super(PDFDictionaryType.Page); + } + + /** + * Determine if page dictionary and page number matches. + * @param pageNumber page number, where first page number is 1 + * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property + */ + public boolean matchesPageNumber(int pageNumber) { + String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS); + if ((pageNumbers == null) || (pageNumbers.length() == 0)) { + return false; + } else if (pageNumbers.equals("*")) { + return true; + } else { + for (String interval : pageNumbers.split("\\s*,\\s*")) { + String[] components = interval.split("\\s*-\\s*"); + if (components.length < 1) { + continue; + } else { + try { + int start = Integer.parseInt(components[0]); + int end = 0; + if (components.length > 1) { + if (!components[1].equals("LAST")) { + end = Integer.parseInt(components[1]); + } + } + if ((end == 0) && (pageNumber == start)) { + return true; + } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) { + return true; + } else { + continue; + } + } catch (NumberFormatException e) { + continue; + } + } + } + } + return false; + } + +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java new file mode 100644 index 000000000..37aeeb890 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; + +// CSOFF: LineLengthCheck + +/** + * Extension element for pdf:reference. + */ +public class PDFReferenceElement extends PDFCollectionEntryElement { + + public static final String ATT_REFID = PDFReferenceExtension.PROPERTY_REFID; + + /** + * Main constructor + * @param parent parent FO node + */ + PDFReferenceElement(FONode parent) { + super(parent, PDFObjectType.Reference); + } + + @Override + public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException { + super.processNode(elementName, locator, attlist, propertyList); + String refid = attlist.getValue(ATT_REFID); + if (refid == null) { + missingPropertyError(ATT_REFID); + } else if (refid.length() == 0) { + invalidPropertyValueError(ATT_REFID, refid, null); + } else { + ((PDFReferenceExtension) getExtension()).setReferenceId(refid); + } + } +} diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java new file mode 100644 index 000000000..09621c01c --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.pdf.extensions; + +// CSOFF: LineLengthCheck + +public class PDFReferenceExtension extends PDFCollectionEntryExtension { + + public static final String PROPERTY_REFID = "refid"; + + private String refid; + private Object resolvedReference; + + PDFReferenceExtension() { + super(PDFObjectType.Reference); + } + + @Override + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue() { + return this; + } + + public String getReferenceId() { + return refid; + } + + public void setReferenceId(String refid) { + this.refid = refid; + } + + public Object getResolvedReference() { + return resolvedReference; + } + + public void setResolvedReference(Object resolvedReference) { + this.resolvedReference = resolvedReference; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java index 3d1887f2d..547662c47 100644 --- a/src/java/org/apache/fop/render/ps/PSPainter.java +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -116,7 +116,7 @@ public class PSPainter extends AbstractIFPainter { } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { try { PSGenerator generator = getGenerator(); saveGraphicsState(); diff --git a/src/java/org/apache/fop/render/txt/TXTRenderer.java b/src/java/org/apache/fop/render/txt/TXTRenderer.java index bf4a46e19..5b398711f 100644 --- a/src/java/org/apache/fop/render/txt/TXTRenderer.java +++ b/src/java/org/apache/fop/render/txt/TXTRenderer.java @@ -582,6 +582,14 @@ public class TXTRenderer extends AbstractPathOrientedRenderer { currentState.pop(); } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + } + + /** {@inheritDoc} */ + protected void endLayer() { + } + /** {@inheritDoc} */ protected void concatenateTransformationMatrix(AffineTransform at) { currentState.push(new CTM(UnitConv.ptToMpt(at))); diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index 2212da434..68e2e3c62 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -545,6 +545,16 @@ public class XMLRenderer extends AbstractXMLRenderer { //only necessary for graphical output } + /** {@inheritDoc} */ + protected void startLayer(String layer) { + //only necessary for graphical output + } + + /** {@inheritDoc} */ + protected void endLayer() { + //only necessary for graphical output + } + /** * {@inheritDoc} * org.apache.fop.area.inline.InlineArea) diff --git a/src/java/org/apache/fop/util/AbstractPaintingState.java b/src/java/org/apache/fop/util/AbstractPaintingState.java index 96c3633e6..3e966d202 100644 --- a/src/java/org/apache/fop/util/AbstractPaintingState.java +++ b/src/java/org/apache/fop/util/AbstractPaintingState.java @@ -24,7 +24,6 @@ import java.awt.geom.AffineTransform; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.Stack; @@ -36,10 +35,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { private static final long serialVersionUID = 5998356138437094188L; /** current state data */ - private AbstractData data = null; + private AbstractData data; /** the state stack */ - private StateStack/**/ stateStack = new StateStack/**/(); + private StateStack stateStack = new StateStack(); /** * Instantiates a new state data object @@ -216,8 +215,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { */ public AffineTransform getTransform() { AffineTransform at = new AffineTransform(); - for (Iterator iter = stateStack.iterator(); iter.hasNext();) { - AbstractData data = (AbstractData)iter.next(); + for (AbstractData data : stateStack) { AffineTransform stackTrans = data.getTransform(); at.concatenate(stackTrans); } @@ -249,7 +247,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { if (stateStack.isEmpty()) { return null; } else { - AbstractData baseData = (AbstractData)stateStack.get(0); + AbstractData baseData = stateStack.get(0); return (AffineTransform) baseData.getTransform().clone(); } } @@ -297,7 +295,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { */ public AbstractData restore() { if (!stateStack.isEmpty()) { - setData((AbstractData)stateStack.pop()); + setData(stateStack.pop()); return this.data; } else { return null; @@ -310,12 +308,11 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * * @param dataList a state data list */ - public void saveAll(List/**/ dataList) { - Iterator it = dataList.iterator(); - while (it.hasNext()) { + public void saveAll(List dataList) { + for (AbstractData data : dataList) { // save current data on stack save(); - setData((AbstractData)it.next()); + setData(data); } } @@ -325,8 +322,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * * @return a list of state data popped from the stack */ - public List/**/ restoreAll() { - List/**/ dataList = new java.util.ArrayList/**/(); + public List restoreAll() { + List dataList = new java.util.ArrayList(); AbstractData data; while (true) { data = getData(); @@ -361,7 +358,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * * @return the state stack */ - protected Stack/**/ getStateStack() { + protected Stack getStateStack() { return this.stateStack; } @@ -369,8 +366,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { @Override public Object clone() { AbstractPaintingState state = instantiate(); - state.stateStack = new StateStack(this.stateStack); - state.data = (AbstractData)this.data.clone(); + state.stateStack = new StateStack(this.stateStack); + if (this.data != null) { + state.data = (AbstractData)this.data.clone(); + } return state; } @@ -385,7 +384,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { /** * A stack implementation which holds state objects */ - public class StateStack extends java.util.Stack { + public class StateStack extends java.util.Stack { private static final long serialVersionUID = 4897178211223823041L; @@ -393,7 +392,6 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { * Default constructor */ public StateStack() { - super(); } /** @@ -419,25 +417,28 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { private static final long serialVersionUID = 5208418041189828624L; /** The current color */ - protected Color color = null; + protected Color color; /** The current background color */ - protected Color backColor = null; + protected Color backColor; /** The current font name */ - protected String fontName = null; + protected String fontName; /** The current font size */ - protected int fontSize = 0; + protected int fontSize; /** The current line width */ - protected float lineWidth = 0; + protected float lineWidth; /** The dash array for the current basic stroke (line type) */ - protected float[] dashArray = null; + protected float[] dashArray; /** The current transform */ - protected AffineTransform transform = null; + protected AffineTransform transform; + + /** The current (optional content group) layer. */ + protected String layer; /** * Returns a newly create data object @@ -485,6 +486,18 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { transform = new AffineTransform(); } + public void setLayer(String layer) { + if (layer != null) { + this.layer = layer; + } else { + throw new IllegalArgumentException(); + } + } + + public String getLayer() { + return this.layer; + } + /** * Returns the derived rotation from the current transform * @@ -523,6 +536,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { this.transform = new AffineTransform(); } data.transform = new AffineTransform(this.transform); + data.layer = this.layer; return data; } @@ -535,7 +549,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable { + ", fontSize=" + fontSize + ", lineWidth=" + lineWidth + ", dashArray=" + dashArray - + ", transform=" + transform; + + ", transform=" + transform + + ", layer=" + layer; } } } diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java index ebd143a69..fb7ce2677 100644 --- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java +++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java @@ -146,16 +146,16 @@ public class SVGPainter extends AbstractIFPainter } /** {@inheritDoc} */ - public void startGroup(AffineTransform[] transforms) throws IFException { - startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms)); + public void startGroup(AffineTransform[] transforms, String layer) throws IFException { + startGroup(SVGUtil.formatAffineTransformsMptToPt(transforms), layer); } /** {@inheritDoc} */ - public void startGroup(AffineTransform transform) throws IFException { - startGroup(SVGUtil.formatAffineTransformMptToPt(transform)); + public void startGroup(AffineTransform transform, String layer) throws IFException { + startGroup(SVGUtil.formatAffineTransformMptToPt(transform), layer); } - private void startGroup(String transform) throws IFException { + private void startGroup(String transform, String layer) throws IFException { try { AttributesImpl atts = new AttributesImpl(); if (transform != null && transform.length() > 0) { diff --git a/status.xml b/status.xml index 352f487c3..91b9c78d3 100644 --- a/status.xml +++ b/status.xml @@ -58,7 +58,10 @@ users, i.e. when the behaviour changes and could affect the layout of existing documents. Example: the fix of marks layering will be such a case when it's done. --> - + + + Enable support for PDF sub-page transitions. + Fix misplaced table cell border in WM RTL context. diff --git a/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java b/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java index d156b908c..ee3498a03 100644 --- a/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java +++ b/test/java/org/apache/fop/fonts/EmbedFontInfoTestCase.java @@ -35,6 +35,8 @@ import static org.junit.Assert.assertTrue; */ public class EmbedFontInfoTestCase { + public EmbedFontInfoTestCase() {} + private EmbedFontInfo sut; private final URI metricsURI = URI.create("test/resources/fonts/ttf/glb12.ttf.xml"); diff --git a/test/java/org/apache/fop/fonts/FontsTestSuite.java b/test/java/org/apache/fop/fonts/FontsTestSuite.java index ad48c46c4..1baba49df 100644 --- a/test/java/org/apache/fop/fonts/FontsTestSuite.java +++ b/test/java/org/apache/fop/fonts/FontsTestSuite.java @@ -29,7 +29,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ FontManagerConfiguratorTestCase.class, - EmbedFontInfo.class, + EmbedFontInfoTestCase.class, FontEventProcessingTestCase.class, FontManagerConfiguratorTestCase.class }) diff --git a/test/java/org/apache/fop/pdf/PDFDestsTestCase.java b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java index 08d841ede..49c1e6dab 100644 --- a/test/java/org/apache/fop/pdf/PDFDestsTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java @@ -32,7 +32,7 @@ import java.util.List; public class PDFDestsTestCase extends PDFObjectTestCase { private PDFDests dests = new PDFDests(); - private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>\n"; + private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>"; @Before public void setUp() { diff --git a/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java index 3f84fac2e..00224e93e 100644 --- a/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java @@ -47,7 +47,7 @@ public class PDFDictionaryTestCase extends PDFObjectTestCase { + " /array [1 (two) 20]\n" + " /number 20\n" + " /null null\n" - + ">>\n"; + + ">>"; @Before public void setUp() { diff --git a/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java b/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java index e982bf246..a8be7ee74 100644 --- a/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFPageLabelsTestCase.java @@ -37,11 +37,11 @@ public class PDFPageLabelsTestCase { int index = 0; StringBuilder expected = new StringBuilder(); expected.append("["); - expected.append(index + " << /S /r >>\n"); + expected.append(index + " << /S /r >>"); pageLabels.addPageLabel(index++, "i"); pageLabels.addPageLabel(index++, "ii"); pageLabels.addPageLabel(index++, "iii"); - expected.append(" " + index + " << /S /D >>\n"); + expected.append(" " + index + " << /S /D >>"); pageLabels.addPageLabel(index++, "1"); pageLabels.addPageLabel(index++, "2"); pageLabels.addPageLabel(index++, "3"); @@ -52,33 +52,33 @@ public class PDFPageLabelsTestCase { pageLabels.addPageLabel(index++, "8"); pageLabels.addPageLabel(index++, "9"); pageLabels.addPageLabel(index++, "10"); - expected.append(" " + index + " << /S /A >>\n"); + expected.append(" " + index + " << /S /A >>"); pageLabels.addPageLabel(index++, "A"); pageLabels.addPageLabel(index++, "B"); - expected.append(" " + index + " << /S /R /St 100 >>\n"); + expected.append(" " + index + " << /S /R /St 100 >>"); pageLabels.addPageLabel(index++, "C"); - expected.append(" " + index + " << /S /R /St 500 >>\n"); + expected.append(" " + index + " << /S /R /St 500 >>"); pageLabels.addPageLabel(index++, "D"); - expected.append(" " + index + " << /S /A /St 5 >>\n"); + expected.append(" " + index + " << /S /A /St 5 >>"); pageLabels.addPageLabel(index++, "E"); pageLabels.addPageLabel(index++, "F"); pageLabels.addPageLabel(index++, "G"); - expected.append(" " + index + " << /P (aa) >>\n"); + expected.append(" " + index + " << /P (aa) >>"); pageLabels.addPageLabel(index++, "aa"); - expected.append(" " + index + " << /P (ab) >>\n"); + expected.append(" " + index + " << /P (ab) >>"); pageLabels.addPageLabel(index++, "ab"); - expected.append(" " + index + " << /P (ac) >>\n"); + expected.append(" " + index + " << /P (ac) >>"); pageLabels.addPageLabel(index++, "ac"); - expected.append(" " + index + " << /S /a >>\n"); + expected.append(" " + index + " << /S /a >>"); pageLabels.addPageLabel(index++, "a"); pageLabels.addPageLabel(index++, "b"); - expected.append(" " + index + " << /S /R /St 2 >>\n"); + expected.append(" " + index + " << /S /R /St 2 >>"); pageLabels.addPageLabel(index++, "II"); - expected.append(" " + index + " << /S /R /St 12 >>\n"); + expected.append(" " + index + " << /S /R /St 12 >>"); pageLabels.addPageLabel(index++, "XII"); - expected.append(" " + index + " <<\n /P (00)\n /S /D\n /St 9\n>>\n"); + expected.append(" " + index + " <<\n /P (00)\n /S /D\n /St 9\n>>"); pageLabels.addPageLabel(index++, "009"); - expected.append(" " + index + " <<\n /P (0)\n /S /D\n /St 10\n>>\n"); + expected.append(" " + index + " <<\n /P (0)\n /S /D\n /St 10\n>>"); pageLabels.addPageLabel(index++, "010"); pageLabels.addPageLabel(index++, "011"); expected.append("]"); diff --git a/test/java/org/apache/fop/pdf/PDFStreamTestCase.java b/test/java/org/apache/fop/pdf/PDFStreamTestCase.java index 93dcea511..20e38a600 100644 --- a/test/java/org/apache/fop/pdf/PDFStreamTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFStreamTestCase.java @@ -118,7 +118,7 @@ public class PDFStreamTestCase { private byte[] createSampleStreamData() throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); - stream.write("stream\n".getBytes("US-ASCII")); + stream.write("\nstream\n".getBytes("US-ASCII")); stream.write(createSampleData()); stream.write("\nendstream".getBytes("US-ASCII")); return stream.toByteArray(); diff --git a/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java b/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java index ceff96a91..12f6e3c1b 100644 --- a/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java +++ b/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java @@ -73,7 +73,7 @@ public class CrossReferenceTableTestCase extends CrossReferenceObjectTest { .append(" /Info 2 0 R\n") .append(" /ID [<0123456789ABCDEF> <0123456789ABCDEF>]\n") .append(" /Size ").append(Integer.toString(offsets.size() + 1)).append('\n') - .append(">>\n"); + .append(">>"); return getBytes(expected); } diff --git a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java index 592335648..be8b5d718 100644 --- a/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java +++ b/test/java/org/apache/fop/render/intermediate/AbstractIFPainterTestCase.java @@ -51,7 +51,7 @@ public class AbstractIFPainterTestCase { public void endViewport() throws IFException { } - public void startGroup(AffineTransform transform) throws IFException { + public void startGroup(AffineTransform transform, String layer) throws IFException { } public void endGroup() throws IFException { diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml new file mode 100644 index 000000000..486b8601c --- /dev/null +++ b/test/layoutengine/standard-testcases/pdf-dictionary-extension_2.xml @@ -0,0 +1,74 @@ + + + + + +

+ This test checks the PDF dictionary extensions related to optional content groups (layers). +

+
+ + + + + + + + + + + + + Bullet 1 + + + Bullet 2 + + + + + + + + + + + + Default + OFF + + + + + + + + BULLET 1A + BULLET 1B + + BULLET 2 + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml b/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml new file mode 100644 index 000000000..ee5061497 --- /dev/null +++ b/test/layoutengine/standard-testcases/pdf-dictionary-extension_3.xml @@ -0,0 +1,122 @@ + + + + + +

+ This test checks the PDF dictionary extensions related to optional content groups (layers), + including navigator and action dictionaries. +

+
+ + + + + + + + + + + + + + + + + Bullet 1 + + + Bullet 2 + + + + + OFF + + + + + + + OFF + + ON + + + + + + OFF + + ON + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + OFF + + + + + + + + BULLET 1A + BULLET 1B + + BULLET 2 + + + + + + + + +
-- 2.39.5