FOP-3048: Resolve links across IF files

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1897194 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fop-core/src/main/java/org/apache/fop/area/RenderPagesModel.java b/fop-core/src/main/java/org/apache/fop/area/RenderPagesModel.java
index b94a32b..91d8d4e 100644
--- a/fop-core/src/main/java/org/apache/fop/area/RenderPagesModel.java
+++ b/fop-core/src/main/java/org/apache/fop/area/RenderPagesModel.java
@@ -191,15 +191,6 @@
     protected void renderPage(PageViewport pageViewport) {
         try {
             renderer.renderPage(pageViewport);
-            if (!pageViewport.isResolved()) {
-                String[] idrefs = pageViewport.getIDRefs();
-                for (String idref : idrefs) {
-                    AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
-                            renderer.getUserAgent().getEventBroadcaster());
-                    eventProducer.unresolvedIDReferenceOnPage(this,
-                            pageViewport.getPageNumberString(), idref);
-                }
-            }
         } catch (Exception e) {
             AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
                     renderer.getUserAgent().getEventBroadcaster());
diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java
index 3745da0..a75e61c 100644
--- a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFParser.java
@@ -58,6 +58,7 @@
 import org.apache.fop.fo.extensions.InternalElementMapping;
 import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants;
 import org.apache.fop.render.intermediate.extensions.DocumentNavigationHandler;
+import org.apache.fop.render.intermediate.extensions.GoToXYAction;
 import org.apache.fop.traits.BorderProps;
 import org.apache.fop.traits.RuleStyle;
 import org.apache.fop.util.ColorUtil;
@@ -164,6 +165,7 @@
 
         private Map<String, StructureTreeElement> structureTreeElements
                 = new HashMap<String, StructureTreeElement>();
+        private Map<String, GoToXYAction> unresolvedIds = new HashMap<>();
 
         private class StructureTreeHandler extends DefaultHandler {
 
@@ -308,12 +310,24 @@
                                     .noStructureTreeInXML(this);
                         }
                         handled = startIFElement(localName, attributes);
+
+                        if (EL_ID.equals(localName) && documentHandler.getDocumentNavigationHandler() != null) {
+                            if (this.navParser == null) {
+                                this.navParser = new DocumentNavigationHandler(
+                                        documentHandler.getDocumentNavigationHandler(),
+                                        structureTreeElements, unresolvedIds);
+                            }
+                            delegate = this.navParser;
+                            delegateDepth++;
+                            delegate.startDocument();
+                            delegate.startElement(uri, localName, qName, attributes);
+                        }
                     }
                 } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) {
                     if (this.navParser == null) {
                         this.navParser = new DocumentNavigationHandler(
                                 this.documentHandler.getDocumentNavigationHandler(),
-                                        structureTreeElements);
+                                        structureTreeElements, unresolvedIds);
                     }
                     delegate = this.navParser;
                     delegateDepth++;
@@ -410,6 +424,9 @@
                     ElementHandler elementHandler = elementHandlers.get(localName);
                     if (elementHandler != null) {
                         try {
+                            if (elementHandler instanceof DocumentHandler) {
+                                addUnresolvedIds();
+                            }
                             elementHandler.endElement();
                         } catch (IFException ife) {
                             handleIFException(ife);
@@ -425,6 +442,14 @@
             }
         }
 
+        private void addUnresolvedIds() throws IFException {
+            for (GoToXYAction action : unresolvedIds.values()) {
+                if (action != null) {
+                    documentHandler.getDocumentNavigationHandler().addResolvedAction(action);
+                }
+            }
+        }
+
         // ============== Element handlers for the intermediate format =============
 
         private interface ElementHandler {
diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java
index 5a401b1..da010d2 100644
--- a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -68,6 +68,7 @@
 import org.apache.fop.area.RegionViewport;
 import org.apache.fop.area.Trait;
 import org.apache.fop.area.inline.AbstractTextArea;
+import org.apache.fop.area.inline.BasicLinkArea;
 import org.apache.fop.area.inline.ForeignObject;
 import org.apache.fop.area.inline.Image;
 import org.apache.fop.area.inline.InlineArea;
@@ -95,7 +96,6 @@
 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;
 
@@ -427,9 +427,6 @@
                 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
         }
     }
