svn commit: r1780923 - in /xmlgraphics/fop/trunk/fop-core/src: main/java/org/apache/fop/pdf/PDFFactory.java main/java/org/apache/fop/pdf/PDFFileSpec.java main/java/org/apache/fop/pdf/PDFLaunch.java test/java/org/apache/fop/pdf/PDFLinkTestCase.java

[email protected]
Newsgroups gmane.text.xml.fop.cvs
Message-ID <[email protected]>
Author: ssteiner
Date: Mon Jan 30 15:24:20 2017
New Revision: 1780923

URL: http://svn.apache.org/viewvc?rev=1780923&view=rev
Log:
FOP-2676: basic-link external-destination does not work for file URI with spaces

Added:
    xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java   (with props)
Modified:
    xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java
    xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java
    xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java

Modified: xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java?rev=1780923&r1=1780922&r2=1780923&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java (original)
+++ xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java Mon Jan 30 15:24:20 2017
@@ -25,6 +25,8 @@ import java.awt.geom.Rectangle2D;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.text.DecimalFormat;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -548,8 +550,6 @@ public class PDFFactory {
         return link;
     }
 
-    private static final String EMBEDDED_FILE = "embedded-file:";
-
     /**
      * Create/find and return the appropriate external PDFAction according to the target
      *
@@ -560,39 +560,113 @@ public class PDFFactory {
      * @return the PDFAction thus created or found
      */
     public PDFAction getExternalAction(String target, boolean newWindow) {
+        URI uri = getTargetUri(target);
+        if (uri != null) {
+            String scheme = uri.getScheme();
+            String filename = uri.getPath();
+            if (filename == null) {
+                filename = uri.getSchemeSpecificPart();
+            }
+            if (scheme == null) {
+                return new PDFUri(uri.toASCIIString());
+            } else if (scheme.equalsIgnoreCase("embedded-file")) {
+                return getActionForEmbeddedFile(filename, newWindow);
+            } else if (scheme.equalsIgnoreCase("file")) {
+                if (filename.startsWith("//")) {
+                    filename = filename.replace("/", "\\");
+                } else if (filename.matches("^/[A-z]:/.*")) {
+                    filename = filename.substring(1);
+                }
+                if (filename.toLowerCase().endsWith(".pdf")) {
+                    int page = -1;
+                    String dest = null;
+                    String fragment = uri.getFragment();
+                    if (fragment != null) {
+                        String fragmentLo = fragment.toLowerCase();
+                        if (fragmentLo.startsWith("page=")) {
+                            page = Integer.parseInt(fragmentLo.substring(5));
+                        } else if (fragmentLo.startsWith("dest=")) {
+                            dest = fragment.substring(5);
+                        }
+                    }
+                    return getGoToPDFAction(filename, dest, page, newWindow);
+                } else {
+                    if (uri.getQuery() != null || uri.getFragment() != null) {
+                        return new PDFUri(uri.toASCIIString());
+                    } else {
+                        return getLaunchAction(filename, newWindow);
+                    }
+                }
+            } else {
+                return new PDFUri(uri.toASCIIString());
+            }
+        }
+        return new PDFUri(target);
+    }
+
+    private URI getTargetUri(String target) {
+        URI uri;
+        try {
+            uri = new URI(target);
+            String scheme = uri.getScheme();
+            String schemeSpecificPart = uri.getSchemeSpecificPart();
+            String authority = uri.getAuthority();
+            if (scheme == null && schemeSpecificPart.matches("//.*")) {
+                uri = getFileUri(target);
+            } else if ((scheme == null) && schemeSpecificPart.matches("/.*")) {
+                uri = getFileUri(target);
+            } else if (scheme != null && scheme.matches("[A-z]")) {
+                uri = getFileUri(target);
+            }  else if (scheme != null && scheme.equalsIgnoreCase("file") && authority != null) {
+                uri = getFileUri(target);
+            }
+        } catch (URISyntaxException e) {
+            uri = getFileUri(target);
+        }
+        return uri;
+    }
+
+    private URI getFileUri(String target) {
+        URI uri;
+        String scheme = null;
+        String fragment = null;
+        String filename = target;
         int index;
         String targetLo = target.toLowerCase();
-        if (target.startsWith(EMBEDDED_FILE)) {
-            // File Attachments (Embedded Files)
-            String filename = target.substring(EMBEDDED_FILE.length());
-            return getActionForEmbeddedFile(filename, newWindow);
-        } else if (targetLo.startsWith("http://")) {
-            // HTTP URL?
-            return new PDFUri(target);
-        } else if (targetLo.startsWith("https://")) {
-            // HTTPS URL?
-            return new PDFUri(target);
-        } else if (targetLo.startsWith("file://")) {
-            // Non PDF files. Try to /Launch them.
-            target = target.substring("file://".length());
-            return getLaunchAction(target);
-        } else if (targetLo.endsWith(".pdf")) {
-            // Bare PDF file name?
-            return getGoToPDFAction(target, null, -1, newWindow);
-        } else if ((index = targetLo.indexOf(".pdf#page=")) > 0) {
-            // PDF file + page?
-            String filename = target.substring(0, index + 4);
-            int page = Integer.parseInt(target.substring(index + 10));
-            return getGoToPDFAction(filename, null, page, newWindow);
-        } else if ((index = targetLo.indexOf(".pdf#dest=")) > 0) {
-            // PDF file + destination?
-            String filename = target.substring(0, index + 4);
-            String dest = target.substring(index + 10);
-            return getGoToPDFAction(filename, dest, -1, newWindow);
-        } else {
-            // None of the above? Default to URI:
-            return new PDFUri(target);
+        if (((index = targetLo.indexOf(".pdf#page=")) > 0)
+                || ((index = targetLo.indexOf(".pdf#dest=")) > 0)) {
+            filename = target.substring(0, index + 4);
+            fragment = target.substring(index + 5);
+        }
+
+        if (targetLo.startsWith("file://")) {
+            scheme = "file";
+            filename = filename.substring("file://".length());
+        } else if (targetLo.startsWith("embedded-file:")) {
+            scheme = "embedded-file";
+            filename = filename.substring("embedded-file:".length());
+        } else if (targetLo.startsWith("file:")) {
+            scheme = "file";
+            filename = filename.substring("file:".length());
+        }
+
+        try {
+             filename = filename.replace("\\", "/");
+             if (filename.matches("[A-z]:.*")) {
+                 scheme = (scheme == null) ? "file" : scheme;
+                 filename = "/" + filename;
+             } else if (filename.matches("//.*")) {
+                 scheme = (scheme == null) ? "file" : scheme;
+                 filename = "//" + filename;
+             } else if (filename.matches("/.*")) {
+                 scheme = (scheme == null) ? "file" : scheme;
+             }
+             uri = new URI(scheme, filename, fragment);
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException(e);
         }
+
+        return uri;
     }
 
     private PDFAction getActionForEmbeddedFile(String filename, boolean newWindow) {
@@ -723,13 +797,15 @@ public class PDFFactory {
 
     /**
      * Creates and returns a launch pdf document action using
-     * <code>file</code> to create a file spcifiaciton for
+     * <code>file</code> to create a file specification for
      * the document/file to be opened with an external application.
      *
      * @param file the pdf file name
+     * @param newWindow boolean indicating whether the target should be
+     *                  displayed in a new window
      * @return the pdf launch object
      */
-    private PDFLaunch getLaunchAction(String file) {
+    private PDFLaunch getLaunchAction(String file, boolean newWindow) {
         getDocument().getProfile().verifyActionAllowed();
 
         PDFFileSpec fileSpec = new PDFFileSpec(file);
@@ -740,7 +816,7 @@ public class PDFFactory {
         } else {
             fileSpec = oldSpec;
         }
-        PDFLaunch launch = new PDFLaunch(fileSpec);
+        PDFLaunch launch = new PDFLaunch(fileSpec, newWindow);
         PDFLaunch oldLaunch = getDocument().findLaunch(launch);
 
         if (oldLaunch == null) {

Modified: xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java?rev=1780923&r1=1780922&r2=1780923&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java (original)
+++ xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java Mon Jan 30 15:24:20 2017
@@ -35,6 +35,7 @@ public class PDFFileSpec extends PDFDict
         super();
         put("Type", new PDFName("Filespec"));
         put("F", filename);
+        put("UF", filename); // for non-ascii filenames, since PDF 1.7, 3.10.2
     }
 
     private String getFilename() {
@@ -77,4 +78,3 @@ public class PDFFileSpec extends PDFDict
         return true;
     }
 }
-

Modified: xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java?rev=1780923&r1=1780922&r2=1780923&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java (original)
+++ xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java Mon Jan 30 15:24:20 2017
@@ -25,6 +25,7 @@ package org.apache.fop.pdf;
 public class PDFLaunch extends PDFAction {
 
     private PDFReference externalFileSpec;
+    private boolean newWindow;
 
     /**
      * Creates a new /Launch action.
@@ -32,6 +33,18 @@ public class PDFLaunch extends PDFAction
      */
     public PDFLaunch(PDFFileSpec fileSpec) {
         this(fileSpec.makeReference());
+        this.newWindow = false;
+    }
+
+    /**
+     * Creates a new /Launch action.
+     * @param fileSpec the file specification to launch
+     * @param newWindow boolean indicating whether the target should be
+     *                  displayed in a new window
+     */
+    public PDFLaunch(PDFFileSpec fileSpec, boolean newWindow) {
+        this(fileSpec.makeReference());
+        this.newWindow = newWindow;
     }
 
     /**
@@ -56,6 +69,9 @@ public class PDFLaunch extends PDFAction
         StringBuffer sb = new StringBuffer(64);
         sb.append("<<\n/S /Launch\n/F ");
         sb.append(externalFileSpec.toString());
+        if (newWindow) {
+            sb.append("\n/NewWindow true");
+        }
         sb.append("\n>>");
 
         return sb.toString();

Added: xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java?rev=1780923&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java (added)
+++ xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java Mon Jan 30 15:24:20 2017
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static java.util.regex.Pattern.quote;
+
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.extensions.Link;
+import org.apache.fop.render.intermediate.extensions.URIAction;
+import org.apache.fop.render.pdf.PDFDocumentHandler;
+
+@RunWith(Parameterized.class)
+public class PDFLinkTestCase {
+    private String target;
+    private String expected;
+
+    public PDFLinkTestCase(String target, String expected) {
+        this.target = target;
+        this.expected = expected;
+    }
+
+    @Parameters
+    public static Collection links() {
+        return Arrays.asList(new Object[][] {
+            // Windows absolute paths
+            {"c:\\foobar.txt", quote("<< /Type /Filespec /F (c:/foobar.txt)")}, //0
+            {"c:\\foo bar.txt", quote("<< /Type /Filespec /F (c:/foo bar.txt)")},
+            {"c:\\foo\\bar.txt", quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"c:\\foo\\bar 2.txt", quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // Windows absolute paths using "/"
+            {"c:/foo bar.txt", quote("<< /Type /Filespec /F (c:/foo bar.txt)")}, //4
+            {"c:/foo/bar.txt", quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"c:/foo/bar 2.txt", quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // Linux absolute paths
+            {"/foobar.txt", quote("<< /Type /Filespec /F (/foobar.txt)")}, //7
+            {"/foo/bar.txt", quote("<< /Type /Filespec /F (/foo/bar.txt)")},
+            {"/foo/bar 2.txt", quote("<< /Type /Filespec /F (/foo/bar 2.txt)")},
+            {"/foo bar.txt", quote("<< /Type /Filespec /F (/foo bar.txt)")},
+
+            // Relative paths
+            {"foobar.txt", quote("<< /URI (foobar.txt)")}, //11
+            {"foo bar.txt", quote("<< /URI (foo%20bar.txt)")},
+            {"./foobar.txt", quote("<< /URI (./foobar.txt)")},
+            {"./foo bar.txt", quote("<< /URI (./foo%20bar.txt)")},
+            {"../foobar.txt", quote("<< /URI (../foobar.txt)")},
+            {"../foo bar.txt", quote("<< /URI (../foo%20bar.txt)")},
+
+            // Windows network paths
+            {"\\\\foo\\bar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.txt)")}, //17
+            {"\\\\foo\\bar 2.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.txt)")},
+            {"\\\\foo\\a\\bar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")},
+            {"\\\\foo\\a\\bar 2.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+
+            // Windows network path using "/"
+            {"//foo/a/bar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")}, // 21
+            {"//foo/a/bar 2.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+
+            // Non ASCII
+            // foo bar.txt (unicode)
+            {"\uFF46\uFF4F\uFF4F\u3000\uFF42\uFF41\uFF52.txt", quote(
+                    "<< /URI (%EF%BD%86%EF%BD%8F%EF%BD%8F%E3%80%80%EF%BD%82%EF%BD%81%EF%BD%92.txt)")}, //23
+            // c:/foo/foo bar.txt (unicode)
+            {"c:/foo/\uFF46\uFF4F\uFF4F\u3000\uFF42\uFF41\uFF52.txt", quote(
+                    "<< /Type /Filespec /F "
+                            + "<FEFF0063003A002F0066006F006F002FFF46FF4FFF4F3000FF42FF41FF52002E007400780074> "
+                            + "/UF <FEFF0063003A002F0066006F006F002FFF46FF4FFF4F3000FF42FF41FF52002E007400780074>")},
+            // \\foo\bar 2\foo bar.txt (unicode)
+            {"\\\\foo\\bar 2\\\uFF46\uFF4F\uFF4F\u3000\uFF42\uFF41\uFF52.txt", quote("<< /Type /Filespec /F "
+            + "<FEFF005C005C0066006F006F005C00620061007200200032005CFF46FF4FFF4F3000FF42FF41FF52002E007400780074> "
+            + "/UF "
+            + "<FEFF005C005C0066006F006F005C00620061007200200032005CFF46FF4FFF4F3000FF42FF41FF52002E007400780074>")},
+
+            // PDF, Windows absolute paths
+            {"c:\\foobar.pdf", quote("<< /Type /Filespec /F (c:/foobar.pdf)")}, //26
+            {"c:\\foo bar.pdf", quote("<< /Type /Filespec /F (c:/foo bar.pdf)")},
+            {"c:\\foo\\bar.pdf", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)")},
+            {"c:\\foo\\bar 2.pdf", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)")},
+
+            // PDF, Linux absolute paths
+            {"/foobar.pdf", quote("<< /Type /Filespec /F (/foobar.pdf)")}, //30
+            {"/foo bar.pdf", quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"/foo/bar.pdf", quote("<< /Type /Filespec /F (/foo/bar.pdf)")},
+            {"/foo/bar 2.pdf", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+
+            // PDF, Relative paths
+            {"foobar.pdf", quote("<< /URI (foobar.pdf)")}, //34
+            {"foo bar.pdf", quote("<< /URI (foo%20bar.pdf)")},
+            {"./foobar.pdf", quote("<< /URI (./foobar.pdf)")},
+            {"./foo bar.pdf", quote("<< /URI (./foo%20bar.pdf)")},
+            {"../foobar.pdf", quote("<< /URI (../foobar.pdf)")},
+            {"../foo bar.pdf", quote("<< /URI (../foo%20bar.pdf)")},
+
+            // PDF, Windows network paths
+            {"\\\\foo\\bar.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)")}, //40
+            {"\\\\foo\\bar 2.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)")},
+            {"\\\\foo\\a\\bar.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)")},
+            {"\\\\foo\\a\\bar 2.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)")},
+
+            // PDF with fragments, Windows absolute paths
+            {"c:\\foobar.pdf#page=2", quote("<< /Type /Filespec /F (c:/foobar.pdf)") + ".*" + quote("/S /GoToR") + ".*"
+                    + quote("/D [ 2 /XYZ null null null ]")}, //44
+            {"c:\\foo bar.pdf#dest=aa", quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D (aa)")},
+            {"c:\\foo\\bar.pdf#page=2", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"c:\\foo\\bar 2.pdf#dest=aa", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D (aa)")},
+
+            // PDF with fragments, Windows absolute paths using "/"
+            {"c:/foo bar.pdf#page=2", quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D [ 2 /XYZ null null null ]")}, //48
+            {"c:/foo/bar.pdf#dest=aa", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D (aa)")},
+            {"c:/foo/bar 2.pdf#page=2", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // PDF with fragments, Linux absolute paths
+            {"/foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (/foobar.pdf)") + ".*" + quote("/S /GoToR") + ".*"
+                    + quote("/D (aa)")}, //51
+            {"/foo/bar.pdf#page=2", quote("<< /Type /Filespec /F (/foo/bar.pdf)") + ".*" + quote("/S /GoToR") + ".*"
+                    + quote("/D [ 2 /XYZ null null null ]")},
+            {"/foo/bar 2.pdf#dest=aa", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)") + ".*" + quote("/S /GoToR")
+                    + ".*" + quote("/D (aa)")},
+            {"/foo bar.pdf#page=2", quote("<< /Type /Filespec /F (/foo bar.pdf)") + ".*" + quote("/S /GoToR") + ".*"
+                    + quote("/D [ 2 /XYZ null null null ]")},
+
+            // PDF with fragments, Relative paths
+            {"foobar.pdf#dest=aa", quote("<< /URI (foobar.pdf#dest=aa)")}, //55
+            {"foo bar.pdf#page=2", quote("<< /URI (foo%20bar.pdf#page=2)")},
+            {"./foobar.pdf#dest=aa", quote("<< /URI (./foobar.pdf#dest=aa)")},
+            {"./foo bar.pdf#page=2", quote("<< /URI (./foo%20bar.pdf#page=2)")},
+            {"../foobar.pdf#dest=aa", quote("<< /URI (../foobar.pdf#dest=aa)")},
+            {"../foo bar.pdf#page=2", quote("<< /URI (../foo%20bar.pdf#page=2)")},
+
+            // PDF with fragments, Windows network paths
+            {"\\\\foo\\bar.pdf#dest=aa", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")}, //61
+            {"\\\\foo\\bar 2.pdf#page=2", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"\\\\foo\\a\\bar.pdf#dest=aa", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"\\\\foo\\a\\bar 2.pdf#page=2", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// prefix, Windows absolute paths
+            {"file://c:\\foobar.txt", quote("<< /Type /Filespec /F (c:/foobar.txt)")}, //65
+            {"file://c:\\foo bar.txt", quote("<< /Type /Filespec /F (c:/foo bar.txt)")},
+            {"file://c:\\foo\\bar.txt", quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file://c:\\foo\\bar 2.txt", quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // file:// prefix, Windows absolute paths using "/"
+            {"file://c:/foo bar.txt", quote("<< /Type /Filespec /F (c:/foo bar.txt)")}, //69
+            {"file://c:/foo/bar.txt", quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file://c:/foo/bar 2.txt", quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // file:// prefix, Linux absolute paths
+            {"file:///foobar.txt", quote("<< /Type /Filespec /F (/foobar.txt)")}, //72
+            {"file:///foo/bar.txt", quote("<< /Type /Filespec /F (/foo/bar.txt)")},
+            {"file:///foo/bar 2.txt", quote("<< /Type /Filespec /F (/foo/bar 2.txt)")},
+            {"file:///foo bar.txt", quote("<< /Type /Filespec /F (/foo bar.txt)")},
+
+            // file:// prefix, Relative paths
+            {"file://foobar.txt", quote("<< /Type /Filespec /F (foobar.txt)")}, //76
+            {"file://foo bar.txt", quote("<< /Type /Filespec /F (foo bar.txt)")},
+            {"file://./foobar.txt", quote("<< /Type /Filespec /F (./foobar.txt)")},
+            {"file://./foo bar.txt", quote("<< /Type /Filespec /F (./foo bar.txt)")},
+            {"file://../foobar.txt", quote("<< /Type /Filespec /F (../foobar.txt)")},
+            {"file://../foo bar.txt", quote("<< /Type /Filespec /F (../foo bar.txt)")},
+
+            // file:// prefix, Windows network paths
+            {"file://\\\\foo\\bar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.txt)")}, //82
+            {"file://\\\\foo\\bar 2.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.txt)")},
+            {"file://\\\\foo\\a\\bar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")},
+            {"file://\\\\foo\\a\\bar 2.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+
+            // file:// prefix, Windows network path using "/"
+            {"file:////foo/a/bar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")}, // 86
+            {"file:////foo/a/bar 2.txt", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+            {"file:////foobar.txt", quote("<< /Type /Filespec /F (\\\\\\\\foobar.txt)")},
+
+            // Proper file:// for windows paths
+            {"file:///c:/foo%20bar.txt", quote("<< /Type /Filespec /F (c:/foo bar.txt)")}, //89
+            {"file:///c:/foo/bar.txt", quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file:///c:/foo/bar%202.txt", quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // Proper file:// for linux paths
+            {"file:///foo/bar%202.txt", quote("<< /Type /Filespec /F (/foo/bar 2.txt)")}, //92
+
+            // file:// PDF, Windows absolute paths
+            {"file://c:\\foobar.pdf", quote("<< /Type /Filespec /F (c:/foobar.pdf)")}, //93
+            {"file://c:\\foo bar.pdf", quote("<< /Type /Filespec /F (c:/foo bar.pdf)")},
+            {"file://c:\\foo\\bar.pdf", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)")},
+            {"file://c:\\foo\\bar 2.pdf", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)")},
+
+            // file:// PDF, Linux absolute paths
+            {"file:///foobar.pdf", quote("<< /Type /Filespec /F (/foobar.pdf)")}, //97
+            {"file:///foo bar.pdf", quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"file:///foo/bar.pdf", quote("<< /Type /Filespec /F (/foo/bar.pdf)")},
+            {"file:///foo/bar 2.pdf", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+
+            // file:// PDF, Relative paths
+            {"file://foobar.pdf", quote("<< /Type /Filespec /F (foobar.pdf)")}, //101
+            {"file://foo bar.pdf", quote("<< /Type /Filespec /F (foo bar.pdf)")},
+            {"file://./foobar.pdf", quote("<< /Type /Filespec /F (./foobar.pdf)")},
+            {"file://./foo bar.pdf", quote("<< /Type /Filespec /F (./foo bar.pdf)")},
+            {"file://../foobar.pdf", quote("<< /Type /Filespec /F (../foobar.pdf)")},
+            {"file://../foo bar.pdf", quote("<< /Type /Filespec /F (../foo bar.pdf)")},
+
+            // file:// PDF, Windows network paths
+            {"file://\\\\foo\\bar.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)")}, //107
+            {"file://\\\\foo\\bar 2.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)")},
+            {"file://\\\\foo\\a\\bar.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)")},
+            {"file://\\\\foo\\a\\bar 2.pdf", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)")},
+
+            // Proper file:// for windows paths
+            {"file:///c:/foo%20bar.pdf", quote("<< /Type /Filespec /F (c:/foo bar.pdf)")}, //111
+            {"file:///c:/foo/bar.pdf", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)")},
+            {"file:///c:/foo/bar%202.pdf", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)")},
+
+            // Proper file:// PDF, for linux paths
+            {"file:///foo/bar%202.pdf", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")}, //114
+
+            // file:// PDF with fragments, Windows absolute paths
+            {"file://c:\\foobar.pdf#page=2", quote("<< /Type /Filespec /F (c:/foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")}, //115
+            {"file://c:\\foo bar.pdf#dest=aa", quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file://c:\\foo\\bar.pdf#page=2", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file://c:\\foo\\bar 2.pdf#dest=aa", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+
+            // file:// PDF with fragments, Windows absolute paths using "/"
+            {"file://c:/foo bar.pdf#page=2", quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")}, //119
+            {"file://c:/foo/bar.pdf#dest=aa", quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file://c:/foo/bar 2.pdf#page=2", quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// PDF with fragments, Linux absolute paths
+            {"file:///foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (/foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")}, //122
+            {"file:///foo/bar.pdf#page=2", quote("<< /Type /Filespec /F (/foo/bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file:///foo/bar 2.pdf#dest=aa", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file:///foo bar.pdf#page=2", quote("<< /Type /Filespec /F (/foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// PDF with fragments, Relative paths
+            {"file://foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")}, //126
+            {"file://foo bar.pdf#page=2", quote("<< /Type /Filespec /F (foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file://./foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (./foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file://./foo bar.pdf#page=2", quote("<< /Type /Filespec /F (./foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file://../foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (../foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file://../foo bar.pdf#page=2", quote("<< /Type /Filespec /F (../foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// PDF with fragments, Windows network paths
+            {"file://\\\\foo\\bar.pdf#dest=aa", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")}, //132
+            {"file://\\\\foo\\bar 2.pdf#page=2", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)")
+                    + ".*" + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file://\\\\foo\\a\\bar.pdf#dest=aa", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)")
+                    + ".*" + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file://\\\\foo\\a\\bar 2.pdf#page=2",
+                    quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)") + ".*"
+                            + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // Proper file:// PDF with fragments, Windows network paths
+            {"file:////foo/bar.pdf#dest=aa", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")}, //136
+            {"file:////foo/bar%202.pdf#page=2", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file:////foo/a/bar.pdf#dest=aa", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)")
+                    + ".*" + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file:////foo/a/bar%202.pdf#page=2", quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)")
+                    + ".*" + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // Proper file:// PDF, for linux paths
+            {"file:///foo/bar%202.pdf#page=2", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")}, //140
+
+            // file: Relative paths
+            {"file:foobar.txt", quote("<< /Type /Filespec /F (foobar.txt)")}, //141
+            {"file:foo bar.txt", quote("<< /Type /Filespec /F (foo bar.txt)")},
+            {"file:./foobar.txt", quote("<< /Type /Filespec /F (./foobar.txt)")},
+            {"file:./foo bar.txt", quote("<< /Type /Filespec /F (./foo bar.txt)")},
+            {"file:../foobar.txt", quote("<< /Type /Filespec /F (../foobar.txt)")},
+            {"file:../foo bar.txt", quote("<< /Type /Filespec /F (../foo bar.txt)")},
+            {"file:\uFF46\uFF4F\uFF4F\u3000\uFF42\uFF41\uFF52.txt",
+                    quote("<< /Type /Filespec /F <FEFFFF46FF4FFF4F3000FF42FF41FF52002E007400780074> "
+                            + "/UF <FEFFFF46FF4FFF4F3000FF42FF41FF52002E007400780074>")},
+
+            // file: PDF Relative paths
+            {"file:foobar.pdf", quote("<< /Type /Filespec /F (foobar.pdf)")}, //148
+            {"file:foo bar.pdf", quote("<< /Type /Filespec /F (foo bar.pdf)")},
+            {"file:./foobar.pdf", quote("<< /Type /Filespec /F (./foobar.pdf)")},
+            {"file:./foo bar.pdf", quote("<< /Type /Filespec /F (./foo bar.pdf)")},
+            {"file:../foobar.pdf", quote("<< /Type /Filespec /F (../foobar.pdf)")},
+            {"file:../foo bar.pdf", quote("<< /Type /Filespec /F (../foo bar.pdf)")},
+
+            // file: PDF with fragments, Relative paths
+            {"file:foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")}, //154
+            {"file:foo bar.pdf#page=2", quote("<< /Type /Filespec /F (foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file:./foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (./foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file:./foo bar.pdf#page=2", quote("<< /Type /Filespec /F (./foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+            {"file:../foobar.pdf#dest=aa", quote("<< /Type /Filespec /F (../foobar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D (aa)")},
+            {"file:../foo bar.pdf#page=2", quote("<< /Type /Filespec /F (../foo bar.pdf)") + ".*"
+                    + quote("/S /GoToR") + ".*" + quote("/D [ 2 /XYZ null null null ]")},
+
+            // file: prefix, Windows absolute paths
+            {"file:c:\\foobar.txt", quote("<< /Type /Filespec /F (c:/foobar.txt)")}, //160
+            {"file:c:\\foo bar.txt", quote("<< /Type /Filespec /F (c:/foo bar.txt)")},
+            {"file:c:\\foo\\bar.txt", quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file:c:\\foo\\bar 2.txt", quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // PDF, Linux absolute paths
+            {"file:/foobar.pdf", quote("<< /Type /Filespec /F (/foobar.pdf)")}, //164
+            {"file:/foo bar.pdf", quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"file:/foo%20bar.pdf", quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"file:/foo/bar.pdf", quote("<< /Type /Filespec /F (/foo/bar.pdf)")},
+            {"file:/foo/bar 2.pdf", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+            {"file:/foo/bar%202.pdf", quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+
+            // Web links
+            {"https://xmlgraphics.apache.org/fop/", quote("<< /URI (https://xmlgraphics.apache.org/fop/)")}, //170
+            {"http://xmlgraphics.apache.org/fop/", quote("<< /URI (http://xmlgraphics.apache.org/fop/)")},
+            {"https://xmlgraphics.apache.org/fop/examples.html",
+                    quote("<< /URI (https://xmlgraphics.apache.org/fop/examples.html)")},
+            {"https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf",
+                    quote("<< /URI (https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf)")},
+            {"https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf#page=2",
+                    quote("<< /URI (https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf#page=2)")},
+            {"https://xmlgraphics.apache.org/fop/fo/fonts.fo",
+                    quote("<< /URI (https://xmlgraphics.apache.org/fop/fo/fonts.fo)")},
+
+            // HTML files
+            {"examples.html#foo", quote("<< /URI (examples.html#foo)")}, //177
+            {"examples.html?foo#bar", quote("/URI (examples.html?foo#bar)")},
+            {"examples.html", quote("<< /URI (examples.html)")},
+            {"file:examples.html", quote("<< /Type /Filespec /F (examples.html)")},
+        });
+    }
+
+    @Test
+    public void testLinks() throws IFException {
+        FOUserAgent ua = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent();
+        PDFDocumentHandler docHandler = new PDFDocumentHandler(new IFContext(ua));
+        docHandler.setFontInfo(new FontInfo());
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        docHandler.setResult(new StreamResult(out));
+        docHandler.startDocument();
+        docHandler.startPage(0, "", "", new Dimension());
+        docHandler.getDocumentNavigationHandler().renderLink(new Link(
+                new URIAction(target, false), new Rectangle()));
+        docHandler.endDocument();
+
+        // Normalize spaces between word for easier testing
+        String outString = out.toString().replaceAll("\\s+", " ");
+
+        Pattern r = Pattern.compile(expected);
+        Matcher m = r.matcher(outString);
+        Assert.assertTrue(m.find());
+    }
+}

Propchange: xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.