org.apache.fop.render.afp.extensions.AFPExtensionHandlerFactory\r
org.apache.fop.render.ps.extensions.PSExtensionHandlerFactory\r
org.apache.fop.fo.extensions.xmp.XMPContentHandlerFactory\r
+org.apache.fop.render.intermediate.extensions.BookmarkExtensionHandlerFactory\r
import java.awt.Color;
import java.awt.Dimension;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import org.apache.fop.Version;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.BlockViewport;
+import org.apache.fop.area.BookmarkData;
import org.apache.fop.area.CTM;
+import org.apache.fop.area.DestinationData;
import org.apache.fop.area.OffDocumentExtensionAttachment;
import org.apache.fop.area.OffDocumentItem;
import org.apache.fop.area.PageSequence;
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.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.Viewport;
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.Bookmark;
+import org.apache.fop.render.intermediate.extensions.BookmarkTree;
+import org.apache.fop.render.intermediate.extensions.GoToXYAction;
+import org.apache.fop.render.pdf.PDFEventProducer;
/**
* This renderer implementation is an adapter to the {@code IFPainter} interface. It is used
private Metadata documentMetadata;
+ /**
+ * Maps XSL-FO element IDs to their on-page XY-positions
+ * Must be used in conjunction with the page reference to fully specify the details
+ * of a "go-to" action.
+ */
+ 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();
+ // can't use a Set because PDFGoTo.equals returns true if the target is the same,
+ // even if the object number differs
+
+ private BookmarkTree bookmarkTree;
+
/**
* Main constructor
*/
painter.endPageSequence();
this.inPageSequence = false;
}
+ finishOpenGoTos();
+ if (this.bookmarkTree != null) {
+ painter.handleExtensionObject(this.bookmarkTree);
+ }
painter.endDocument();
} catch (IFException e) {
handleIFExceptionWithIOException(e);
}
+ idPositions.clear();
+ idGoTos.clear();
super.stopRenderer();
log.debug("Rendering finished.");
}
/** {@inheritDoc} */
public void processOffDocumentItem(OffDocumentItem odi) {
- if (odi instanceof OffDocumentExtensionAttachment) {
+ if (odi instanceof DestinationData) {
+ // render Destinations
+ renderDestination((DestinationData) odi);
+ } else if (odi instanceof BookmarkData) {
+ // render Bookmark-Tree
+ renderBookmarkTree((BookmarkData) odi);
+ } else if (odi instanceof OffDocumentExtensionAttachment) {
ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
renderXMPMetadata((XMPMetadata)attachment);
}
}
+ private void renderDestination(DestinationData dd) {
+ String targetID = dd.getIDRef();
+ if (targetID == null || targetID.length() == 0) {
+ throw new IllegalArgumentException("DestinationData must contain a ID reference");
+ }
+ PageViewport pv = dd.getPageViewport();
+ if (pv != null) {
+ GoToXYAction action = getGoToActionForID(targetID, pv.getPageIndex());
+ /*
+ pdfDoc.getFactory().makeDestination(
+ dd.getIDRef(), gt.makeReference());
+ */
+ } else {
+ //Warning already issued by AreaTreeHandler (debug level is sufficient)
+ log.debug("Unresolved destination item received: " + dd.getIDRef());
+ }
+ }
+
+ /**
+ * Renders a Bookmark-Tree object
+ * @param bookmarks the BookmarkData object containing all the Bookmark-Items
+ */
+ protected void renderBookmarkTree(BookmarkData bookmarks) {
+ assert this.bookmarkTree == null;
+ this.bookmarkTree = new BookmarkTree();
+ for (int i = 0; i < bookmarks.getCount(); i++) {
+ BookmarkData ext = bookmarks.getSubData(i);
+ Bookmark b = renderBookmarkItem(ext);
+ bookmarkTree.addBookmark(b);
+ }
+ }
+
+ private Bookmark renderBookmarkItem(BookmarkData bookmarkItem) {
+
+ String targetID = bookmarkItem.getIDRef();
+ if (targetID == null || targetID.length() == 0) {
+ throw new IllegalArgumentException("DestinationData must contain a ID reference");
+ }
+ GoToXYAction action = null;
+ PageViewport pv = bookmarkItem.getPageViewport();
+
+ if (pv != null) {
+ action = getGoToActionForID(targetID, pv.getPageIndex());
+ } else {
+ //Warning already issued by AreaTreeHandler (debug level is sufficient)
+ log.debug("Bookmark with IDRef \"" + targetID + "\" has a null PageViewport.");
+ }
+
+ Bookmark b = new Bookmark(
+ bookmarkItem.getBookmarkTitle(),
+ bookmarkItem.showChildItems(),
+ action);
+ for (int i = 0; i < bookmarkItem.getCount(); i++) {
+ b.addChildBookmark(renderBookmarkItem(bookmarkItem.getSubData(i)));
+ }
+ return b;
+ }
+
private void renderXMPMetadata(XMPMetadata metadata) {
this.documentMetadata = metadata.getMetadata();
}
+ private GoToXYAction getGoToActionForID(String targetID, int pageIndex) {
+ // Already a PDFGoTo present for this target? If not, create.
+ 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);
+ } else {
+ // Not complete yet, can't use getPDFGoTo:
+ action = new GoToXYAction(pageIndex, null);
+ unfinishedGoTos.add(action);
+ }
+ idGoTos.put(targetID, action);
+ }
+ return action;
+ }
+
+ private void finishOpenGoTos() {
+ int count = unfinishedGoTos.size();
+ if (count > 0) {
+ Point defaultPos = new Point(0, 0); // top-o-page
+ while (!unfinishedGoTos.isEmpty()) {
+ GoToXYAction action = (GoToXYAction)unfinishedGoTos.get(0);
+ noteGoToPosition(action, defaultPos);
+ }
+ PDFEventProducer eventProducer = PDFEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.nonFullyResolvedLinkTargets(this, count);
+ // dysfunctional if pageref is null
+ }
+ }
+
+ private void noteGoToPosition(GoToXYAction action, Point position) {
+ action.setTargetLocation(position);
+ unfinishedGoTos.remove(action);
+ }
+
+ private void noteGoToPosition(GoToXYAction action, PageViewport pv, Point position) {
+ noteGoToPosition(action, position);
+ }
+
+ private void saveAbsolutePosition(String id, PageViewport pv,
+ int relativeIPP, int relativeBPP, AffineTransform tf) {
+ Point position = new Point(relativeIPP, relativeBPP);
+ tf.transform(position, position);
+ idPositions.put(id, position);
+ // is there already a PDFGoTo waiting to be completed?
+ GoToXYAction action = (GoToXYAction)idGoTos.get(id);
+ if (action != null) {
+ noteGoToPosition(action, pv, position);
+ }
+ }
+
+ private void saveAbsolutePosition(String id, int relativeIPP, int relativeBPP) {
+ saveAbsolutePosition(id, this.currentPageViewport,
+ relativeIPP, relativeBPP, graphicContext.getTransform());
+ }
+
+ protected void saveBlockPosIfTargetable(Block block) {
+ String id = getTargetableID(block);
+ if (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.
+ int ipp = block.getXOffset();
+ int bpp = block.getYOffset() + block.getSpaceBefore();
+ int positioning = block.getPositioning();
+ if (!(positioning == Block.FIXED || positioning == Block.ABSOLUTE)) {
+ ipp += currentIPPosition;
+ bpp += currentBPPosition;
+ }
+ saveAbsolutePosition(id, currentPageViewport, ipp, bpp, graphicContext.getTransform());
+ }
+ }
+
+ private void saveInlinePosIfTargetable(InlineArea inlineArea) {
+ String id = getTargetableID(inlineArea);
+ if (id != null) {
+ int extraMarginBefore = 5000; // millipoints
+ int ipp = currentIPPosition;
+ int bpp = currentBPPosition + inlineArea.getOffset() - extraMarginBefore;
+ saveAbsolutePosition(id, ipp, bpp);
+ }
+ }
+
+ private String getTargetableID(Area area) {
+ String id = (String) area.getTrait(Trait.PROD_ID);
+ if (id == null || id.length() == 0
+ || !currentPageViewport.isFirstWithID(id)
+ || idPositions.containsKey(id)) {
+ return null;
+ } else {
+ return id;
+ }
+ }
+
/** {@inheritDoc} */
public void startPageSequence(PageSequence pageSequence) {
try {
painter.endDocumentHeader();
this.inPageSequence = true;
}
- //TODO Put the page-sequence's ID in the area tree
painter.startPageSequence(null);
} catch (IFException e) {
handleIFException(e);
restoreGraphicsState();
}
- /*
- protected void renderReferenceArea(Block block) {
- // TODO Auto-generated method stub
- }*/
+ /** {@inheritDoc} */
+ protected void renderInlineArea(InlineArea inlineArea) {
+ saveInlinePosIfTargetable(inlineArea);
+ super.renderInlineArea(inlineArea);
+ }
/** {@inheritDoc} */
protected void renderBlock(Block block) {
if (log.isTraceEnabled()) {
log.trace("renderBlock() " + block);
}
+ saveBlockPosIfTargetable(block);
super.renderBlock(block);
}
--- /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.apache.xmlgraphics.util.XMLizable;
+
+/**
+ * Abstract base class for document actions, like "go-to" actions with absolute page coordinates.
+ */
+public abstract class AbstractAction implements XMLizable {
+
+}
--- /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 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.XMLUtil;
+
+/**
+ * This class is a bookmark element for use in the intermediate format.
+ */
+public class Bookmark implements XMLizable, BookmarkExtensionConstants {
+
+ private String title;
+ private boolean show;
+ private List childBookmarks;
+ private AbstractAction action;
+
+ /**
+ * Creates a new bookmark.
+ * @param title the bookmark's title
+ * @param show true if the bookmark shall be shown, false for hidden
+ * @param action the action performed when the bookmark is clicked
+ */
+ public Bookmark(String title, boolean show, AbstractAction action) {
+ this.title = title;
+ this.show = show;
+ this.action = action;
+ }
+
+ /**
+ * Returns the bookmark's title.
+ * @return the title
+ */
+ public String getTitle() {
+ return this.title;
+ }
+
+ /**
+ * Indicates whether the bookmark shall be shown initially.
+ * @return true if it shall be shown
+ */
+ public boolean isShown() {
+ return this.show;
+ }
+
+ /**
+ * Returns the action performed when the bookmark is clicked.
+ * @return the action
+ */
+ public AbstractAction getAction() {
+ return this.action;
+ }
+
+ /**
+ * Sets the action performed when the bookmark is clicked.
+ * @param action the action
+ */
+ public void setAction(AbstractAction action) {
+ this.action = action;
+ }
+
+ /**
+ * Adds a child bookmark.
+ * @param bookmark the child bookmark
+ */
+ public void addChildBookmark(Bookmark bookmark) {
+ if (this.childBookmarks == null) {
+ this.childBookmarks = new java.util.ArrayList();
+ }
+ this.childBookmarks.add(bookmark);
+ }
+
+ /**
+ * Returns a list of child bookmarks.
+ * @return the child bookmarks
+ */
+ public List getChildBookmarks() {
+ if (this.childBookmarks == null) {
+ return Collections.EMPTY_LIST;
+ } else {
+ return Collections.unmodifiableList(this.childBookmarks);
+ }
+ }
+
+ /** {@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");
+ handler.startElement(BOOKMARK.getNamespaceURI(),
+ BOOKMARK.getLocalName(), BOOKMARK.getQName(), atts);
+ if (getAction() != null) {
+ 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());
+ }
+
+}
--- /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.apache.xmlgraphics.util.QName;
+
+import org.apache.fop.render.intermediate.IFConstants;
+
+/**
+ * Constants for the IF bookmark extension.
+ */
+public interface BookmarkExtensionConstants {
+
+ /** Namespace URI for the bookmark extension */
+ String NAMESPACE = IFConstants.NAMESPACE + "/bookmarks";
+
+ /** the bookmark-tree element */
+ QName BOOKMARK_TREE = new QName(NAMESPACE, "bookmark-tree");
+ /** the bookmark element */
+ QName BOOKMARK = new QName(NAMESPACE, "bookmark");
+ /** the goto-xy element */
+ QName GOTO_XY = new QName(NAMESPACE, "goto-xy");
+ /** the goto-uri element */
+ QName GOTO_URI = new QName(NAMESPACE, "goto-uri");
+
+}
--- /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 java.awt.Point;
+import java.util.Stack;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.XMLUtil;
+
+/**
+ * Factory for the ContentHandler that handles the IF bookmarks namespace.
+ */
+public class BookmarkExtensionHandlerFactory
+ implements ContentHandlerFactory, BookmarkExtensionConstants {
+
+ /** Logger instance */
+ protected static Log log = LogFactory.getLog(BookmarkExtensionHandlerFactory.class);
+
+ /** {@inheritDoc} */
+ public String[] getSupportedNamespaces() {
+ return new String[] {NAMESPACE};
+ }
+
+ /** {@inheritDoc} */
+ public ContentHandler createContentHandler() {
+ return new BookmarkExtensionHandler();
+ }
+
+ private static class BookmarkExtensionHandler extends DefaultHandler
+ implements ContentHandlerFactory.ObjectSource {
+
+ private StringBuffer content = new StringBuffer();
+ //private Attributes lastAttributes;
+ private Stack objectStack = new Stack();
+ private BookmarkTree bookmarkTree;
+
+ private ObjectBuiltListener listener;
+
+ /** {@inheritDoc} */
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ boolean handled = false;
+ if (NAMESPACE.equals(uri)) {
+ if (BOOKMARK_TREE.getLocalName().equals(localName)) {
+ if (bookmarkTree != null) {
+ throw new SAXException(localName + " must be the root element!");
+ }
+ bookmarkTree = new BookmarkTree();
+ objectStack.push(bookmarkTree);
+ } else if (BOOKMARK.getLocalName().equals(localName)) {
+ String title = attributes.getValue("title");
+ String s = attributes.getValue("starting-state");
+ boolean show = !"hide".equals(s);
+ Bookmark b = new Bookmark(title, show, null);
+ Object o = objectStack.peek();
+ if (o instanceof AbstractAction) {
+ AbstractAction action = (AbstractAction)objectStack.pop();
+ o = objectStack.peek();
+ ((Bookmark)o).setAction(action);
+ }
+ if (o instanceof BookmarkTree) {
+ ((BookmarkTree)o).addBookmark(b);
+ } else {
+ ((Bookmark)o).addChildBookmark(b);
+ }
+ objectStack.push(b);
+ } else if (GOTO_XY.getLocalName().equals(localName)) {
+ 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));
+ objectStack.push(action);
+ } else if (GOTO_URI.getLocalName().equals(localName)) {
+ String gotoURI = attributes.getValue("uri");
+ URIAction action = new URIAction(gotoURI);
+ objectStack.push(action);
+ } else {
+ throw new SAXException(
+ "Invalid element " + localName + " in namespace: " + uri);
+ }
+ handled = true;
+ }
+ if (!handled) {
+ if (PSExtensionAttachment.CATEGORY.equals(uri)) {
+ throw new SAXException("Unhandled element " + localName + " in namespace: "
+ + uri);
+ } else {
+ log.warn("Unhandled element " + localName + " in namespace: " + uri);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (NAMESPACE.equals(uri)) {
+ if (BOOKMARK_TREE.getLocalName().equals(localName)) {
+ //nop
+ } else if (BOOKMARK.getLocalName().equals(localName)) {
+ if (objectStack.peek() instanceof AbstractAction) {
+ AbstractAction action = (AbstractAction)objectStack.pop();
+ Bookmark b = (Bookmark)objectStack.pop();
+ b.setAction(action);
+ } else {
+ objectStack.pop();
+ }
+ }
+ }
+ content.setLength(0); // Reset text buffer (see characters())
+ }
+
+ /** {@inheritDoc} */
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ content.append(ch, start, length);
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws SAXException {
+ if (listener != null) {
+ listener.notifyObjectBuilt(getObject());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Object getObject() {
+ return bookmarkTree;
+ }
+
+ /** {@inheritDoc} */
+ public void setObjectBuiltListener(ObjectBuiltListener listener) {
+ this.listener = listener;
+ }
+ }
+
+}
--- /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 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, BookmarkExtensionConstants {
+
+ private List bookmarks = new java.util.ArrayList();
+
+ /**
+ * Constructs a new bookmark tree.
+ */
+ public BookmarkTree() {
+ //nop
+ }
+
+ /**
+ * Adds a new top-level bookmark.
+ * @param bookmark the bookmark
+ */
+ public void addBookmark(Bookmark bookmark) {
+ this.bookmarks.add(bookmark);
+ }
+
+ /**
+ * Returns a list of top-level bookmarks.
+ * @return the top-level bookmarks
+ */
+ public List getBookmarks() {
+ 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());
+ }
+
+}
--- /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 java.awt.Point;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.util.XMLUtil;
+
+/**
+ * Action class which represents a "go-to" action to an absolute coordinate on a page.
+ */
+public class GoToXYAction extends AbstractAction implements BookmarkExtensionConstants {
+
+ private int pageIndex;
+ private Point targetLocation;
+
+ /**
+ * 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)
+ */
+ public GoToXYAction(int pageIndex, Point targetLocation) {
+ this.pageIndex = pageIndex;
+ this.targetLocation = targetLocation;
+ }
+
+ /**
+ * Returns the page index of the target page.
+ * @return the page index (0-based)
+ */
+ public int getPageIndex() {
+ return this.pageIndex;
+ }
+
+ /**
+ * Returns the absolute coordinates of the target location on the page.
+ * @return the target location (coordinates in millipoints)
+ */
+ public Point getTargetLocation() {
+ return this.targetLocation;
+ }
+
+ /**
+ * Sets the absolute coordinates of the target location on the page.
+ * @param location the location (coordinates in millipoints)
+ */
+ public void setTargetLocation(Point location) {
+ this.targetLocation = location;
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ 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());
+ }
+
+}
--- /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;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.util.XMLUtil;
+
+/**
+ * Action class which represents a "URI" action, i.e. an action that will call up an external
+ * resource identified by a URI.
+ */
+public class URIAction extends AbstractAction implements BookmarkExtensionConstants {
+
+ private String uri;
+
+ /**
+ * Creates a new instance.
+ * @param uri the target URI
+ */
+ public URIAction(String uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Returns the target URI.
+ * @return the target URI
+ */
+ public String getURI() {
+ return this.uri;
+ }
+
+ /** {@inheritDoc} */
+ public void toSAX(ContentHandler handler) throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute(null, "uri", "uri", XMLUtil.CDATA, getURI());
+ handler.startElement(GOTO_URI.getNamespaceURI(),
+ GOTO_URI.getLocalName(), GOTO_URI.getQName(), atts);
+ handler.endElement(GOTO_URI.getNamespaceURI(),
+ GOTO_URI.getLocalName(), GOTO_URI.getQName());
+ }
+
+}
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
import java.io.IOException;
+import java.util.Iterator;
import java.util.Map;
import org.w3c.dom.Document;
import org.apache.fop.fonts.LazyFont;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
+import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFNumber;
+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.pdf.PDFTextUtil;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFPainter;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState;
+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.util.CharUtilities;
/**
/** the current annotation list to add annotations to */
protected PDFResourceContext currentContext;
- /**
- * Map of pages using the PageViewport as the key
- * this is used for prepared pages that cannot be immediately
- * rendered
- */
- protected Map pages;
-
/** 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;
+ /** Used for bookmarks/outlines. */
+ private Map pageReferences = new java.util.HashMap();
+
/**
* Default constructor.
*/
/** {@inheritDoc} */
public void endDocument() throws IFException {
try {
- //finishOpenGoTos();
-
pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
pdfDoc.outputTrailer(this.outputStream);
this.pdfDoc = null;
- this.pages = null;
-
- //pageReferences.clear();
pdfResources = null;
this.generator = null;
currentContext = null;
currentPage = null;
-
- //idPositions.clear();
- //idGoTos.clear();
} catch (IOException ioe) {
throw new IFException("I/O error in endDocument()", ioe);
}
pdfUtil.generatePageLabel(index, name);
currentPageRef = currentPage.referencePDF();
+ this.pageReferences.put(new Integer(index), new PageReference(currentPage, size));
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
}
}
+ 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 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) {
} else if (extension instanceof Metadata) {
XMPMetadata wrapper = new XMPMetadata(((Metadata)extension));
pdfUtil.renderXMPMetadata(wrapper);
+ } else if (extension instanceof BookmarkTree) {
+ renderBookmarkTree((BookmarkTree)extension);
} else {
- throw new UnsupportedOperationException(
- "Don't know how to handle extension object: " + extension);
+ log.warn("Don't know how to handle extension object: "
+ + extension + " (" + extension.getClass().getName());
+ }
+ }
+
+ private static final class PageReference {
+
+ private PDFReference pageRef;
+ private Dimension pageDimension;
+
+ private PageReference(PDFPage page, Dimension dim) {
+ this.pageRef = page.makeReference();
+ this.pageDimension = new Dimension(dim);
}
}
--- /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.intermediate;
+
+import org.w3c.dom.Document;
+
+/**
+ * Check interface for intermediate format checks.
+ */
+public interface IFCheck {
+
+ /**
+ * Called to perform the check.
+ * @param intermediate the intermediate format file as a DOM document
+ */
+ void check(Document intermediate);
+
+}
--- /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.intermediate;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.area.AreaTreeModel;
+import org.apache.fop.area.AreaTreeParser;
+import org.apache.fop.area.RenderPagesModel;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.layoutengine.EvalCheck;
+import org.apache.fop.layoutengine.TrueCheck;
+import org.apache.fop.render.intermediate.IFRenderer;
+import org.apache.fop.render.intermediate.IFSerializer;
+import org.apache.fop.util.DelegatingContentHandler;
+
+/**
+ * Does tests on the intermediate format.
+ */
+public class IFTester {
+
+ private static final Map IF_CHECK_CLASSES = new java.util.HashMap();
+
+ static {
+ IF_CHECK_CLASSES.put("true", TrueCheck.class);
+ IF_CHECK_CLASSES.put("eval", EvalCheck.class);
+ }
+
+ private FopFactory fopFactory = FopFactory.newInstance();
+
+ private SAXTransformerFactory tfactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ private File backupDir;
+
+ /**
+ * Main constructor
+ * @param backupDir an optional directory in which to write the serialized
+ * intermediate format file (may be null)
+ */
+ public IFTester(File backupDir) {
+ this.backupDir = backupDir;
+ }
+
+ /**
+ * Factory method to create IF checks from DOM elements.
+ * @param el DOM element to create the check from
+ * @return The newly create check
+ */
+ protected IFCheck createIFCheck(Element el) {
+ String name = el.getTagName();
+ Class clazz = (Class)IF_CHECK_CLASSES.get(name);
+ if (clazz != null) {
+ try {
+ Constructor c = clazz.getDeclaredConstructor(new Class[] {Node.class});
+ IFCheck instance = (IFCheck)c.newInstance(new Object[] {el});
+ return instance;
+ } catch (Exception e) {
+ throw new RuntimeException("Error while instantiating check '"
+ + name + "': " + e.getMessage());
+ }
+ } else {
+ throw new IllegalArgumentException("No check class found: " + name);
+ }
+ }
+
+ private Document createIF(Document areaTreeXML) throws TransformerException {
+ try {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+
+ IFRenderer ifRenderer = new IFRenderer();
+ ifRenderer.setUserAgent(ua);
+
+ IFSerializer serializer = new IFSerializer();
+ DOMResult result = new DOMResult();
+ serializer.setResult(result);
+ ifRenderer.setPainter(serializer);
+
+ ua.setRendererOverride(ifRenderer);
+ FontInfo fontInfo = new FontInfo();
+ //Construct the AreaTreeModel that will received the individual pages
+ final AreaTreeModel treeModel = new RenderPagesModel(ua,
+ null, fontInfo, null);
+
+ //Iterate over all intermediate files
+ AreaTreeParser parser = new AreaTreeParser();
+ ContentHandler handler = parser.getContentHandler(treeModel, ua);
+
+ DelegatingContentHandler proxy = new DelegatingContentHandler() {
+
+ public void endDocument() throws SAXException {
+ super.endDocument();
+ //Signal the end of the processing.
+ //The renderer can finalize the target document.
+ treeModel.endDocument();
+ }
+
+ };
+ proxy.setDelegateContentHandler(handler);
+
+ Transformer transformer = tfactory.newTransformer();
+ transformer.transform(new DOMSource(areaTreeXML), new SAXResult(proxy));
+
+ return (Document)result.getNode();
+ } catch (Exception e) {
+ throw new TransformerException(
+ "Error while generating intermediate format file: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Runs the intermediate format checks.
+ * @param testFile the original test file
+ * @param checksRoot the root element containing the IF checks
+ * @param areaTreeXML the area tree XML
+ * @throws TransformerException if an error occurs while transforming the content
+ */
+ public void doIFChecks(File testFile, Element checksRoot, Document areaTreeXML)
+ throws TransformerException {
+ Document ifDocument = createIF(areaTreeXML);
+ if (this.backupDir != null) {
+ Transformer transformer = tfactory.newTransformer();
+ Source src = new DOMSource(ifDocument);
+ File targetFile = new File(this.backupDir, testFile.getName() + ".if.xml");
+ Result res = new StreamResult(targetFile);
+ transformer.transform(src, res);
+ }
+
+ //First create check before actually running them
+ List checks = new java.util.ArrayList();
+ NodeList nodes = checksRoot.getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (node instanceof Element) {
+ checks.add(createIFCheck((Element)node));
+ }
+ }
+
+ if (checks.size() == 0) {
+ throw new RuntimeException("No checks are available!");
+ }
+
+ //Run the actual tests now that we know that the checks themselves are ok
+ doIFChecks(checks, ifDocument);
+ }
+
+ private void doIFChecks(List checks, Document ifDocument) {
+ Iterator i = checks.iterator();
+ while (i.hasNext()) {
+ IFCheck check = (IFCheck)i.next();
+ check.check(ifDocument);
+ }
+ }
+
+}
import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.PrefixResolverDefault;
import org.apache.xpath.XPathAPI;
import org.apache.xpath.objects.XObject;
-import org.w3c.dom.Node;
+
+import org.apache.fop.intermediate.IFCheck;
/**
* Simple check that requires an XPath expression to evaluate to true.
*/
-public class EvalCheck implements LayoutEngineCheck {
+public class EvalCheck implements LayoutEngineCheck, IFCheck {
private String expected;
private String xpath;
if (nd != null) {
this.tolerance = Double.parseDouble(nd.getNodeValue());
}
- this.prefixResolver = new PrefixResolverDefault(node);
+ this.prefixResolver = new MyPrefixResolver(new PrefixResolverDefault(node));
}
- /** @see org.apache.fop.layoutengine.LayoutEngineCheck */
+ /** {@inheritDoc} */
public void check(LayoutResult result) {
+ doCheck(result.getAreaTree());
+ }
+
+ /** {@inheritDoc} */
+ public void check(Document intermediate) {
+ doCheck(intermediate);
+ }
+
+ private void doCheck(Document doc) {
XObject res;
try {
- res = XPathAPI.eval(result.getAreaTree(), xpath, prefixResolver);
+ res = XPathAPI.eval(doc, xpath, prefixResolver);
} catch (TransformerException e) {
throw new RuntimeException("XPath evaluation failed: " + e.getMessage());
}
}
}
- /** @see java.lang.Object#toString() */
+ /** {@inheritDoc} */
public String toString() {
return "XPath: " + xpath;
}
+ private static class MyPrefixResolver implements PrefixResolver {
+
+ private PrefixResolver delegate;
+
+ public MyPrefixResolver(PrefixResolver delegate) {
+ this.delegate = delegate;
+ }
+
+ /** {@inheritDoc} */
+ public String getBaseIdentifier() {
+ String s = delegate.getBaseIdentifier();
+ System.out.println(s);
+ return s;
+ }
+
+ /** {@inheritDoc} */
+ public String getNamespaceForPrefix(String prefix) {
+ String s = delegate.getNamespaceForPrefix(prefix);
+ System.out.println(s);
+ return s;
+ }
+
+ /** {@inheritDoc} */
+ public String getNamespaceForPrefix(String prefix, Node context) {
+ String s = delegate.getNamespaceForPrefix(prefix, context);
+ System.out.println(s);
+ return s;
+ }
+
+ /** {@inheritDoc} */
+ public boolean handlesNullPrefixes() {
+ return delegate.handlesNullPrefixes();
+ }
+
+ }
+
}
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FormattingResults;
+import org.apache.fop.intermediate.IFTester;
import org.apache.fop.layoutmgr.ElementListObserver;
import org.apache.fop.render.xml.XMLRenderer;
import org.apache.fop.util.ConsoleEventListenerForTests;
*/
public class LayoutEngineTester {
- private static final Map CHECK_CLASSES = new java.util.HashMap();
+ private static final Map AT_CHECK_CLASSES = new java.util.HashMap();
// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private Templates testcase2checks;
private File areaTreeBackupDir;
+ private IFTester ifTester;
static {
- CHECK_CLASSES.put("true", TrueCheck.class);
- CHECK_CLASSES.put("eval", EvalCheck.class);
- CHECK_CLASSES.put("element-list", ElementListCheck.class);
- CHECK_CLASSES.put("result", ResultCheck.class);
+ AT_CHECK_CLASSES.put("true", TrueCheck.class);
+ AT_CHECK_CLASSES.put("eval", EvalCheck.class);
+ AT_CHECK_CLASSES.put("element-list", ElementListCheck.class);
+ AT_CHECK_CLASSES.put("result", ResultCheck.class);
}
/**
this.areaTreeBackupDir = areaTreeBackupDir;
fopFactory.getFontManager().setBase14KerningEnabled(false);
fopFactoryWithBase14Kerning.getFontManager().setBase14KerningEnabled(true);
+ this.ifTester = new IFTester(areaTreeBackupDir);
}
private Templates getTestcase2FOStylesheet() throws TransformerConfigurationException {
}
/**
- * Factory method to create checks from DOM elements.
+ * Factory method to create AT checks from DOM elements.
* @param el DOM element to create the check from
* @return The newly create check
*/
- protected LayoutEngineCheck createCheck(Element el) {
+ protected LayoutEngineCheck createATCheck(Element el) {
String name = el.getTagName();
- Class clazz = (Class)CHECK_CLASSES.get(name);
+ Class clazz = (Class)AT_CHECK_CLASSES.get(name);
if (clazz != null) {
try {
Constructor c = clazz.getDeclaredConstructor(new Class[] {Node.class});
}
}
+
/**
- * Perform all checks on the area tree.
+ * Perform all checks on the area tree and, optionally, on the intermediate format.
* @param testFile Test case XML file
* @param result The layout results
* @throws TransformerException if a problem occurs in XSLT/JAXP
DOMResult res = new DOMResult();
transformer.transform(src, res);
- List checks = new java.util.ArrayList();
Document doc = (Document)res.getNode();
- NodeList nodes = doc.getDocumentElement().getChildNodes();
+ Element root = doc.getDocumentElement();
+
+ Element atChecks = (Element)root.getElementsByTagName("at-checks").item(0);
+ doATChecks(atChecks, result);
+
+ //IF tests only when checks are available
+ NodeList nodes;
+ nodes = root.getElementsByTagName("if-checks");
+ if (nodes.getLength() > 0) {
+ Element ifChecks = (Element)nodes.item(0);
+ ifTester.doIFChecks(testFile, ifChecks, result.getAreaTree());
+ }
+ }
+
+ private void doATChecks(Element checksRoot, LayoutResult result) {
+ //First create check before actually running them
+ List checks = new java.util.ArrayList();
+ NodeList nodes = checksRoot.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
- checks.add(createCheck((Element)node));
+ checks.add(createATCheck((Element)node));
}
}
if (checks.size() == 0) {
throw new RuntimeException("No checks are available!");
}
+
+ //Run the actual tests now that we know that the checks themselves are ok
+ doATChecks(checks, result);
+ }
+
+ private void doATChecks(List checks, LayoutResult result) {
Iterator i = checks.iterator();
while (i.hasNext()) {
LayoutEngineCheck check = (LayoutEngineCheck)i.next();
import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.PrefixResolverDefault;
import org.apache.xpath.XPathAPI;
import org.apache.xpath.objects.XBoolean;
import org.apache.xpath.objects.XObject;
-import org.w3c.dom.Node;
+
+import org.apache.fop.intermediate.IFCheck;
/**
* Simple check that requires an XPath expression to evaluate to true.
*/
-public class TrueCheck implements LayoutEngineCheck {
+public class TrueCheck implements LayoutEngineCheck, IFCheck {
private String xpath;
private String failureMessage;
this.prefixResolver = new PrefixResolverDefault(node);
}
- /** @see org.apache.fop.layoutengine.LayoutEngineCheck */
+ /** {@inheritDoc} */
public void check(LayoutResult result) {
+ doCheck(result.getAreaTree());
+ }
+
+ /** {@inheritDoc} */
+ public void check(Document intermediate) {
+ doCheck(intermediate);
+ }
+
+ private void doCheck(Document doc) {
XObject res;
try {
- res = XPathAPI.eval(result.getAreaTree(), xpath, prefixResolver);
+ res = XPathAPI.eval(doc, xpath, prefixResolver);
} catch (TransformerException e) {
throw new RuntimeException("XPath evaluation failed: " + e.getMessage());
}
}
- /** @see java.lang.Object#toString() */
+ /** {@inheritDoc} */
public String toString() {
return "XPath: " + xpath;
}
<fo:bookmark-title>Section 2</fo:bookmark-title>
</fo:bookmark>
</fo:bookmark>
+ <fo:bookmark internal-destination="bc">
+ <fo:bookmark-title>Fixed Block Container</fo:bookmark-title>
+ </fo:bookmark>
</fo:bookmark-tree>
<fo:page-sequence id="page-sequence" master-reference="normal" white-space-collapse="true">
<fo:flow flow-name="xsl-region-body">
<fo:block>Blah blah bla.</fo:block>
<fo:block id="chapter2-sec2" font-weight="bold">Section 2</fo:block>
<fo:block>Blah blah bla.</fo:block>
+ <fo:block-container absolute-position="fixed" left="3in" top="3in" width="1.5in" height="1in">
+ <fo:block id="bc">
+ Text in a block-container.
+ </fo:block>
+ </fo:block-container>"
</fo:flow>
</fo:page-sequence>
</fo:root>
<eval expected="(P2,chapter2)" xpath="//bookmarkTree/bookmark[2]/@internal-link"/>
<eval expected="(P2,chapter2-sec1)" xpath="//bookmarkTree/bookmark[2]/bookmark[1]/@internal-link"/>
<eval expected="(P2,chapter2-sec2)" xpath="//bookmarkTree/bookmark[2]/bookmark[2]/@internal-link"/>
-
</checks>
+ <if-checks xmlns:bm="http://xmlgraphics.apache.org/fop/intermediate/bookmarks">
+ <eval expected="show" xpath="//bm:bookmark-tree/bm:bookmark[1]/@starting-state"/>
+ <eval expected="Chapter 1" xpath="//bm:bookmark-tree/bm:bookmark[1]/@title"/>
+ <eval expected="0" xpath="//bm:bookmark-tree/bm:bookmark[1]/bm:goto-xy/@page-index"/>
+ <eval expected="20000" xpath="//bm:bookmark-tree/bm:bookmark[1]/bm:goto-xy/@x"/>
+ <eval expected="20000" xpath="//bm:bookmark-tree/bm:bookmark[1]/bm:goto-xy/@y"/>
+
+ <eval expected="hide" xpath="//bm:bookmark-tree/bm:bookmark[2]/@starting-state"/>
+ <eval expected="Chapter 2" xpath="//bm:bookmark-tree/bm:bookmark[2]/@title"/>
+ <eval expected="1" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:goto-xy/@page-index"/>
+ <eval expected="20000" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:goto-xy/@x"/>
+ <eval expected="20000" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:goto-xy/@y"/>
+
+ <eval expected="show" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:bookmark[1]/@starting-state"/>
+ <eval expected="Section 1" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:bookmark[1]/@title"/>
+ <eval expected="1" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:bookmark[1]/bm:goto-xy/@page-index"/>
+ <eval expected="20000" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:bookmark[1]/bm:goto-xy/@x"/>
+ <eval expected="51680" xpath="//bm:bookmark-tree/bm:bookmark[2]/bm:bookmark[1]/bm:goto-xy/@y"/>
+
+ <eval expected="show" xpath="//bm:bookmark-tree/bm:bookmark[3]/@starting-state"/>
+ <eval expected="Fixed Block Container" xpath="//bm:bookmark-tree/bm:bookmark[3]/@title"/>
+ <eval expected="1" xpath="//bm:bookmark-tree/bm:bookmark[3]/bm:goto-xy/@page-index"/>
+ <eval expected="216000" xpath="//bm:bookmark-tree/bm:bookmark[3]/bm:goto-xy/@x"/>
+ <eval expected="216000" xpath="//bm:bookmark-tree/bm:bookmark[3]/bm:goto-xy/@y"/>
+ </if-checks>
</testcase>
<xsl:variable name="basic-checks" select="document('basic-checks.xml')/checks/*" />
<xsl:template match="testcase">
- <xsl:apply-templates select="checks" />
+ <checks>
+ <xsl:apply-templates select="checks"/>
+ <xsl:apply-templates select="if-checks"/>
+ </checks>
</xsl:template>
<xsl:template match="checks">
- <checks>
+ <at-checks>
<xsl:copy-of select="$basic-checks" />
<xsl:copy-of select="*" />
- </checks>
+ </at-checks>
</xsl:template>
+<xsl:template match="if-checks">
+ <if-checks>
+ <xsl:copy-of select="*"/>
+ </if-checks>
+</xsl:template>
+
<xsl:template match="text()" />
</xsl:stylesheet>