@@ -981,6 +978,12 @@
             action.setStructureTreeElement(structElem);
             Link link = new Link(action, ipRect);
             this.deferredLinks.add(link);
+        } else if (ip instanceof BasicLinkArea) {
+            BasicLinkArea linkArea = (BasicLinkArea) ip;
+            String id = linkArea.getResolver().getIDRefs()[0];
+            action = getGoToActionForID(id, -1);
+            Link link = new Link(action, ipRect);
+            this.deferredLinks.add(link);
         }
     }
 
diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
index 6974370..f654ef3 100644
--- a/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
+++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
@@ -38,6 +38,8 @@
 import org.apache.fop.render.intermediate.PageIndexContext;
 import org.apache.fop.util.XMLUtil;
 
+import static org.apache.fop.render.intermediate.IFConstants.EL_ID;
+
 /**
  * ContentHandler that handles the IF document navigation namespace.
  */
@@ -46,9 +48,11 @@
 
     /** Logger instance */
     protected static final Log log = LogFactory.getLog(DocumentNavigationHandler.class);
+    private static final String NAME = "name";
 
     private StringBuffer content = new StringBuffer();
     private Stack objectStack = new Stack();
+    private Map<String, GoToXYAction> unresolvedIds;
 
     private IFDocumentNavigationHandler navHandler;
 
@@ -62,16 +66,27 @@
      * @param structureTreeElements the elements representing the structure of the document
      */
     public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler,
