aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2008-12-09 23:50:41 +0000
committerJeremias Maerki <jeremias@apache.org>2008-12-09 23:50:41 +0000
commit342147f014c1eefe5fd113ef028848fade400c35 (patch)
treedb80d47d9fefb2217eaf62922150c3fac4c26d1d
parenta6ddced309701a1ca8d79a9e7bec3288afba2cc8 (diff)
downloadxmlgraphics-fop-342147f014c1eefe5fd113ef028848fade400c35.tar.gz
xmlgraphics-fop-342147f014c1eefe5fd113ef028848fade400c35.zip
Forgot to add link support. First version added now. Generalizing this for the IF is not so easy it seems.
Moved document navigation features (bookmarks, links, named destinations) into a separate handler interface that can be optionally implemented by document handler implementations. This will need a bit more work to be complete (parsing for document navigation from IF, cleanup in PDF library etc.). git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@724932 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/java/org/apache/fop/pdf/PDFReference.java30
-rw-r--r--src/java/org/apache/fop/pdf/PDFUri.java16
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java5
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java6
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFDocumentNavigationHandler.java52
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java151
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java42
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java58
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/ActionSet.java87
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/Bookmark.java7
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionConstants.java5
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionHandlerFactory.java7
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java32
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/Link.java99
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/NamedDestination.java14
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/ReferencedAction.java58
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/URIAction.java45
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFContentGenerator.java11
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java92
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java162
-rw-r--r--test/java/org/apache/fop/pdf/PDFObjectTestCase.java18
21 files changed, 878 insertions, 119 deletions
diff --git a/src/java/org/apache/fop/pdf/PDFReference.java b/src/java/org/apache/fop/pdf/PDFReference.java
index 5c3bdf3dc..1d9ddc349 100644
--- a/src/java/org/apache/fop/pdf/PDFReference.java
+++ b/src/java/org/apache/fop/pdf/PDFReference.java
@@ -33,7 +33,8 @@ import java.lang.ref.SoftReference;
*/
public class PDFReference implements PDFWritable {
- private String indirectReference;
+ private int objectNumber;
+ private int generation;
private Reference objReference;
@@ -42,7 +43,8 @@ public class PDFReference implements PDFWritable {
* @param obj the object to be referenced
*/
public PDFReference(PDFObject obj) {
- this.indirectReference = obj.referencePDF();
+ this.objectNumber = obj.getObjectNumber();
+ this.generation = obj.getGeneration();
this.objReference = new SoftReference(obj);
}
@@ -54,7 +56,11 @@ public class PDFReference implements PDFWritable {
if (ref == null) {
throw new NullPointerException("ref must not be null");
}
- this.indirectReference = ref;
+ String[] parts = ref.split(" ");
+ assert parts.length == 3;
+ this.objectNumber = Integer.parseInt(parts[0]);
+ this.generation = Integer.parseInt(parts[1]);
+ assert "R".equals(parts[2]);
}
/**
@@ -73,9 +79,25 @@ public class PDFReference implements PDFWritable {
}
}
+ /**
+ * Returns the object number.
+ * @return the object number
+ */
+ public int getObjectNumber() {
+ return this.objectNumber;
+ }
+
+ /**
+ * Returns the generation.
+ * @return the generation
+ */
+ public int getGeneration() {
+ return this.generation;
+ }
+
/** {@inheritDoc} */
public String toString() {
- return this.indirectReference;
+ return getObjectNumber() + " " + getGeneration() + " R";
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/pdf/PDFUri.java b/src/java/org/apache/fop/pdf/PDFUri.java
index 296e38945..a6124ec03 100644
--- a/src/java/org/apache/fop/pdf/PDFUri.java
+++ b/src/java/org/apache/fop/pdf/PDFUri.java
@@ -41,14 +41,22 @@ public class PDFUri extends PDFAction {
* @return the action to place next to /A within a Link
*/
public String getAction() {
+ if (hasObjectNumber()) {
+ return referencePDF();
+ } else {
+ return getDictString();
+ }
+ }
+
+ private String getDictString() {
return "<< /URI (" + uri + ")\n/S /URI >>";
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public String toPDFString() {
- throw new UnsupportedOperationException("This method should not be called");
+ //TODO Convert this class into a dictionary
+ return getObjectID() + getDictString() + "\nendobj\n";
+ //throw new UnsupportedOperationException("This method should not be called");
}
}
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java
index b1f1f20f9..4894604a2 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java
@@ -57,6 +57,11 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler {
}
/** {@inheritDoc} */
+ public IFDocumentNavigationHandler getDocumentNavigationHandler() {
+ return null; //By default, this is not supported
+ }
+
+ /** {@inheritDoc} */
public void startDocumentHeader() throws IFException {
//nop
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java
index e3634e9ef..f1d6e2057 100644
--- a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java
@@ -113,6 +113,12 @@ public interface IFDocumentHandler {
IFDocumentHandlerConfigurator getConfigurator();
/**
+ * Returns a document navigation handler if this feature is supported.
+ * @return the document navigation handler or null if not supported
+ */
+ IFDocumentNavigationHandler getDocumentNavigationHandler();
+
+ /**
* Indicates whether the painter supports to handle the pages in mixed order rather than
* ascending order.
* @return true if out-of-order handling is supported
diff --git a/src/java/org/apache/fop/render/intermediate/IFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/intermediate/IFDocumentNavigationHandler.java
new file mode 100644
index 000000000..eef13e105
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFDocumentNavigationHandler.java
@@ -0,0 +1,52 @@
+/*
+ * 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.intermediate;
+
+import org.apache.fop.render.intermediate.extensions.AbstractAction;
+import org.apache.fop.render.intermediate.extensions.BookmarkTree;
+import org.apache.fop.render.intermediate.extensions.Link;
+import org.apache.fop.render.intermediate.extensions.NamedDestination;
+
+
+/**
+ * Interface to handle document navigation features. This is an optional interface for
+ * document handler implementations which support document navigation features.
+ */
+public interface IFDocumentNavigationHandler {
+
+ /**
+ * Renders a named destination.
+ * @param destination the named destination
+ * @throws IFException if an error occurs while handling this event
+ */
+ void renderNamedDestination(NamedDestination destination) throws IFException;
+
+ /**
+ * Render the bookmark tree.
+ * @param tree the bookmark tree
+ * @throws IFException if an error occurs while handling this event
+ */
+ void renderBookmarkTree(BookmarkTree tree) throws IFException;
+
+ void renderLink(Link link) throws IFException;
+
+ void addResolvedAction(AbstractAction action) throws IFException;
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index fecde8ae4..4b2b04395 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -28,6 +28,7 @@ import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
@@ -67,6 +68,7 @@ import org.apache.fop.area.inline.AbstractTextArea;
import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
@@ -82,10 +84,14 @@ import org.apache.fop.fonts.LazyFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Renderer;
+import org.apache.fop.render.intermediate.extensions.AbstractAction;
+import org.apache.fop.render.intermediate.extensions.ActionSet;
import org.apache.fop.render.intermediate.extensions.Bookmark;
import org.apache.fop.render.intermediate.extensions.BookmarkTree;
import org.apache.fop.render.intermediate.extensions.GoToXYAction;
+import org.apache.fop.render.intermediate.extensions.Link;
import org.apache.fop.render.intermediate.extensions.NamedDestination;
+import org.apache.fop.render.intermediate.extensions.URIAction;
import org.apache.fop.render.pdf.PDFEventProducer;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
@@ -130,12 +136,6 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
private Map idPositions = new java.util.HashMap();
/**
- * Maps XSL-FO element IDs to "go-to" actions targeting the corresponding areas
- * These objects may not all be fully filled in yet
- */
- private Map idGoTos = new java.util.HashMap();
-
- /**
* The "go-to" actions in idGoTos that are not complete yet
*/
private List unfinishedGoTos = new java.util.ArrayList();
@@ -143,6 +143,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
// even if the object number differs
private BookmarkTree bookmarkTree;
+ private List deferredDestinations = new java.util.ArrayList();
+ private List deferredLinks = new java.util.ArrayList();
+ private ActionSet actionSet = new ActionSet();
private TextUtil textUtil = new TextUtil();
@@ -202,6 +205,22 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
/**
+ * Returns the document navigation handler if available/supported.
+ * @return the document navigation handler or null if not supported
+ */
+ protected IFDocumentNavigationHandler getDocumentNavigationHandler() {
+ return this.documentHandler.getDocumentNavigationHandler();
+ }
+
+ /**
+ * Indicates whether document navigation features are supported by the document handler.
+ * @return true if document navigation features are available
+ */
+ protected boolean hasDocumentNavigation() {
+ return getDocumentNavigationHandler() != null;
+ }
+
+ /**
* Creates a default {@code IFDocumentHandler} when none has been set.
* @return the default IFDocumentHandler
*/
@@ -246,17 +265,32 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
this.inPageSequence = false;
}
documentHandler.startDocumentTrailer();
+
+ //Wrap up document navigation
finishOpenGoTos();
+ Iterator iter;
+ iter = this.actionSet.getActions();
+ while (iter.hasNext()) {
+ getDocumentNavigationHandler().addResolvedAction((AbstractAction)iter.next());
+ }
+ iter = this.deferredDestinations.iterator();
+ while (iter.hasNext()) {
+ NamedDestination dest = (NamedDestination)iter.next();
+ iter.remove();
+ getDocumentNavigationHandler().renderNamedDestination(dest);
+ }
+
if (this.bookmarkTree != null) {
- documentHandler.handleExtensionObject(this.bookmarkTree);
+ getDocumentNavigationHandler().renderBookmarkTree(this.bookmarkTree);
}
+
documentHandler.endDocumentTrailer();
documentHandler.endDocument();
} catch (IFException e) {
handleIFExceptionWithIOException(e);
}
idPositions.clear();
- idGoTos.clear();
+ actionSet.clear();
super.stopRenderer();
log.debug("Rendering finished.");
}
@@ -278,6 +312,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
private void renderDestination(DestinationData dd) {
+ if (!hasDocumentNavigation()) {
+ return;
+ }
String targetID = dd.getIDRef();
if (targetID == null || targetID.length() == 0) {
throw new IllegalArgumentException("DestinationData must contain a ID reference");
@@ -285,12 +322,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
PageViewport pv = dd.getPageViewport();
if (pv != null) {
GoToXYAction action = getGoToActionForID(targetID, pv.getPageIndex());
- NamedDestination namedDestination = new NamedDestination(targetID, action);
- try {
- documentHandler.handleExtensionObject(namedDestination);
- } catch (IFException ife) {
- handleIFException(ife);
- }
+ NamedDestination namedDestination = new NamedDestination(targetID,
+ action.createReference());
+ this.deferredDestinations.add(namedDestination);
} else {
//Warning already issued by AreaTreeHandler (debug level is sufficient)
log.debug("Unresolved destination item received: " + dd.getIDRef());
@@ -303,6 +337,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
*/
protected void renderBookmarkTree(BookmarkData bookmarks) {
assert this.bookmarkTree == null;
+ if (!hasDocumentNavigation()) {
+ return;
+ }
this.bookmarkTree = new BookmarkTree();
for (int i = 0; i < bookmarks.getCount(); i++) {
BookmarkData ext = bookmarks.getSubData(i);
@@ -330,7 +367,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
Bookmark b = new Bookmark(
bookmarkItem.getBookmarkTitle(),
bookmarkItem.showChildItems(),
- action);
+ (action != null ? action.createReference() : null));
for (int i = 0; i < bookmarkItem.getCount(); i++) {
b.addChildBookmark(renderBookmarkItem(bookmarkItem.getSubData(i)));
}
@@ -342,23 +379,21 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
private GoToXYAction getGoToActionForID(String targetID, int pageIndex) {
- // Already a PDFGoTo present for this target? If not, create.
- GoToXYAction action = (GoToXYAction)idGoTos.get(targetID);
+ // Already a GoToXY present for this target? If not, create.
+ GoToXYAction action = (GoToXYAction)actionSet.get(targetID);
+ //GoToXYAction action = (GoToXYAction)idGoTos.get(targetID);
if (action == null) {
- //String pdfPageRef = (String) pageReferences.get(pvKey);
Point position = (Point)idPositions.get(targetID);
// can the GoTo already be fully filled in?
- if (/*pdfPageRef != null &&*/ position != null) {
- // getPDFGoTo shares PDFGoTo objects as much as possible.
- // It also takes care of assignObjectNumber and addTrailerObject.
- //action = pdfDoc.getFactory().getPDFGoTo(pdfPageRef, position);
- action = new GoToXYAction(pageIndex, position);
+ if (position != null) {
+ action = new GoToXYAction(targetID, pageIndex, position);
} else {
// Not complete yet, can't use getPDFGoTo:
- action = new GoToXYAction(pageIndex, null);
+ action = new GoToXYAction(targetID, pageIndex, null);
unfinishedGoTos.add(action);
}
- idGoTos.put(targetID, action);
+ action = (GoToXYAction)actionSet.put(action);
+ //idGoTos.put(targetID, action);
}
return action;
}
@@ -393,7 +428,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
tf.transform(position, position);
idPositions.put(id, position);
// is there already a PDFGoTo waiting to be completed?
- GoToXYAction action = (GoToXYAction)idGoTos.get(id);
+ GoToXYAction action = (GoToXYAction)actionSet.get(id);
if (action != null) {
noteGoToPosition(action, pv, position);
}
@@ -507,9 +542,18 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
super.renderPage(page);
this.painter = null;
documentHandler.endPageContent();
+
documentHandler.startPageTrailer();
- //TODO Handle page trailer
+ if (hasDocumentNavigation()) {
+ Iterator iter = this.deferredLinks.iterator();
+ while (iter.hasNext()) {
+ Link link = (Link)iter.next();
+ iter.remove();
+ getDocumentNavigationHandler().renderLink(link);
+ }
+ }
documentHandler.endPageTrailer();
+
documentHandler.endPage();
} catch (IFException e) {
handleIFException(e);
@@ -800,6 +844,59 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
/** {@inheritDoc} */
+ public void renderInlineParent(InlineParent ip) {
+ // stuff we only need if a link must be created:
+ Rectangle ipRect = null;
+ AbstractAction action = null;
+ // make sure the rect is determined *before* calling super!
+ int ipp = currentIPPosition;
+ int bpp = currentBPPosition + ip.getOffset();
+ ipRect = new Rectangle(ipp, bpp, ip.getIPD(), ip.getBPD());
+ AffineTransform transform = graphicContext.getTransform();
+ ipRect = transform.createTransformedShape(ipRect).getBounds();
+
+ // render contents
+ super.renderInlineParent(ip);
+
+ boolean linkTraitFound = false;
+
+ // try INTERNAL_LINK first
+ Trait.InternalLink intLink = (Trait.InternalLink) ip.getTrait(Trait.INTERNAL_LINK);
+ if (intLink != null) {
+ linkTraitFound = true;
+ String pvKey = intLink.getPVKey();
+
+ String idRef = intLink.getIDRef();
+ boolean pvKeyOK = pvKey != null && pvKey.length() > 0;
+ boolean idRefOK = idRef != null && idRef.length() > 0;
+ if (pvKeyOK && idRefOK) {
+ action = getGoToActionForID(idRef, this.currentPageViewport.getPageIndex());
+ } else {
+ //Warnings already issued by AreaTreeHandler
+ }
+ }
+
+ // no INTERNAL_LINK, look for EXTERNAL_LINK
+ if (!linkTraitFound) {
+ Trait.ExternalLink extLink = (Trait.ExternalLink) ip.getTrait(Trait.EXTERNAL_LINK);
+ if (extLink != null) {
+ String extDest = extLink.getDestination();
+ if (extDest != null && extDest.length() > 0) {
+ linkTraitFound = true;
+ action = new URIAction(extDest, extLink.newWindow());
+ action = actionSet.put(action);
+ }
+ }
+ }
+
+ // warn if link trait found but not allowed, else create link
+ if (linkTraitFound) {
+ Link link = new Link(action.createReference(), ipRect);
+ this.deferredLinks.add(link);
+ }
+ }
+
+ /** {@inheritDoc} */
protected void renderBlock(Block block) {
if (log.isTraceEnabled()) {
log.trace("renderBlock() " + block);
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 661a2fddd..c4fb0cb0d 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -38,6 +38,10 @@ import org.apache.xmlgraphics.util.XMLizable;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.intermediate.extensions.AbstractAction;
+import org.apache.fop.render.intermediate.extensions.BookmarkTree;
+import org.apache.fop.render.intermediate.extensions.Link;
+import org.apache.fop.render.intermediate.extensions.NamedDestination;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.ColorUtil;
@@ -47,7 +51,8 @@ import org.apache.fop.util.XMLUtil;
/**
* IFPainter implementation that serializes the intermediate format to XML.
*/
-public class IFSerializer extends AbstractXMLWritingIFDocumentHandler implements IFConstants, IFPainter {
+public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
+ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
private IFDocumentHandler mimicHandler;
@@ -83,6 +88,11 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler implements
}
}
+ /** {@inheritDoc} */
+ public IFDocumentNavigationHandler getDocumentNavigationHandler() {
+ return this;
+ }
+
public void mimicDocumentHandler(IFDocumentHandler targetHandler) {
this.mimicHandler = targetHandler;
}
@@ -553,4 +563,34 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler implements
XMLUtil.addAttribute(atts, localName, value);
}
+ // ---=== IFDocumentNavigationHandler ===---
+
+ /** {@inheritDoc} */
+ public void renderNamedDestination(NamedDestination destination) throws IFException {
+ renderXMLizable(destination);
+ }
+
+ /** {@inheritDoc} */
+ public void renderBookmarkTree(BookmarkTree tree) throws IFException {
+ renderXMLizable(tree);
+ }
+
+ /** {@inheritDoc} */
+ public void addResolvedAction(AbstractAction action) throws IFException {
+ renderXMLizable(action);
+ }
+
+ /** {@inheritDoc} */
+ public void renderLink(Link link) throws IFException {
+ renderXMLizable(link);
+ }
+
+ private void renderXMLizable(XMLizable object) throws IFException {
+ try {
+ object.toSAX(handler);
+ } catch (SAXException e) {
+ throw new IFException("SAX error serializing object", e);
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java
index 37a3032cd..f5bde16e1 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java
@@ -26,4 +26,62 @@ import org.apache.xmlgraphics.util.XMLizable;
*/
public abstract class AbstractAction implements XMLizable {
+ private String id;
+
+ /**
+ * Sets an ID to make the action referencable.
+ * @param id the ID
+ */
+ public void setID(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns an optional ID for this action.
+ * @return the ID or null
+ */
+ public String getID() {
+ return this.id;
+ }
+
+ /**
+ * Indicates whether the action has an ID and is therefore referencable.
+ * @return true if the action has an ID
+ */
+ public boolean hasID() {
+ return this.id != null;
+ }
+
+ /**
+ * Indicates whether two action are equal. Note: this is not the same as
+ * {@link Object#equals(Object)}!
+ * @param other the other action to compare to
+ * @return true if the actions are equal
+ */
+ public abstract boolean isSame(AbstractAction other);
+
+ /**
+ * Indicates whether this action is a reference.
+ * @return true if this action is a reference, false for a normal action
+ */
+ public boolean isReference() {
+ return false;
+ }
+
+ /**
+ * Creates a reference to this action.
+ * @return the reference
+ */
+ public AbstractAction createReference() {
+ return new ReferencedAction(getID());
+ }
+
+ /**
+ * Returns a string that is used to prefix a generated ID to make it unique.
+ * @return the prefix string
+ */
+ public String getIDPrefix() {
+ return null;
+ }
+
}
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/ActionSet.java b/src/java/org/apache/fop/render/intermediate/extensions/ActionSet.java
new file mode 100644
index 000000000..df663baef
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/extensions/ActionSet.java
@@ -0,0 +1,87 @@
+/*
+ * 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.intermediate.extensions;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import org.apache.xmlgraphics.util.XMLizable;
+
+/**
+ * This class manages actions and action references. Some action (like {@link GoToXYAction}s)
+ * cannot be fully resolved at the time they are needed, so they are deferred. This class
+ * helps manages the references and resolution.
+ */
+public class ActionSet implements XMLizable {
+
+ private int lastGeneratedID = 0;
+ private Map actionRegistry = new java.util.HashMap();
+
+ public synchronized String generateNewID(AbstractAction action) {
+ this.lastGeneratedID++;
+ String prefix = action.getIDPrefix();
+ if (prefix == null) {
+ throw new IllegalArgumentException("Action class is not compatible");
+ }
+ return prefix + this.lastGeneratedID;
+ }
+
+ public AbstractAction get(String id) {
+ return (AbstractAction)this.actionRegistry.get(id);
+ }
+
+ public AbstractAction put(AbstractAction action) {
+ if (!action.hasID()) {
+ action.setID(generateNewID(action));
+ }
+ AbstractAction effAction = normalize(action);
+ if (effAction == action) {
+ this.actionRegistry.put(action.getID(), action);
+ }
+ return effAction;
+ }
+
+ public void clear() {
+ this.actionRegistry.clear();
+ }
+
+ public AbstractAction normalize(AbstractAction action) {
+ Iterator iter = this.actionRegistry.values().iterator();
+ while (iter.hasNext()) {
+ AbstractAction a = (AbstractAction)iter.next();
+ if (a.isSame(action)) {
+ return a;
+ }
+ }
+ return action;
+ }
+
+ public Iterator getActions() {
+ return this.actionRegistry.values().iterator();
+ }
+
+ public void toSAX(ContentHandler handler) throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/Bookmark.java b/src/java/org/apache/fop/render/intermediate/extensions/Bookmark.java
index 446da6ef9..b4c4bf192 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/Bookmark.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/Bookmark.java
@@ -29,6 +29,7 @@ import org.xml.sax.helpers.AttributesImpl;
import org.apache.xmlgraphics.util.XMLizable;
+import org.apache.fop.util.XMLConstants;
import org.apache.fop.util.XMLUtil;
/**
@@ -114,9 +115,13 @@ public class Bookmark implements XMLizable, DocumentNavigationExtensionConstants
atts.addAttribute(null, "title", "title", XMLUtil.CDATA, getTitle());
atts.addAttribute(null, "starting-state", "starting-state",
XMLUtil.CDATA, isShown() ? "show" : "hide");
+ if (getAction().isReference()) {
+ atts.addAttribute(null, ACTION_REF, ACTION_REF,
+ XMLConstants.CDATA, getAction().getID());
+ }
handler.startElement(BOOKMARK.getNamespaceURI(),
BOOKMARK.getLocalName(), BOOKMARK.getQName(), atts);
- if (getAction() != null) {
+ if (!getAction().isReference()) {
getAction().toSAX(handler);
}
if (this.childBookmarks != null) {
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionConstants.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionConstants.java
index a8f458f97..6c61f219e 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionConstants.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionConstants.java
@@ -38,10 +38,15 @@ public interface DocumentNavigationExtensionConstants {
/** the named-destination element */
QName NAMED_DESTINATION = new QName(NAMESPACE, "named-destination");
+ /** the link element */
+ QName LINK = new QName(NAMESPACE, "link");
/** the goto-xy element */
QName GOTO_XY = new QName(NAMESPACE, "goto-xy");
/** the goto-uri element */
QName GOTO_URI = new QName(NAMESPACE, "goto-uri");
+ /** Attribute name for the action reference */
+ String ACTION_REF = "action-ref";
+
}
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionHandlerFactory.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionHandlerFactory.java
index 2822ff2ad..9044a4a62 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionHandlerFactory.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationExtensionHandlerFactory.java
@@ -98,14 +98,17 @@ public class DocumentNavigationExtensionHandlerFactory
NamedDestination dest = new NamedDestination(name, null);
objectStack.push(dest);
} else if (GOTO_XY.getLocalName().equals(localName)) {
+ String id = attributes.getValue("id");
int pageIndex = XMLUtil.getAttributeAsInt(attributes, "page-index");
int x = XMLUtil.getAttributeAsInt(attributes, "x");
int y = XMLUtil.getAttributeAsInt(attributes, "y");
- GoToXYAction action = new GoToXYAction(pageIndex, new Point(x, y));
+ GoToXYAction action = new GoToXYAction(id, pageIndex, new Point(x, y));
objectStack.push(action);
} else if (GOTO_URI.getLocalName().equals(localName)) {
String gotoURI = attributes.getValue("uri");
- URIAction action = new URIAction(gotoURI);
+ String showDestination = attributes.getValue("show-destination");
+ boolean newWindow = "new".equals(showDestination);
+ URIAction action = new URIAction(gotoURI, newWindow);
objectStack.push(action);
} else {
throw new SAXException(
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java b/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java
index 67cd5b592..4148b76f6 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/GoToXYAction.java
@@ -40,9 +40,10 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx
* @param pageIndex the page index (0-based) of the target page
* @param targetLocation the absolute location on the page (coordinates in millipoints)
*/
- public GoToXYAction(int pageIndex, Point targetLocation) {
+ public GoToXYAction(String id, int pageIndex, Point targetLocation) {
+ setID(id);
this.pageIndex = pageIndex;
- this.targetLocation = targetLocation;
+ setTargetLocation(targetLocation);
}
/**
@@ -70,8 +71,35 @@ public class GoToXYAction extends AbstractAction implements DocumentNavigationEx
}
/** {@inheritDoc} */
+ public boolean isSame(AbstractAction other) {
+ if (other == null) {
+ throw new NullPointerException("other must not be null");
+ }
+ if (!(other instanceof GoToXYAction)) {
+ return false;
+ }
+ GoToXYAction otherAction = (GoToXYAction)other;
+ if (getPageIndex() != otherAction.getPageIndex()) {
+ return false;
+ }
+ if (getTargetLocation() == null && otherAction.getTargetLocation() != null) {
+ return false;
+ }
+ if (!getTargetLocation().equals(otherAction.getTargetLocation())) {
+ return false;
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
+ if (getTargetLocation() == null) {
+ setTargetLocation(new Point(0, 0));
+ }
AttributesImpl atts = new AttributesImpl();
+ if (hasID()) {
+ atts.addAttribute(null, "id", "id", XMLUtil.CDATA, getID());
+ }
atts.addAttribute(null, "page-index", "page-index",
XMLUtil.CDATA, Integer.toString(pageIndex));
atts.addAttribute(null, "x", "x", XMLUtil.CDATA, Integer.toString(targetLocation.x));
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/Link.java b/src/java/org/apache/fop/render/intermediate/extensions/Link.java
new file mode 100644
index 000000000..b54697ac4
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/extensions/Link.java
@@ -0,0 +1,99 @@
+/*
+ * 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.intermediate.extensions;
+
+import java.awt.Rectangle;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.xmlgraphics.util.XMLizable;
+
+import org.apache.fop.render.intermediate.IFUtil;
+import org.apache.fop.util.XMLConstants;
+
+/**
+ * This class is a link element for use in the intermediate format.
+ */
+public class Link implements XMLizable, DocumentNavigationExtensionConstants {
+
+ /** Attribute name for the target rectangle */
+ public static final String RECT = "rect";
+
+ private AbstractAction action;
+ private Rectangle targetRect;
+
+ /**
+ * Creates a new named destination.
+ * @param action the action performed when the destination is selected
+ * @param targetRect the target rectangle (coordinates in millipoints)
+ */
+ public Link(AbstractAction action, Rectangle targetRect) {
+ this.action = action;
+ this.targetRect = targetRect;
+ }
+
+ /**
+ * Returns the action performed when the destination is selected.
+ * @return the action
+ */
+ public AbstractAction getAction() {
+ return this.action;
+ }
+
+ /**
+ * Returns the target rectangle, i.e. the hot zone in which the link is activated.
+ * @return the target rectangle
+ */
+ public Rectangle getTargetRect() {
+ return new Rectangle(this.targetRect);
+ }
+
+ /**
+ * Sets the action performed when the destination is selected.
+ * @param action the action
+ */
+ public void setAction(AbstractAction action) {
+ this.action = action;
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ if (getAction() == null) {
+ throw new IllegalStateException("Action has not been set");
+ }
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(null, RECT, RECT,
+ XMLConstants.CDATA, IFUtil.toString(getTargetRect()));
+ if (getAction().isReference()) {
+ atts.addAttribute(null, ACTION_REF, ACTION_REF,
+ XMLConstants.CDATA, getAction().getID());
+ }
+ handler.startElement(LINK.getNamespaceURI(),
+ LINK.getLocalName(), LINK.getQName(), atts);
+ if (!getAction().isReference()) {
+ getAction().toSAX(handler);
+ }
+ handler.endElement(LINK.getNamespaceURI(),
+ LINK.getLocalName(), LINK.getQName());
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/NamedDestination.java b/src/java/org/apache/fop/render/intermediate/extensions/NamedDestination.java
index 85b6aca19..21676832b 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/NamedDestination.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/NamedDestination.java
@@ -32,6 +32,9 @@ import org.apache.fop.util.XMLConstants;
*/
public class NamedDestination implements XMLizable, DocumentNavigationExtensionConstants {
+ /** Attribute name for the destination name */
+ public static final String NAME = "name";
+
private String name;
private AbstractAction action;
@@ -71,11 +74,18 @@ public class NamedDestination implements XMLizable, DocumentNavigationExtensionC
/** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
+ if (getAction() == null) {
+ throw new IllegalStateException("Action has not been set");
+ }
AttributesImpl atts = new AttributesImpl();
- atts.addAttribute(null, "name", "name", XMLConstants.CDATA, getName());
+ atts.addAttribute(null, NAME, NAME, XMLConstants.CDATA, getName());
+ if (getAction().isReference()) {
+ atts.addAttribute(null, ACTION_REF, ACTION_REF,
+ XMLConstants.CDATA, getAction().getID());
+ }
handler.startElement(NAMED_DESTINATION.getNamespaceURI(),
NAMED_DESTINATION.getLocalName(), NAMED_DESTINATION.getQName(), atts);
- if (getAction() != null) {
+ if (!getAction().isReference()) {
getAction().toSAX(handler);
}
handler.endElement(NAMED_DESTINATION.getNamespaceURI(),
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/ReferencedAction.java b/src/java/org/apache/fop/render/intermediate/extensions/ReferencedAction.java
new file mode 100644
index 000000000..20ec11362
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/extensions/ReferencedAction.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.intermediate.extensions;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Action class which references another action.
+ */
+public class ReferencedAction extends AbstractAction
+ implements DocumentNavigationExtensionConstants {
+
+ /**
+ * Creates a new instance.
+ * @param id the ID
+ */
+ public ReferencedAction(String id) {
+ if (id == null || id.length() == 0) {
+ throw new NullPointerException("ID must not be set");
+ }
+ setID(id);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isReference() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ //nop, handled by referencer
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSame(AbstractAction other) {
+ throw new UnsupportedOperationException(
+ "isSame() may not be called on " + getClass().getName());
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/URIAction.java b/src/java/org/apache/fop/render/intermediate/extensions/URIAction.java
index e3020dac3..835a4b589 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/URIAction.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/URIAction.java
@@ -32,13 +32,19 @@ import org.apache.fop.util.XMLUtil;
public class URIAction extends AbstractAction implements DocumentNavigationExtensionConstants {
private String uri;
+ private boolean newWindow;
/**
* Creates a new instance.
* @param uri the target URI
+ * @param newWindow true if the link should be opened in a new window
*/
- public URIAction(String uri) {
+ public URIAction(String uri, boolean newWindow) {
+ if (uri == null) {
+ throw new NullPointerException("uri must not be null");
+ }
this.uri = uri;
+ this.newWindow = newWindow;
}
/**
@@ -49,10 +55,47 @@ public class URIAction extends AbstractAction implements DocumentNavigationExten
return this.uri;
}
+ /**
+ * Indicates whether the link shall be opened in a new window.
+ * @return true if a new window shall be opened
+ */
+ public boolean isNewWindow() {
+ return this.newWindow;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSame(AbstractAction other) {
+ if (other == null) {
+ throw new NullPointerException("other must not be null");
+ }
+ if (!(other instanceof URIAction)) {
+ return false;
+ }
+ URIAction otherAction = (URIAction)other;
+ if (!getURI().equals(otherAction.getURI())) {
+ return false;
+ }
+ if (isNewWindow() != otherAction.isNewWindow()) {
+ return false;
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String getIDPrefix() {
+ return "fop:" + GOTO_URI.getLocalName();
+ }
+
/** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
AttributesImpl atts = new AttributesImpl();
+ if (hasID()) {
+ atts.addAttribute(null, "id", "id", XMLUtil.CDATA, getID());
+ }
atts.addAttribute(null, "uri", "uri", XMLUtil.CDATA, getURI());
+ if (isNewWindow()) {
+ atts.addAttribute(null, "show-destination", "show-destination", XMLUtil.CDATA, "new");
+ }
handler.startElement(GOTO_URI.getNamespaceURI(),
GOTO_URI.getLocalName(), GOTO_URI.getQName(), atts);
handler.endElement(GOTO_URI.getNamespaceURI(),
diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
index 4b0f35bec..567ef0507 100644
--- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
@@ -238,17 +238,6 @@ public class PDFContentGenerator {
}
/**
- * Establishes a new foreground or fill color. In contrast to updateColor
- * this method does not check the PDFState for optimization possibilities.
- * @param col the color to apply
- * @param fill true to set the fill color, false for the foreground color
- * @param pdf StringBuffer to write the PDF code to
- *//*
- public void setColor(Color col, boolean fill, StringBuffer pdf) {
- assert pdf != null;
- }*/
-
- /**
* Establishes a new foreground or fill color.
* @param col the color to apply
* @param fill true to set the fill color, false for the foreground color
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
index 1358b1c5e..6535be758 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -21,9 +21,7 @@ package org.apache.fop.render.pdf;
import java.awt.Dimension;
import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
import java.io.IOException;
-import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
@@ -34,23 +32,17 @@ import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
-import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.PDFOutline;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
-import org.apache.fop.render.intermediate.extensions.AbstractAction;
-import org.apache.fop.render.intermediate.extensions.Bookmark;
-import org.apache.fop.render.intermediate.extensions.BookmarkTree;
-import org.apache.fop.render.intermediate.extensions.GoToXYAction;
-import org.apache.fop.render.intermediate.extensions.NamedDestination;
/**
* {@code IFDocumentHandler} implementation that produces PDF.
@@ -81,12 +73,15 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** the current page to add annotations to */
protected PDFPage currentPage;
- /** the current page's PDF reference string (to avoid numerous function calls) */
- protected String currentPageRef;
+ /** the current page's PDF reference */
+ protected PageReference currentPageRef;
/** Used for bookmarks/outlines. */
protected Map pageReferences = new java.util.HashMap();
+ private PDFDocumentNavigationHandler documentNavigationHandler
+ = new PDFDocumentNavigationHandler(this);
+
/**
* Default constructor.
*/
@@ -114,6 +109,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
return new PDFRendererConfigurator(getUserAgent());
}
+ /** {@inheritDoc} */
+ public IFDocumentNavigationHandler getDocumentNavigationHandler() {
+ return this.documentNavigationHandler;
+ }
+
PDFRenderingUtil getPDFUtil() {
return this.pdfUtil;
}
@@ -142,6 +142,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void endDocument() throws IFException {
try {
+ this.documentNavigationHandler.commit();
pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
pdfDoc.outputTrailer(this.outputStream);
@@ -182,15 +183,14 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
pdfUtil.generatePageLabel(index, name);
- currentPageRef = currentPage.referencePDF();
- this.pageReferences.put(new Integer(index), new PageReference(currentPage, size));
+ currentPageRef = new PageReference(currentPage, size);
+ this.pageReferences.put(new Integer(index), currentPageRef);
this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream, this.currentPage);
// Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's
AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
size.height / 1000f);
generator.concatenate(basicPageTransform);
-
}
/** {@inheritDoc} */
@@ -220,51 +220,6 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
}
}
- private void renderBookmarkTree(BookmarkTree tree) {
- Iterator iter = tree.getBookmarks().iterator();
- while (iter.hasNext()) {
- Bookmark b = (Bookmark)iter.next();
- renderBookmark(b, null);
- }
- }
-
- private void renderBookmark(Bookmark bookmark, PDFOutline parent) {
- if (parent == null) {
- parent = pdfDoc.getOutlineRoot();
- }
- PDFAction action = getAction(bookmark.getAction());
- PDFOutline pdfOutline = pdfDoc.getFactory().makeOutline(parent,
- bookmark.getTitle(), action, bookmark.isShown());
- Iterator iter = bookmark.getChildBookmarks().iterator();
- while (iter.hasNext()) {
- Bookmark b = (Bookmark)iter.next();
- renderBookmark(b, pdfOutline);
- }
- }
-
- private void renderNamedDestination(NamedDestination destination) {
- PDFAction action = getAction(destination.getAction());
- pdfDoc.getFactory().makeDestination(
- destination.getName(), action.makeReference());
- }
-
- private PDFAction getAction(AbstractAction action) {
- if (action instanceof GoToXYAction) {
- GoToXYAction a = (GoToXYAction)action;
- PageReference pageRef = (PageReference)this.pageReferences.get(
- new Integer(a.getPageIndex()));
- //Convert target location from millipoints to points and adjust for different
- //page origin
- Point2D p2d = new Point2D.Double(
- a.getTargetLocation().x / 1000.0,
- (pageRef.pageDimension.height - a.getTargetLocation().y) / 1000.0);
- return pdfDoc.getFactory().getPDFGoTo(pageRef.pageRef.toString(), p2d);
- } else {
- throw new UnsupportedOperationException("Unsupported action type: "
- + action + " (" + action.getClass().getName() + ")");
- }
- }
-
/** {@inheritDoc} */
public void handleExtensionObject(Object extension) throws IFException {
if (extension instanceof XMPMetadata) {
@@ -272,17 +227,18 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
} else if (extension instanceof Metadata) {
XMPMetadata wrapper = new XMPMetadata(((Metadata)extension));
pdfUtil.renderXMPMetadata(wrapper);
- } else if (extension instanceof BookmarkTree) {
- renderBookmarkTree((BookmarkTree)extension);
- } else if (extension instanceof NamedDestination) {
- renderNamedDestination((NamedDestination)extension);
} else {
log.debug("Don't know how to handle extension object. Ignoring: "
+ extension + " (" + extension.getClass().getName() + ")");
}
}
- private static final class PageReference {
+ PageReference getPageReference(int pageIndex) {
+ return (PageReference)this.pageReferences.get(
+ new Integer(pageIndex));
+ }
+
+ static final class PageReference {
private PDFReference pageRef;
private Dimension pageDimension;
@@ -291,6 +247,14 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
this.pageRef = page.makeReference();
this.pageDimension = new Dimension(dim);
}
+
+ public PDFReference getPageRef() {
+ return this.pageRef;
+ }
+
+ public Dimension getPageDimension() {
+ return this.pageDimension;
+ }
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
new file mode 100644
index 000000000..a50fb85db
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
@@ -0,0 +1,162 @@
+/*
+ * 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;
+
+import java.awt.Rectangle;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.fop.pdf.PDFAction;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFactory;
+import org.apache.fop.pdf.PDFLink;
+import org.apache.fop.pdf.PDFOutline;
+import org.apache.fop.pdf.PDFReference;
+import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.extensions.AbstractAction;
+import org.apache.fop.render.intermediate.extensions.ActionSet;
+import org.apache.fop.render.intermediate.extensions.Bookmark;
+import org.apache.fop.render.intermediate.extensions.BookmarkTree;
+import org.apache.fop.render.intermediate.extensions.GoToXYAction;
+import org.apache.fop.render.intermediate.extensions.Link;
+import org.apache.fop.render.intermediate.extensions.NamedDestination;
+import org.apache.fop.render.intermediate.extensions.URIAction;
+import org.apache.fop.render.pdf.PDFDocumentHandler.PageReference;
+
+/**
+ * Implementation of the {@link IFDocumentNavigationHandler} interface for PDF output.
+ */
+public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler {
+
+ private PDFDocumentHandler documentHandler;
+
+ private ActionSet actionSet = new ActionSet();
+ private List deferredLinks = new java.util.ArrayList();
+
+ /**
+ * Default constructor.
+ * @param documentHandler the parent document handler
+ */
+ public PDFDocumentNavigationHandler(PDFDocumentHandler documentHandler) {
+ super();
+ this.documentHandler = documentHandler;
+ }
+
+ PDFDocument getPDFDoc() {
+ return this.documentHandler.pdfDoc;
+ }
+
+ /** {@inheritDoc} */
+ public void renderNamedDestination(NamedDestination destination) throws IFException {
+ PDFReference actionRef = getAction(destination.getAction());
+ getPDFDoc().getFactory().makeDestination(
+ destination.getName(), actionRef);
+ }
+
+ /** {@inheritDoc} */
+ public void renderBookmarkTree(BookmarkTree tree) throws IFException {
+ Iterator iter = tree.getBookmarks().iterator();
+ while (iter.hasNext()) {
+ Bookmark b = (Bookmark)iter.next();
+ renderBookmark(b, null);
+ }
+ }
+
+ private void renderBookmark(Bookmark bookmark, PDFOutline parent) {
+ if (parent == null) {
+ parent = getPDFDoc().getOutlineRoot();
+ }
+ PDFReference actionRef = getAction(bookmark.getAction());
+ PDFOutline pdfOutline = getPDFDoc().getFactory().makeOutline(parent,
+ bookmark.getTitle(), actionRef.toString(), bookmark.isShown());
+ Iterator iter = bookmark.getChildBookmarks().iterator();
+ while (iter.hasNext()) {
+ Bookmark b = (Bookmark)iter.next();
+ renderBookmark(b, pdfOutline);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void renderLink(Link link) throws IFException {
+ this.deferredLinks.add(link);
+ }
+
+ /**
+ * Commits all pending elements to the PDF document.
+ */
+ public void commit() {
+ Iterator iter = this.deferredLinks.iterator();
+ while (iter.hasNext()) {
+ Link link = (Link)iter.next();
+ Rectangle targetRect = link.getTargetRect();
+ int pageHeight = documentHandler.currentPageRef.getPageDimension().height;
+ Rectangle2D targetRect2D = new Rectangle2D.Double(
+ targetRect.getMinX() / 1000.0,
+ (pageHeight - targetRect.getMinY()) / 1000.0,
+ targetRect.getWidth() / 1000.0,
+ targetRect.getHeight() / 1000.0);
+ PDFReference actionRef = getAction(link.getAction());
+ //makeLink() currently needs a PDFAction and not a reference
+ //TODO Revisit when PDFLink is converted to a PDFDictionary
+ PDFAction pdfAction = (PDFAction)actionRef.getObject();
+ PDFLink pdfLink = getPDFDoc().getFactory().makeLink(
+ targetRect2D, pdfAction);
+ documentHandler.currentPage.addAnnotation(pdfLink);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void addResolvedAction(AbstractAction action) throws IFException {
+ actionSet.put(action);
+ }
+
+ private PDFReference getAction(AbstractAction action) {
+ if (action.isReference()) {
+ String id = action.getID();
+ action = actionSet.get(id);
+ if (action == null) {
+ throw new IllegalStateException("Action could not be resolved: " + id);
+ }
+ }
+ PDFFactory factory = getPDFDoc().getFactory();
+ if (action instanceof GoToXYAction) {
+ GoToXYAction a = (GoToXYAction)action;
+ PageReference pageRef = this.documentHandler.getPageReference(a.getPageIndex());
+ //Convert target location from millipoints to points and adjust for different
+ //page origin
+ Point2D p2d = new Point2D.Double(
+ a.getTargetLocation().x / 1000.0,
+ (pageRef.getPageDimension().height - a.getTargetLocation().y) / 1000.0);
+ return factory.getPDFGoTo(pageRef.getPageRef().toString(), p2d).makeReference();
+ } else if (action instanceof URIAction) {
+ URIAction u = (URIAction)action;
+ PDFAction pdfAction = factory.getExternalAction(u.getURI(), u.isNewWindow());
+ getPDFDoc().registerObject(pdfAction);
+ return pdfAction.makeReference();
+ } else {
+ throw new UnsupportedOperationException("Unsupported action type: "
+ + action + " (" + action.getClass().getName() + ")");
+ }
+ }
+
+}
diff --git a/test/java/org/apache/fop/pdf/PDFObjectTestCase.java b/test/java/org/apache/fop/pdf/PDFObjectTestCase.java
index 40ddec6ba..d41a0f0f3 100644
--- a/test/java/org/apache/fop/pdf/PDFObjectTestCase.java
+++ b/test/java/org/apache/fop/pdf/PDFObjectTestCase.java
@@ -56,4 +56,22 @@ public class PDFObjectTestCase extends TestCase {
}
+ /**
+ * Tests PDF object references.
+ * @throws Exception if an error occurs
+ */
+ public void testReference() throws Exception {
+ PDFDictionary dict = new PDFDictionary();
+ dict.setObjectNumber(7);
+ PDFReference ref = dict.makeReference();
+ assertEquals(ref.getObjectNumber(), 7);
+ assertEquals(ref.getGeneration(), 0);
+ assertEquals(ref.toString(), "7 0 R");
+
+ ref = new PDFReference("8 0 R");
+ assertEquals(ref.getObjectNumber(), 8);
+ assertEquals(ref.getGeneration(), 0);
+ assertEquals(ref.toString(), "8 0 R");
+ }
+
}