// can't use a Set because PDFGoTo.equals returns true if the target is the same,
// even if the object number differs
+ /** Maps unique PageViewport key to page indices (for link target handling) */
+ protected Map pageIndices = new java.util.HashMap();
+
private BookmarkTree bookmarkTree;
private List deferredDestinations = new java.util.ArrayList();
private List deferredLinks = new java.util.ArrayList();
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 (hasDocumentNavigation()) {
+ finishOpenGoTos();
+ Iterator iter = this.deferredDestinations.iterator();
+ while (iter.hasNext()) {
+ NamedDestination dest = (NamedDestination)iter.next();
+ iter.remove();
+ getDocumentNavigationHandler().renderNamedDestination(dest);
+ }
- if (this.bookmarkTree != null) {
- getDocumentNavigationHandler().renderBookmarkTree(this.bookmarkTree);
+ if (this.bookmarkTree != null) {
+ getDocumentNavigationHandler().renderBookmarkTree(this.bookmarkTree);
+ }
}
documentHandler.endDocumentTrailer();
} catch (IFException e) {
handleIFExceptionWithIOException(e);
}
+ pageIndices.clear();
idPositions.clear();
actionSet.clear();
super.stopRenderer();
PageViewport pv = dd.getPageViewport();
if (pv != null) {
GoToXYAction action = getGoToActionForID(targetID, pv.getPageIndex());
- NamedDestination namedDestination = new NamedDestination(targetID,
- action.createReference());
+ NamedDestination namedDestination = new NamedDestination(targetID, action);
this.deferredDestinations.add(namedDestination);
} else {
//Warning already issued by AreaTreeHandler (debug level is sufficient)
Bookmark b = new Bookmark(
bookmarkItem.getBookmarkTitle(),
bookmarkItem.showChildItems(),
- (action != null ? action.createReference() : null));
+ action);
for (int i = 0; i < bookmarkItem.getCount(); i++) {
b.addChildBookmark(renderBookmarkItem(bookmarkItem.getSubData(i)));
}
GoToXYAction action = (GoToXYAction)actionSet.get(targetID);
//GoToXYAction action = (GoToXYAction)idGoTos.get(targetID);
if (action == null) {
+ if (pageIndex < 0) {
+ //pageIndex = page
+ }
Point position = (Point)idPositions.get(targetID);
// can the GoTo already be fully filled in?
- if (position != null) {
+ if (pageIndex >= 0 && position != null) {
action = new GoToXYAction(targetID, pageIndex, position);
} else {
// Not complete yet, can't use getPDFGoTo:
private void noteGoToPosition(GoToXYAction action, Point position) {
action.setTargetLocation(position);
+ try {
+ getDocumentNavigationHandler().addResolvedAction(action);
+ } catch (IFException ife) {
+ handleIFException(ife);
+ }
unfinishedGoTos.remove(action);
}
private void noteGoToPosition(GoToXYAction action, PageViewport pv, Point position) {
+ action.setPageIndex(pv.getPageIndex());
noteGoToPosition(action, position);
}
Point position = new Point(relativeIPP, relativeBPP);
tf.transform(position, position);
idPositions.put(id, position);
- // is there already a PDFGoTo waiting to be completed?
+ // is there already a GoTo action waiting to be completed?
GoToXYAction action = (GoToXYAction)actionSet.get(id);
if (action != null) {
noteGoToPosition(action, pv, position);
relativeIPP, relativeBPP, graphicContext.getTransform());
}
- protected void saveBlockPosIfTargetable(Block block) {
+ private void saveBlockPosIfTargetable(Block block) {
String id = getTargetableID(block);
- if (id != null) {
+ if (hasDocumentNavigation() && id != null) {
// FIXME: Like elsewhere in the renderer code, absolute and relative
// directions are happily mixed here. This makes sure that the
// links point to the right location, but it is not correct.
private void saveInlinePosIfTargetable(InlineArea inlineArea) {
String id = getTargetableID(inlineArea);
- if (id != null) {
+ if (hasDocumentNavigation() && id != null) {
int extraMarginBefore = 5000; // millipoints
int ipp = currentIPPosition;
int bpp = currentBPPosition + inlineArea.getOffset() - extraMarginBefore;
return xmp;
}
+ /** {@inheritDoc} */
+ public void preparePage(PageViewport page) {
+ super.preparePage(page);
+ }
+
/** {@inheritDoc} */
public void renderPage(PageViewport page) throws IOException, FOPException {
if (log.isTraceEnabled()) {
log.trace("renderPage() " + page);
}
try {
+ pageIndices.put(page.getKey(), new Integer(page.getPageIndex()));
Rectangle2D viewArea = page.getViewArea();
Dimension dim = new Dimension(
(int)Math.ceil(viewArea.getWidth()),
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());
+ Integer pageIndex = (Integer)pageIndices.get(pvKey);
+ action = getGoToActionForID(idRef, (pageIndex != null ? pageIndex.intValue() : -1));
} else {
//Warnings already issued by AreaTreeHandler
}
// warn if link trait found but not allowed, else create link
if (linkTraitFound) {
- Link link = new Link(action.createReference(), ipRect);
+ Link link = new Link(action, ipRect);
this.deferredLinks.add(link);
}
}
protected void renderText(String s,
int[] letterAdjust,
Font font, AbstractTextArea parentArea) {
- float fontSize = font.getFontSize() / 1000f;
+ //float fontSize = font.getFontSize() / 1000f;
int l = s.length();
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
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.Bookmark;
import org.apache.fop.render.intermediate.extensions.BookmarkTree;
+import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants;
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;
import org.apache.fop.util.DOM2SAX;
+import org.apache.fop.util.XMLConstants;
import org.apache.fop.util.XMLUtil;
/**
private IFDocumentHandler mimicHandler;
+ /** Holds the intermediate format state */
+ private IFState state;
+
/**
* Default constructor.
*/
return this;
}
+ /**
+ * Tells this serializer to mimic the given document handler (mostly applies to the font set
+ * that is used during layout).
+ * @param targetHandler the document handler to mimic
+ */
public void mimicDocumentHandler(IFDocumentHandler targetHandler) {
this.mimicHandler = targetHandler;
}
+ /**
+ * Returns the document handler that is being mimicked by this serializer.
+ * @return the mimicked document handler or null if no such document handler has been set
+ */
public IFDocumentHandler getMimickedDocumentHandler() {
return this.mimicHandler;
}
handler.startDocument();
handler.startPrefixMapping("", NAMESPACE);
handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE);
+ handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX,
+ DocumentNavigationExtensionConstants.NAMESPACE);
handler.startElement(EL_DOCUMENT);
} catch (SAXException e) {
throw new IFException("SAX error in startDocument()", e);
try {
handler.endElement(EL_DOCUMENT);
handler.endDocument();
+ finishDocumentNavigation();
} catch (SAXException e) {
throw new IFException("SAX error in endDocument()", e);
}
}
/** {@inheritDoc} */
- public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "index", Integer.toString(index));
public IFPainter startPageContent() throws IFException {
try {
handler.startElement(EL_PAGE_CONTENT);
+ this.state = IFState.create();
return this;
} catch (SAXException e) {
throw new IFException("SAX error in startPageContent()", e);
/** {@inheritDoc} */
public void endPageContent() throws IFException {
try {
+ this.state = null;
handler.endElement(EL_PAGE_CONTENT);
} catch (SAXException e) {
throw new IFException("SAX error in endPageContent()", e);
public void startPageTrailer() throws IFException {
try {
handler.startElement(EL_PAGE_TRAILER);
+ commitNavigation();
} catch (SAXException e) {
throw new IFException("SAX error in startPageTrailer()", e);
}
Color color) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
+ boolean changed;
if (family != null) {
- addAttribute(atts, "family", family);
+ changed = !family.equals(state.getFontFamily());
+ if (changed) {
+ state.setFontFamily(family);
+ addAttribute(atts, "family", family);
+ }
}
if (style != null) {
- addAttribute(atts, "style", style);
+ changed = !style.equals(state.getFontStyle());
+ if (changed) {
+ state.setFontStyle(style);
+ addAttribute(atts, "style", style);
+ }
}
if (weight != null) {
- addAttribute(atts, "weight", weight.toString());
+ changed = (weight.intValue() != state.getFontWeight());
+ if (changed) {
+ state.setFontWeight(weight.intValue());
+ addAttribute(atts, "weight", weight.toString());
+ }
}
if (variant != null) {
- addAttribute(atts, "variant", variant);
+ changed = !variant.equals(state.getFontVariant());
+ if (changed) {
+ state.setFontVariant(variant);
+ addAttribute(atts, "variant", variant);
+ }
}
if (size != null) {
- addAttribute(atts, "size", size.toString());
+ changed = (size.intValue() != state.getFontSize());
+ if (changed) {
+ state.setFontSize(size.intValue());
+ addAttribute(atts, "size", size.toString());
+ }
}
if (color != null) {
- addAttribute(atts, "color", toString(color));
+ changed = !color.equals(state.getTextColor());
+ if (changed) {
+ state.setTextColor(color);
+ addAttribute(atts, "color", toString(color));
+ }
+ }
+ if (atts.getLength() > 0) {
+ handler.element(EL_FONT, atts);
}
- handler.element(EL_FONT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in setFont()", e);
}
// ---=== IFDocumentNavigationHandler ===---
+ private Map incompleteActions = new java.util.HashMap();
+ private List completeActions = new java.util.LinkedList();
+
+ private void noteAction(AbstractAction action) {
+ if (action == null) {
+ throw new NullPointerException("action must not be null");
+ }
+ if (!action.isComplete()) {
+ assert action.hasID();
+ incompleteActions.put(action.getID(), action);
+ }
+ }
+
/** {@inheritDoc} */
public void renderNamedDestination(NamedDestination destination) throws IFException {
- renderXMLizable(destination);
+ noteAction(destination.getAction());
+
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(null, "name", "name", XMLConstants.CDATA, destination.getName());
+ try {
+ handler.startElement(DocumentNavigationExtensionConstants.NAMED_DESTINATION, atts);
+ serializeXMLizable(destination.getAction());
+ handler.endElement(DocumentNavigationExtensionConstants.NAMED_DESTINATION);
+ } catch (SAXException e) {
+ throw new IFException("SAX error serializing named destination", e);
+ }
}
/** {@inheritDoc} */
public void renderBookmarkTree(BookmarkTree tree) throws IFException {
- renderXMLizable(tree);
+ AttributesImpl atts = new AttributesImpl();
+ try {
+ handler.startElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE, atts);
+ Iterator iter = tree.getBookmarks().iterator();
+ while (iter.hasNext()) {
+ Bookmark b = (Bookmark)iter.next();
+ serializeBookmark(b);
+ }
+ handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE);
+ } catch (SAXException e) {
+ throw new IFException("SAX error serializing bookmark tree", e);
+ }
}
- /** {@inheritDoc} */
- public void addResolvedAction(AbstractAction action) throws IFException {
- renderXMLizable(action);
+ private void serializeBookmark(Bookmark bookmark) throws SAXException, IFException {
+ noteAction(bookmark.getAction());
+
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(null, "title", "title", XMLUtil.CDATA, bookmark.getTitle());
+ atts.addAttribute(null, "starting-state", "starting-state",
+ XMLUtil.CDATA, bookmark.isShown() ? "show" : "hide");
+ handler.startElement(DocumentNavigationExtensionConstants.BOOKMARK, atts);
+ serializeXMLizable(bookmark.getAction());
+ Iterator iter = bookmark.getChildBookmarks().iterator();
+ while (iter.hasNext()) {
+ Bookmark b = (Bookmark)iter.next();
+ serializeBookmark(b);
+ }
+ handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK);
+
}
/** {@inheritDoc} */
public void renderLink(Link link) throws IFException {
- renderXMLizable(link);
+ noteAction(link.getAction());
+
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(null, "rect", "rect",
+ XMLConstants.CDATA, IFUtil.toString(link.getTargetRect()));
+ try {
+ handler.startElement(DocumentNavigationExtensionConstants.LINK, atts);
+ serializeXMLizable(link.getAction());
+ handler.endElement(DocumentNavigationExtensionConstants.LINK);
+ } catch (SAXException e) {
+ throw new IFException("SAX error serializing link", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void addResolvedAction(AbstractAction action) throws IFException {
+ assert action.isComplete();
+ assert action.hasID();
+ AbstractAction noted = (AbstractAction)incompleteActions.get(action.getID());
+ if (noted != null) {
+ incompleteActions.remove(action.getID());
+ completeActions.add(action);
+ } else {
+ //ignore as it was already complete when it was first used.
+ }
+ }
+
+ private void commitNavigation() throws IFException {
+ Iterator iter = this.completeActions.iterator();
+ while (iter.hasNext()) {
+ AbstractAction action = (AbstractAction)iter.next();
+ iter.remove();
+ serializeXMLizable(action);
+ }
+ assert this.completeActions.size() == 0;
+ }
+
+ private void finishDocumentNavigation() {
+ assert this.incompleteActions.size() == 0 : "Still holding incomplete actions!";
}
- private void renderXMLizable(XMLizable object) throws IFException {
+ private void serializeXMLizable(XMLizable object) throws IFException {
try {
object.toSAX(handler);
} catch (SAXException e) {
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
+ * Indicates whether the action is complete, i.e has all the required information to be
+ * rendered in the target format.
+ * @return true if the action is complete
*/
- public boolean isReference() {
- return false;
- }
-
- /**
- * Creates a reference to this action.
- * @return the reference
- */
- public AbstractAction createReference() {
- return new ReferencedAction(getID());
+ public boolean isComplete() {
+ return true;
}
/**
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 {
+public class ActionSet {
private int lastGeneratedID = 0;
private Map actionRegistry = new java.util.HashMap();
+ /**
+ * Generates a new synthetic ID for an action.
+ * @param action the action
+ * @return the generated ID
+ */
public synchronized String generateNewID(AbstractAction action) {
this.lastGeneratedID++;
String prefix = action.getIDPrefix();
return prefix + this.lastGeneratedID;
}
+ /**
+ * Returns the action with the given ID.
+ * @param id the ID
+ * @return the action or null if no action with this ID is stored
+ */
public AbstractAction get(String id) {
return (AbstractAction)this.actionRegistry.get(id);
}
+ /**
+ * Puts an action into the set and returns the normalized instance (another one if the given
+ * one is equal to another.
+ * @param action the action
+ * @return the action instance that should be used in place of the given one
+ */
public AbstractAction put(AbstractAction action) {
if (!action.hasID()) {
action.setID(generateNewID(action));
return effAction;
}
+ /**
+ * Clears the set.
+ */
public void clear() {
this.actionRegistry.clear();
}
- public AbstractAction normalize(AbstractAction action) {
+ private AbstractAction normalize(AbstractAction action) {
Iterator iter = this.actionRegistry.values().iterator();
while (iter.hasNext()) {
AbstractAction a = (AbstractAction)iter.next();
return action;
}
- public Iterator getActions() {
- return this.actionRegistry.values().iterator();
- }
-
- public void toSAX(ContentHandler handler) throws SAXException {
- // TODO Auto-generated method stub
-
- }
}
package org.apache.fop.render.intermediate.extensions;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
-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.util.XMLConstants;
-import org.apache.fop.util.XMLUtil;
-
/**
* This class is a bookmark element for use in the intermediate format.
*/
-public class Bookmark implements XMLizable, DocumentNavigationExtensionConstants {
+public class Bookmark {
private String title;
private boolean show;
}
}
- /** {@inheritDoc} */
- public void toSAX(ContentHandler handler) throws SAXException {
- AttributesImpl atts = new AttributesImpl();
- 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().isReference()) {
- getAction().toSAX(handler);
- }
- if (this.childBookmarks != null) {
- Iterator iter = this.childBookmarks.iterator();
- while (iter.hasNext()) {
- Bookmark b = (Bookmark)iter.next();
- b.toSAX(handler);
- }
- }
- handler.endElement(BOOKMARK.getNamespaceURI(),
- BOOKMARK.getLocalName(), BOOKMARK.getQName());
- }
-
}
package org.apache.fop.render.intermediate.extensions;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-import org.apache.xmlgraphics.util.XMLizable;
-
/**
* This class is the root of the bookmark tree for use in the intermediate format.
*/
-public class BookmarkTree implements XMLizable, DocumentNavigationExtensionConstants {
+public class BookmarkTree {
private List bookmarks = new java.util.ArrayList();
return Collections.unmodifiableList(this.bookmarks);
}
- /** {@inheritDoc} */
- public void toSAX(ContentHandler handler) throws SAXException {
- AttributesImpl atts = new AttributesImpl();
- handler.startElement(BOOKMARK_TREE.getNamespaceURI(),
- BOOKMARK_TREE.getLocalName(), BOOKMARK_TREE.getQName(), atts);
- Iterator iter = this.bookmarks.iterator();
- while (iter.hasNext()) {
- Bookmark b = (Bookmark)iter.next();
- b.toSAX(handler);
- }
- handler.endElement(BOOKMARK_TREE.getNamespaceURI(),
- BOOKMARK_TREE.getLocalName(), BOOKMARK_TREE.getQName());
- }
-
}
*/
public interface DocumentNavigationExtensionConstants {
- /** Namespace URI for the bookmark extension */
+ /** Namespace URI for the document navigation namespace */
String NAMESPACE = IFConstants.NAMESPACE + "/document-navigation";
+ /** Default Namespace prefix for the document navigation namespace */
+ String PREFIX = "nav";
+
/** the bookmark-tree element */
- QName BOOKMARK_TREE = new QName(NAMESPACE, "bookmark-tree");
+ QName BOOKMARK_TREE = new QName(NAMESPACE, PREFIX, "bookmark-tree");
/** the bookmark element */
- QName BOOKMARK = new QName(NAMESPACE, "bookmark");
+ QName BOOKMARK = new QName(NAMESPACE, PREFIX, "bookmark");
/** the named-destination element */
- QName NAMED_DESTINATION = new QName(NAMESPACE, "named-destination");
+ QName NAMED_DESTINATION = new QName(NAMESPACE, PREFIX, "named-destination");
/** the link element */
- QName LINK = new QName(NAMESPACE, "link");
+ QName LINK = new QName(NAMESPACE, PREFIX, "link");
/** the goto-xy element */
- QName GOTO_XY = new QName(NAMESPACE, "goto-xy");
+ QName GOTO_XY = new QName(NAMESPACE, PREFIX, "goto-xy");
/** the goto-uri element */
- QName GOTO_URI = new QName(NAMESPACE, "goto-uri");
+ QName GOTO_URI = new QName(NAMESPACE, PREFIX, "goto-uri");
- /** Attribute name for the action reference */
- String ACTION_REF = "action-ref";
}
*/
public class GoToXYAction extends AbstractAction implements DocumentNavigationExtensionConstants {
- private int pageIndex;
+ private int pageIndex = -1;
private Point targetLocation;
+ /**
+ * Creates a new instance with yet unknown location.
+ * @param id the identifier for this action
+ */
+ public GoToXYAction(String id) {
+ this(id, -1, null);
+ }
+
/**
* Creates a new instance.
- * @param pageIndex the page index (0-based) of the target page
- * @param targetLocation the absolute location on the page (coordinates in millipoints)
+ * @param id the identifier for this action
+ * @param pageIndex the index (0-based) of the target page, -1 if the page index is
+ * still unknown
+ * @param targetLocation the absolute location on the page (coordinates in millipoints),
+ * or null, if the position isn't known, yet
*/
public GoToXYAction(String id, int pageIndex, Point targetLocation) {
setID(id);
- this.pageIndex = pageIndex;
+ if (pageIndex < 0 && targetLocation != null) {
+ throw new IllegalArgumentException(
+ "Page index may not be null if target location is known!");
+ }
+ setPageIndex(pageIndex);
setTargetLocation(targetLocation);
}
+ /**
+ * Sets the index of the target page.
+ * @param pageIndex the index (0-based) of the target page
+ */
+ public void setPageIndex(int pageIndex) {
+ this.pageIndex = pageIndex;
+ }
+
/**
* Returns the page index of the target page.
* @return the page index (0-based)
this.targetLocation = location;
}
+ /** {@inheritDoc} */
+ public boolean isComplete() {
+ return (getPageIndex() >= 0) && (getTargetLocation() != null);
+ }
+
/** {@inheritDoc} */
public boolean isSame(AbstractAction other) {
if (other == null) {
if (getPageIndex() != otherAction.getPageIndex()) {
return false;
}
- if (getTargetLocation() == null && otherAction.getTargetLocation() != null) {
+ if (getTargetLocation() == null || otherAction.getTargetLocation() == null) {
return false;
}
if (!getTargetLocation().equals(otherAction.getTargetLocation())) {
setTargetLocation(new Point(0, 0));
}
AttributesImpl atts = new AttributesImpl();
- if (hasID()) {
+ if (isComplete()) {
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));
+ atts.addAttribute(null, "y", "y", XMLUtil.CDATA, Integer.toString(targetLocation.y));
+ } else {
+ atts.addAttribute(null, "idref", "idref", 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));
- atts.addAttribute(null, "y", "y", XMLUtil.CDATA, Integer.toString(targetLocation.y));
handler.startElement(GOTO_XY.getNamespaceURI(),
GOTO_XY.getLocalName(), GOTO_XY.getQName(), atts);
handler.endElement(GOTO_XY.getNamespaceURI(),
GOTO_XY.getLocalName(), GOTO_XY.getQName());
}
+ /** {@inheritDoc} */
+ public String toString() {
+ return "GoToXY: ID=" + getID()
+ + ", page=" + getPageIndex()
+ + ", loc=" + getTargetLocation() + ", "
+ + (isComplete() ? "complete" : "INCOMPLETE");
+ }
+
}
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";
+public class Link {
private AbstractAction action;
private Rectangle targetRect;
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());
- }
-
}
package org.apache.fop.render.intermediate.extensions;
-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.util.XMLConstants;
/**
* This class is a named destination element for use in the intermediate format.
*/
-public class NamedDestination implements XMLizable, DocumentNavigationExtensionConstants {
-
- /** Attribute name for the destination name */
- public static final String NAME = "name";
+public class NamedDestination {
private String name;
private 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, 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().isReference()) {
- getAction().toSAX(handler);
- }
- handler.endElement(NAMED_DESTINATION.getNamespaceURI(),
- NAMED_DESTINATION.getLocalName(), NAMED_DESTINATION.getQName());
- }
-
}
+++ /dev/null
-/*
- * 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());
- }
-
-}
/** {@inheritDoc} */
public void endDocument() throws IFException {
try {
- this.documentNavigationHandler.commit();
pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
pdfDoc.outputTrailer(this.outputStream);
/** {@inheritDoc} */
public void endPage() throws IFException {
try {
+ this.documentNavigationHandler.commit();
this.pdfDoc.registerObject(generator.getStream());
currentPage.setContents(generator.getStream());
PDFAnnotList annots = currentPage.getAnnotations();
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
-import java.util.List;
+import java.util.Map;
import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFactory;
+import org.apache.fop.pdf.PDFGoTo;
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;
private PDFDocumentHandler documentHandler;
- private ActionSet actionSet = new ActionSet();
- private List deferredLinks = new java.util.ArrayList();
+ private Map incompleteActions = new java.util.HashMap();
+ private Map completeActions = new java.util.HashMap();
/**
* Default constructor.
/** {@inheritDoc} */
public void renderNamedDestination(NamedDestination destination) throws IFException {
- PDFReference actionRef = getAction(destination.getAction());
+ PDFAction action = getAction(destination.getAction());
getPDFDoc().getFactory().makeDestination(
- destination.getName(), actionRef);
+ destination.getName(), action.makeReference());
}
/** {@inheritDoc} */
if (parent == null) {
parent = getPDFDoc().getOutlineRoot();
}
- PDFReference actionRef = getAction(bookmark.getAction());
+ PDFAction action = getAction(bookmark.getAction());
PDFOutline pdfOutline = getPDFDoc().getFactory().makeOutline(parent,
- bookmark.getTitle(), actionRef.toString(), bookmark.isShown());
+ bookmark.getTitle(), action.makeReference().toString(), bookmark.isShown());
Iterator iter = bookmark.getChildBookmarks().iterator();
while (iter.hasNext()) {
Bookmark b = (Bookmark)iter.next();
/** {@inheritDoc} */
public void renderLink(Link link) throws IFException {
- this.deferredLinks.add(link);
+ Rectangle targetRect = link.getTargetRect();
+ int pageHeight = documentHandler.currentPageRef.getPageDimension().height;
+ Rectangle2D targetRect2D = new Rectangle2D.Double(
+ targetRect.getMinX() / 1000.0,
+ (pageHeight - targetRect.getMinY() - targetRect.getHeight()) / 1000.0,
+ targetRect.getWidth() / 1000.0,
+ targetRect.getHeight() / 1000.0);
+ PDFAction pdfAction = getAction(link.getAction());
+ //makeLink() currently needs a PDFAction and not a reference
+ //TODO Revisit when PDFLink is converted to a PDFDictionary
+ PDFLink pdfLink = getPDFDoc().getFactory().makeLink(
+ targetRect2D, pdfAction);
+ documentHandler.currentPage.addAnnotation(pdfLink);
}
/**
* 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);
+ assert action.isComplete();
+ PDFAction pdfAction = (PDFAction)this.incompleteActions.remove(action.getID());
+ if (pdfAction == null) {
+ getAction(action);
+ } else if (pdfAction instanceof PDFGoTo) {
+ PDFGoTo pdfGoTo = (PDFGoTo)pdfAction;
+ updateTargetLocation(pdfGoTo, (GoToXYAction)action);
+ } else {
+ throw new UnsupportedOperationException(
+ "Action type not supported: " + pdfAction.getClass().getName());
+ }
}
- 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) {
+ private PDFAction getAction(AbstractAction action) {
+ PDFAction pdfAction = (PDFAction)this.completeActions.get(action.getID());
+ if (pdfAction != null) {
+ return pdfAction;
+ } else 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();
+ PDFGoTo pdfGoTo = new PDFGoTo(null);
+ getPDFDoc().assignObjectNumber(pdfGoTo);
+ if (action.isComplete()) {
+ updateTargetLocation(pdfGoTo, a);
+ } else {
+ this.incompleteActions.put(action.getID(), pdfGoTo);
+ }
+ return pdfGoTo;
} else if (action instanceof URIAction) {
URIAction u = (URIAction)action;
- PDFAction pdfAction = factory.getExternalAction(u.getURI(), u.isNewWindow());
+ assert u.isComplete();
+ PDFFactory factory = getPDFDoc().getFactory();
+ pdfAction = factory.getExternalAction(u.getURI(), u.isNewWindow());
getPDFDoc().registerObject(pdfAction);
- return pdfAction.makeReference();
+ this.completeActions.put(action.getID(), pdfAction);
+ return pdfAction;
} else {
throw new UnsupportedOperationException("Unsupported action type: "
+ action + " (" + action.getClass().getName() + ")");
}
}
+ private void updateTargetLocation(PDFGoTo pdfGoTo, GoToXYAction action) {
+ PageReference pageRef = this.documentHandler.getPageReference(action.getPageIndex());
+ //Convert target location from millipoints to points and adjust for different
+ //page origin
+ Point2D p2d = null;
+ p2d = new Point2D.Double(
+ action.getTargetLocation().x / 1000.0,
+ (pageRef.getPageDimension().height - action.getTargetLocation().y) / 1000.0);
+ String pdfPageRef = pageRef.getPageRef().toString();
+ pdfGoTo.setPageReference(pdfPageRef);
+ pdfGoTo.setPosition(p2d);
+
+ //Queue this object now that it's complete
+ getPDFDoc().addObject(pdfGoTo);
+ this.completeActions.put(action.getID(), pdfGoTo);
+ }
+
}
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
+import org.apache.xmlgraphics.util.QName;
+
/**
* This class is a delegating SAX ContentHandler which has the purpose to provide a few handy
* methods that make life easier when generating SAX events.
* @throws SAXException if a SAX exception occurs
*/
public void startElement(String localName) throws SAXException {
- getDelegateContentHandler().startElement(getMainNamespace(), localName, localName,
- EMPTY_ATTS);
+ startElement(localName, EMPTY_ATTS);
+ }
+
+ /**
+ * Convenience method to generate a startElement SAX event.
+ * @param qName the qualified name of the element
+ * @param atts the attributes
+ * @throws SAXException if a SAX exception occurs
+ */
+ public void startElement(QName qName, Attributes atts) throws SAXException {
+ getDelegateContentHandler().startElement(qName.getNamespaceURI(), qName.getLocalName(),
+ qName.getQName(), atts);
+ }
+
+ /**
+ * Convenience method to generate a startElement SAX event.
+ * @param qName the qualified name of the element
+ * @throws SAXException if a SAX exception occurs
+ */
+ public void startElement(QName qName) throws SAXException {
+ startElement(qName, EMPTY_ATTS);
}
/**
}
/**
- * Convenience method to generate an empty element.
+ * Convenience method to generate a startElement SAX event.
+ * @param qName the qualified name of the element
+ * @throws SAXException if a SAX exception occurs
+ */
+ public void endElement(QName qName) throws SAXException {
+ getDelegateContentHandler().endElement(qName.getNamespaceURI(), qName.getLocalName(),
+ qName.getQName());
+ }
+
+ /**
+ * Convenience method to generate an empty element with attributes.
* @param localName the local name of the element
* @param atts the attributes
* @throws SAXException if a SAX exception occurs
getDelegateContentHandler().endElement(getMainNamespace(), localName, localName);
}
+ /**
+ * Convenience method to generate an empty element with attributes.
+ * @param qName the qualified name of the element
+ * @param atts the attributes
+ * @throws SAXException if a SAX exception occurs
+ */
+ public void element(QName qName, Attributes atts) throws SAXException {
+ getDelegateContentHandler().startElement(qName.getNamespaceURI(), qName.getLocalName(),
+ qName.getQName(), atts);
+ getDelegateContentHandler().endElement(qName.getNamespaceURI(), qName.getLocalName(),
+ qName.getQName());
+ }
+
}