-            Map<String, StructureTreeElement> structureTreeElements) {
+            Map<String, StructureTreeElement> structureTreeElements, Map<String, GoToXYAction> unresolvedIds) {
         this.navHandler = navHandler;
         assert structureTreeElements != null;
         this.structureTreeElements = structureTreeElements;
+        this.unresolvedIds = unresolvedIds;
     }
 
     /** {@inheritDoc} */
     public void startElement(String uri, String localName, String qName, Attributes attributes)
             throws SAXException {
         boolean handled = false;
+        if (EL_ID.equals(localName)) {
+            String idref = attributes.getValue(NAME);
+            if (unresolvedIds.containsKey(idref)) {
+                GoToXYAction action = new GoToXYAction(idref);
+                action.setPageIndex(navHandler.getPageIndex());
+                action.setTargetLocation(new Point(0, 0));
+                unresolvedIds.put(idref, action);
+            }
+            handled = true;
+        }
         if (NAMESPACE.equals(uri)) {
             if (BOOKMARK_TREE.getLocalName().equals(localName)) {
                 if (!objectStack.isEmpty()) {
@@ -124,6 +139,7 @@
                     final Point location;
                     if (pageIndex < 0) {
                         location = null;
+                        unresolvedIds.put(id, null);
                     } else {
                         if (hasNavigation() && !inBookmark() && pageIndexRelative >= 0) {
                             int currentPageIndex = navHandler.getPageIndex();
@@ -136,6 +152,7 @@
                         final int y = XMLUtil
                                 .getAttributeAsInt(attributes, "y");
                         location = new Point(x, y);
+                        unresolvedIds.remove(id);
                     }
                     action = new GoToXYAction(id, pageIndex, location,
                             new PageIndexRelative(pageIndex, pageIndexRelative));
diff --git a/fop-core/src/test/java/org/apache/fop/events/EventProcessingTestCase.java b/fop-core/src/test/java/org/apache/fop/events/EventProcessingTestCase.java
index 66ec431..5e96883 100644
--- a/fop-core/src/test/java/org/apache/fop/events/EventProcessingTestCase.java
+++ b/fop-core/src/test/java/org/apache/fop/events/EventProcessingTestCase.java
@@ -106,12 +106,6 @@
     }
 
     @Test
-    public void testArea() throws Exception {
-        doTest("area.fo",
-                AreaEventProducer.class.getName() + ".unresolvedIDReferenceOnPage");
-    }
-
-    @Test
     public void testBookmarks() throws Exception {
         doTest("bookmarks.fo",
                 AreaEventProducer.class.getName() + ".unresolvedIDReference");
diff --git a/fop-core/src/test/java/org/apache/fop/render/extensions/DocumentNavigationHandlerTestCase.java b/fop-core/src/test/java/org/apache/fop/render/extensions/DocumentNavigationHandlerTestCase.java
index 327f31a..1b8efd1 100644
--- a/fop-core/src/test/java/org/apache/fop/render/extensions/DocumentNavigationHandlerTestCase.java
+++ b/fop-core/src/test/java/org/apache/fop/render/extensions/DocumentNavigationHandlerTestCase.java
@@ -23,6 +23,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -30,7 +31,13 @@
 import java.util.Iterator;
 import java.util.List;
 
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
 import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -45,11 +52,15 @@
 import org.apache.fop.accessibility.StructureTreeElement;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.pdf.PDFLinearizationTestCase;
 import org.apache.fop.pdf.PDFVTTestCase;
 import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
 import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFParser;
+import org.apache.fop.render.intermediate.IFUtil;
 import org.apache.fop.render.intermediate.extensions.AbstractAction;
 import org.apache.fop.render.intermediate.extensions.Bookmark;
 import org.apache.fop.render.intermediate.extensions.BookmarkTree;
@@ -81,7 +92,7 @@
             }
         };
         DocumentNavigationHandler navigationHandler = new DocumentNavigationHandler(pdfDocumentNavigationHandler,
-                new HashMap<String, StructureTreeElement>());
+                new HashMap<String, StructureTreeElement>(), new HashMap<String, GoToXYAction>());
         QName xy = DocumentNavigationExtensionConstants.GOTO_XY;
         Attributes attributes = mock(Attributes.class);
         when(attributes.getValue("page-index")).thenReturn("0");
@@ -96,6 +107,28 @@
     }
 
     @Test
+    public void testGotoXYMergedIF() throws Exception {
+        InputStream ifXml = getClass().getResourceAsStream("link.if.xml");
+        ByteArrayOutputStream pdf = iFToPDF(ifXml);
+        Assert.assertTrue(pdf.toString().contains("/S /GoTo"));
+    }
+
+    private ByteArrayOutputStream iFToPDF(InputStream is) throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        FOUserAgent userAgent = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent();
+        Transformer transformer = TransformerFactory.newInstance().newTransformer();
+        Source src = new StreamSource(is);
+        IFDocumentHandler documentHandler
+                = userAgent.getRendererFactory().createDocumentHandler(userAgent, MimeConstants.MIME_PDF);
+        documentHandler.setResult(new StreamResult(out));
+        IFUtil.setupFonts(documentHandler);
+        IFParser parser = new IFParser();
+        Result res = new SAXResult(parser.getContentHandler(documentHandler, userAgent));
+        transformer.transform(src, res);
+        return out;
+    }
+
+    @Test
     public void testGotoXYPrevousPage() throws SAXException, IFException, IOException {
         FOUserAgent ua = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent();
         PDFDocumentHandler documentHandler = new PDFDocumentHandler(new IFContext(ua));
@@ -116,7 +149,7 @@
             }
         };
         DocumentNavigationHandler navigationHandler = new DocumentNavigationHandler(pdfDocumentNavigationHandler,
-                new HashMap<String, StructureTreeElement>());
+                new HashMap<String, StructureTreeElement>(), new HashMap<String, GoToXYAction>());
         QName xy = DocumentNavigationExtensionConstants.GOTO_XY;
         Attributes attributes = mock(Attributes.class);
         when(attributes.getValue("page-index")).thenReturn("0");
@@ -150,7 +183,7 @@
 
         PDFDocumentNavigationHandler pdfDocumentNavigationHandler = new PDFDocumentNavigationHandler(documentHandler);
         DocumentNavigationHandler navigationHandler = new DocumentNavigationHandler(pdfDocumentNavigationHandler,
-                new HashMap<String, StructureTreeElement>());
+                new HashMap<String, StructureTreeElement>(), new HashMap<String, GoToXYAction>());
         QName xy = DocumentNavigationExtensionConstants.GOTO_XY;
 
         Attributes attributes = mock(Attributes.class);
@@ -199,7 +232,7 @@
             }
         };
         DocumentNavigationHandler navigationHandler = new DocumentNavigationHandler(pdfDocumentNavigationHandler,
-                new HashMap<String, StructureTreeElement>());
+                new HashMap<String, StructureTreeElement>(), new HashMap<String, GoToXYAction>());
         Attributes attributes = mock(Attributes.class);
         when(attributes.getValue("page-index")).thenReturn("0");
         when(attributes.getValue("x")).thenReturn("0");