Index: fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java
===================================================================
--- fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java	(revision 1779640)
+++ fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java	(working copy)
@@ -25,6 +25,8 @@
 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 @@
         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,204 @@
      * @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) {
+                // Relative URI or URL
+                // Since PDFs can be served up from browser, Launch actions will not work
+                // Example:
+                //   ./foobar.pdf
+                //   ./foobar.pdf#dest=aa
+                //   https://xmlgraphics.apache.org/fop
+                //   https://www.somewhere.com/doc/help.pdf
+                //   https://www.somewhere.com/doc/help.pdf#page=1
+                return new PDFUri(uri.toASCIIString());
+
+            } else if (scheme.equalsIgnoreCase("embedded-file")) {
+                // File Attachments (Embedded Files)
+                // Examples:
+                //   embedded-file:foobar.pdf
+                return getActionForEmbeddedFile(filename, newWindow);
+
+            } else if (scheme.equalsIgnoreCase("file")) {
+                if (filename.startsWith("//")) {
+                    // Windows network path
+                    // Example:
+                    //   //foo/bar.pdf
+                    filename = filename.replace("/", "\\");
+
+                } else if (filename.matches("^/[A-z]:/.*")) {
+                    // Windows path
+                    // Example:
+                    //   /c:/foo/bar.txt
+                    filename = filename.substring(1); // strip off first /
+                }
+
+                if (filename.toLowerCase().endsWith(".pdf")) {
+                    // Get page/dest from URI fragment
+                    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) {
+                        // URI has fragment or query, use URI action
+                        // Examples:
+                        //    file:examples.html#foo
+                        return new PDFUri(uri.toASCIIString());
+                    } else {
+                        // URI is a file, use launch action
+                        return getLaunchAction(filename, newWindow);
+                    }
+                }
+
+            } else {
+                // All other URI schemes
+                return new PDFUri(uri.toASCIIString());
+            }
+        }
+
+        // Fallback, unable to get a valid URI
+        return new PDFUri(target);
+    }
+
+    private URI getTargetUri(String target) {
+        URI uri;
+
+        try {
+            // Assume well constructed URI, if not try to recover target as a file URI
+            if (target != null) {
+                uri = new URI(target);
+            } else {
+                uri = new URI("");
+            }
+
+            String scheme = uri.getScheme();
+            String schemeSpecificPart = uri.getSchemeSpecificPart();
+
+            String authority = uri.getAuthority();
+
+            if (scheme == null && schemeSpecificPart.matches("//.*")) {
+                // Target is a Windows network file path but uses wrong file separator
+                // Examples:
+                //   //foo/a/bar.txt       "foo" authority with "/a/bar.txt" path is wrong!     Correct: "file:////foo/a/bar.txt"
+                uri = getFileUri(target);
+
+            } else if ((scheme == null) && schemeSpecificPart.matches("/.*")) {
+                // No scheme, and starts with /.  This URI is absolute so use file scheme
+                // Examples:
+                //    /foo/bar/a.txt    ->  file:/foo/bar/a.txt
+                uri = getFileUri(target);  // file: blah
+
+            } else if (scheme != null && scheme.matches("[A-z]")) {
+                // Target is a Windows path
+                // Examples:
+                //   c:/foo/bar.txt     "c" scheme with "/foobar.png" path is wrong!        Correct: "file:///c:/foo/bar.txt"
+                uri = getFileUri(target);
+
+            }  else if (scheme != null && scheme.equalsIgnoreCase("file") && authority != null) {
+                // Target is relative file uri, but incorrectly specified with file scheme
+                // Target is a windows absolute path file uri, but has // instead of ///
+                // Examples:
+                //   file://foobar.txt      "foobar.txt" authority with empty path is wrong!    Correct: "file:foobar.txt"
+                //   file://./foo bar.txt   "." authority with "/foobar.txt" path is wrong!     Correct: "file:./foobar.txt"
+                //   file://../foo bar.txt  ".." authority with "../foo bar" path is wrong!     Correct: "file:../foobar.txt"
+                //   file://c:/foo/bar.txt  "c:" authority with "/foo/bar.txt" path are wrong!  Correct: "file:///c:/foo/bar.txt"
+                uri = getFileUri(target);
+            }
+
+        } catch (URISyntaxException e) {
+            // Target is a file path or target that was not percent encoded
+            // Examples:
+            //   foo bar.png            illegal character in path at index 3            Correct: "foo%20bar.png"
+            //   c:\foobar.png          illegal character in opaque part at index 2     Correct: "file:///c:/foobar.png"
+            //   c:/foo bar.png         illegal character in path at index 6            Correct: "file:///c:/foo%20bar.png"
+            //   file://foo bar.png     illegal character in authority at index 7       Correct: "foo%20bar.png"
+            //   \\server\foo\bar.png   illegal character in path at index 0            Correct: "file:////server/foo/bar.png"
+            uri = getFileUri(target);   // returns foo%20.bar.png
+        }
+
+        return uri;
+    }
+
+    private URI getFileUri(String target) {
+        // Note: target is not a valid file URI string
+
+        URI uri;
+        String scheme = null;
+        String fragment = null;
+        String filename = target;
+
+        // Extract supported PDF fragments
         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);
         }
+
+        // Extract scheme and filename
+        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 {
+             // Convert to Java style path string
+             filename = filename.replace("\\", "/");
+             if (filename.matches("[A-z]:.*")) {
+                 // Windows absolute path.  Must specify file scheme.
+                 // Example:
+                 //    c:/foobar.txt -> file:///c:/foo/bar.txt
+                 scheme = (scheme == null) ? "file" : scheme;
+                 filename = "/" + filename;
+             } else if (filename.matches("//.*")) {
+                 // Windows network path.  Must specify file scheme.
+                 // Example:
+                 //    //foo/bar/file.png -> file:////foo/bar/file.png
+                 scheme = (scheme == null) ? "file" : scheme;
+                 filename = "//" + filename;
+             } else if (filename.matches("/.*")) {
+                 // Linux absolute path.  Must specify file scheme.
+                 // Example:
+                 //    /foo/bar/file.png -> file:/foo/bar/file.png
+                 scheme = (scheme == null) ? "file" : scheme;
+             } else {
+                 // Relative file
+                 // Unless already specified, relative file URIs do not have a scheme
+             }
+
+             uri = new URI(scheme, filename, fragment);
+
+        } catch (URISyntaxException e) {
+            uri = null;
+            log.warn("Cannot get File URI for: " + target + "\nReason:" + e.getMessage());
+        }
+
+        return uri;
     }
 
     private PDFAction getActionForEmbeddedFile(String filename, boolean newWindow) {
@@ -723,13 +888,15 @@
 
     /**
      * 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 +907,7 @@
         } else {
             fileSpec = oldSpec;
         }
-        PDFLaunch launch = new PDFLaunch(fileSpec);
+        PDFLaunch launch = new PDFLaunch(fileSpec, newWindow);
         PDFLaunch oldLaunch = getDocument().findLaunch(launch);
 
         if (oldLaunch == null) {
Index: fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java
===================================================================
--- fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java	(revision 1779640)
+++ fop-core/src/main/java/org/apache/fop/pdf/PDFFileSpec.java	(working copy)
@@ -35,6 +35,7 @@
         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 @@
         return true;
     }
 }
-
Index: fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java
===================================================================
--- fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java	(revision 1779640)
+++ fop-core/src/main/java/org/apache/fop/pdf/PDFLaunch.java	(working copy)
@@ -25,6 +25,7 @@
 public class PDFLaunch extends PDFAction {
 
     private PDFReference externalFileSpec;
+    private boolean newWindow;
 
     /**
      * Creates a new /Launch action.
@@ -32,10 +33,22 @@
      */
     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;
+    }
+
+    /**
+     * Creates a new /Launch action.
      * @param fileSpec a reference to the file specification
      */
     public PDFLaunch(PDFReference fileSpec) {
@@ -56,6 +69,9 @@
         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();
Index: fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java
===================================================================
--- fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java	(revision 1779640)
+++ fop-core/src/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java	(working copy)
@@ -40,6 +40,7 @@
         PDFNumsArrayTestCase.class,
         PDFRectangleTestCase.class,
         PDFReferenceTestCase.class,
+        PDFLinkTestCase.class,
         VersionTestCase.class,
         VersionControllerTestCase.class
 })
Index: fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java
===================================================================
--- fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java	(nonexistent)
+++ fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java	(working copy)
@@ -0,0 +1,354 @@
+/*
+ * 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.Collection;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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", Pattern.quote("<< /Type /Filespec /F (c:/foobar.txt)")}, //0
+            {"c:\\foo bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.txt)")},
+            {"c:\\foo\\bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"c:\\foo\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // Windows absolute paths using "/"
+            {"c:/foo bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.txt)")}, //4
+            {"c:/foo/bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"c:/foo/bar 2.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // Linux absolute paths
+            {"/foobar.txt", Pattern.quote("<< /Type /Filespec /F (/foobar.txt)")}, //7
+            {"/foo/bar.txt", Pattern.quote("<< /Type /Filespec /F (/foo/bar.txt)")},
+            {"/foo/bar 2.txt", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.txt)")},
+            {"/foo bar.txt", Pattern.quote("<< /Type /Filespec /F (/foo bar.txt)")},
+
+            // Relative paths
+            {"foobar.txt", Pattern.quote("<< /URI (foobar.txt)")}, //11
+            {"foo bar.txt", Pattern.quote("<< /URI (foo%20bar.txt)")},
+            {"./foobar.txt", Pattern.quote("<< /URI (./foobar.txt)")},
+            {"./foo bar.txt", Pattern.quote("<< /URI (./foo%20bar.txt)")},
+            {"../foobar.txt", Pattern.quote("<< /URI (../foobar.txt)")},
+            {"../foo bar.txt", Pattern.quote("<< /URI (../foo%20bar.txt)")},
+
+            // Windows network paths
+            {"\\\\foo\\bar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.txt)")}, //17
+            {"\\\\foo\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.txt)")},
+            {"\\\\foo\\a\\bar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")},
+            {"\\\\foo\\a\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+
+            // Windows network path using "/"
+            {"//foo/a/bar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")}, // 21
+            {"//foo/a/bar 2.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+
+            // Non ASCII
+            // foo bar.txt (unicode)
+            {"\uFF46\uFF4F\uFF4F\u3000\uFF42\uFF41\uFF52.txt", Pattern.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", Pattern.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", Pattern.quote("<< /Type /Filespec /F <FEFF005C005C0066006F006F005C00620061007200200032005CFF46FF4FFF4F3000FF42FF41FF52002E007400780074> /UF <FEFF005C005C0066006F006F005C00620061007200200032005CFF46FF4FFF4F3000FF42FF41FF52002E007400780074>")},
+
+            // PDF, Windows absolute paths
+            {"c:\\foobar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foobar.pdf)")}, //26
+            {"c:\\foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)")},
+            {"c:\\foo\\bar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)")},
+            {"c:\\foo\\bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)")},
+
+            // PDF, Linux absolute paths
+            {"/foobar.pdf", Pattern.quote("<< /Type /Filespec /F (/foobar.pdf)")}, //30
+            {"/foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"/foo/bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar.pdf)")},
+            {"/foo/bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+
+            // PDF, Relative paths
+            {"foobar.pdf", Pattern.quote("<< /URI (foobar.pdf)")}, //34
+            {"foo bar.pdf", Pattern.quote("<< /URI (foo%20bar.pdf)")},
+            {"./foobar.pdf", Pattern.quote("<< /URI (./foobar.pdf)")},
+            {"./foo bar.pdf", Pattern.quote("<< /URI (./foo%20bar.pdf)")},
+            {"../foobar.pdf", Pattern.quote("<< /URI (../foobar.pdf)")},
+            {"../foo bar.pdf", Pattern.quote("<< /URI (../foo%20bar.pdf)")},
+
+            // PDF, Windows network paths
+            {"\\\\foo\\bar.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)")}, //40
+            {"\\\\foo\\bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)")},
+            {"\\\\foo\\a\\bar.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)")},
+            {"\\\\foo\\a\\bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)")},
+
+            // PDF with fragments, Windows absolute paths
+            {"c:\\foobar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")}, //44
+            {"c:\\foo bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"c:\\foo\\bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"c:\\foo\\bar 2.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+
+            // PDF with fragments, Windows absolute paths using "/"
+            {"c:/foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")}, //48
+            {"c:/foo/bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"c:/foo/bar 2.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // PDF with fragments, Linux absolute paths
+            {"/foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (/foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //51
+            {"/foo/bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (/foo/bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"/foo/bar 2.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"/foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (/foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // PDF with fragments, Relative paths
+            {"foobar.pdf#dest=aa", Pattern.quote("<< /URI (foobar.pdf#dest=aa)")}, //55
+            {"foo bar.pdf#page=2", Pattern.quote("<< /URI (foo%20bar.pdf#page=2)")},
+            {"./foobar.pdf#dest=aa", Pattern.quote("<< /URI (./foobar.pdf#dest=aa)")},
+            {"./foo bar.pdf#page=2", Pattern.quote("<< /URI (./foo%20bar.pdf#page=2)")},
+            {"../foobar.pdf#dest=aa", Pattern.quote("<< /URI (../foobar.pdf#dest=aa)")},
+            {"../foo bar.pdf#page=2", Pattern.quote("<< /URI (../foo%20bar.pdf#page=2)")},
+
+            // PDF with fragments, Windows network paths
+            {"\\\\foo\\bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //61
+            {"\\\\foo\\bar 2.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"\\\\foo\\a\\bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"\\\\foo\\a\\bar 2.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// prefix, Windows absolute paths
+            {"file://c:\\foobar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foobar.txt)")}, //65
+            {"file://c:\\foo bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.txt)")},
+            {"file://c:\\foo\\bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file://c:\\foo\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // file:// prefix, Windows absolute paths using "/"
+            {"file://c:/foo bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.txt)")}, //69
+            {"file://c:/foo/bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file://c:/foo/bar 2.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // file:// prefix, Linux absolute paths
+            {"file:///foobar.txt", Pattern.quote("<< /Type /Filespec /F (/foobar.txt)")}, //72
+            {"file:///foo/bar.txt", Pattern.quote("<< /Type /Filespec /F (/foo/bar.txt)")},
+            {"file:///foo/bar 2.txt", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.txt)")},
+            {"file:///foo bar.txt", Pattern.quote("<< /Type /Filespec /F (/foo bar.txt)")},
+
+            // file:// prefix, Relative paths
+            {"file://foobar.txt", Pattern.quote("<< /Type /Filespec /F (foobar.txt)")}, //76
+            {"file://foo bar.txt", Pattern.quote("<< /Type /Filespec /F (foo bar.txt)")},
+            {"file://./foobar.txt", Pattern.quote("<< /Type /Filespec /F (./foobar.txt)")},
+            {"file://./foo bar.txt", Pattern.quote("<< /Type /Filespec /F (./foo bar.txt)")},
+            {"file://../foobar.txt", Pattern.quote("<< /Type /Filespec /F (../foobar.txt)")},
+            {"file://../foo bar.txt", Pattern.quote("<< /Type /Filespec /F (../foo bar.txt)")},
+
+            // file:// prefix, Windows network paths
+            {"file://\\\\foo\\bar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.txt)")}, //82
+            {"file://\\\\foo\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.txt)")},
+            {"file://\\\\foo\\a\\bar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")},
+            {"file://\\\\foo\\a\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+
+            // file:// prefix, Windows network path using "/"
+            {"file:////foo/a/bar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.txt)")}, // 86
+            {"file:////foo/a/bar 2.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.txt)")},
+            {"file:////foobar.txt", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foobar.txt)")},
+
+            // Proper file:// for windows paths
+            {"file:///c:/foo%20bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.txt)")}, //89
+            {"file:///c:/foo/bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file:///c:/foo/bar%202.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // Proper file:// for linux paths
+            {"file:///foo/bar%202.txt", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.txt)")}, //92
+
+            // file:// PDF, Windows absolute paths
+            {"file://c:\\foobar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foobar.pdf)")}, //93
+            {"file://c:\\foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)")},
+            {"file://c:\\foo\\bar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)")},
+            {"file://c:\\foo\\bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)")},
+
+            // file:// PDF, Linux absolute paths
+            {"file:///foobar.pdf", Pattern.quote("<< /Type /Filespec /F (/foobar.pdf)")}, //97
+            {"file:///foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"file:///foo/bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar.pdf)")},
+            {"file:///foo/bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+
+            // file:// PDF, Relative paths
+            {"file://foobar.pdf", Pattern.quote("<< /Type /Filespec /F (foobar.pdf)")}, //101
+            {"file://foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (foo bar.pdf)")},
+            {"file://./foobar.pdf", Pattern.quote("<< /Type /Filespec /F (./foobar.pdf)")},
+            {"file://./foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (./foo bar.pdf)")},
+            {"file://../foobar.pdf", Pattern.quote("<< /Type /Filespec /F (../foobar.pdf)")},
+            {"file://../foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (../foo bar.pdf)")},
+
+            // file:// PDF, Windows network paths
+            {"file://\\\\foo\\bar.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)")}, //107
+            {"file://\\\\foo\\bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)")},
+            {"file://\\\\foo\\a\\bar.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)")},
+            {"file://\\\\foo\\a\\bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)")},
+
+            // Proper file:// for windows paths
+            {"file:///c:/foo%20bar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)")}, //111
+            {"file:///c:/foo/bar.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)")},
+            {"file:///c:/foo/bar%202.pdf", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)")},
+
+            // Proper file:// PDF, for linux paths
+            {"file:///foo/bar%202.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")}, //114
+
+            // file:// PDF with fragments, Windows absolute paths
+            {"file://c:\\foobar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")}, //115
+            {"file://c:\\foo bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file://c:\\foo\\bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file://c:\\foo\\bar 2.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+
+            // file:// PDF with fragments, Windows absolute paths using "/"
+            {"file://c:/foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")}, //119
+            {"file://c:/foo/bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file://c:/foo/bar 2.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// PDF with fragments, Linux absolute paths
+            {"file:///foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (/foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //122
+            {"file:///foo/bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (/foo/bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file:///foo/bar 2.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file:///foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (/foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// PDF with fragments, Relative paths
+            {"file://foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //126
+            {"file://foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file://./foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (./foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file://./foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (./foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file://../foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (../foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file://../foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (../foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // file:// PDF with fragments, Windows network paths
+            {"file://\\\\foo\\bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //132
+            {"file://\\\\foo\\bar 2.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file://\\\\foo\\a\\bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file://\\\\foo\\a\\bar 2.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // Proper file:// PDF with fragments, Windows network paths
+            {"file:////foo/bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //136
+            {"file:////foo/bar%202.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file:////foo/a/bar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file:////foo/a/bar%202.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (\\\\\\\\foo\\\\a\\\\bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // Proper file:// PDF, for linux paths
+            {"file:///foo/bar%202.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")}, //140
+
+            // file: Relative paths
+            {"file:foobar.txt", Pattern.quote("<< /Type /Filespec /F (foobar.txt)")}, //141
+            {"file:foo bar.txt", Pattern.quote("<< /Type /Filespec /F (foo bar.txt)")},
+            {"file:./foobar.txt", Pattern.quote("<< /Type /Filespec /F (./foobar.txt)")},
+            {"file:./foo bar.txt", Pattern.quote("<< /Type /Filespec /F (./foo bar.txt)")},
+            {"file:../foobar.txt", Pattern.quote("<< /Type /Filespec /F (../foobar.txt)")},
+            {"file:../foo bar.txt", Pattern.quote("<< /Type /Filespec /F (../foo bar.txt)")},
+            {"file:\uFF46\uFF4F\uFF4F\u3000\uFF42\uFF41\uFF52.txt", Pattern.quote("<< /Type /Filespec /F <FEFFFF46FF4FFF4F3000FF42FF41FF52002E007400780074> /UF <FEFFFF46FF4FFF4F3000FF42FF41FF52002E007400780074>")},
+
+            // file: PDF Relative paths
+            {"file:foobar.pdf", Pattern.quote("<< /Type /Filespec /F (foobar.pdf)")}, //148
+            {"file:foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (foo bar.pdf)")},
+            {"file:./foobar.pdf", Pattern.quote("<< /Type /Filespec /F (./foobar.pdf)")},
+            {"file:./foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (./foo bar.pdf)")},
+            {"file:../foobar.pdf", Pattern.quote("<< /Type /Filespec /F (../foobar.pdf)")},
+            {"file:../foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (../foo bar.pdf)")},
+
+            // file: PDF with fragments, Relative paths
+            {"file:foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")}, //154
+            {"file:foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file:./foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (./foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file:./foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (./foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+            {"file:../foobar.pdf#dest=aa", Pattern.quote("<< /Type /Filespec /F (../foobar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D (aa)")},
+            {"file:../foo bar.pdf#page=2", Pattern.quote("<< /Type /Filespec /F (../foo bar.pdf)") + ".*" + Pattern.quote("/S /GoToR") + ".*" + Pattern.quote("/D [ 2 /XYZ null null null ]")},
+
+            // file: prefix, Windows absolute paths
+            {"file:c:\\foobar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foobar.txt)")}, //160
+            {"file:c:\\foo bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo bar.txt)")},
+            {"file:c:\\foo\\bar.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar.txt)")},
+            {"file:c:\\foo\\bar 2.txt", Pattern.quote("<< /Type /Filespec /F (c:/foo/bar 2.txt)")},
+
+            // PDF, Linux absolute paths
+            {"file:/foobar.pdf", Pattern.quote("<< /Type /Filespec /F (/foobar.pdf)")}, //164
+            {"file:/foo bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"file:/foo%20bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo bar.pdf)")},
+            {"file:/foo/bar.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar.pdf)")},
+            {"file:/foo/bar 2.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+            {"file:/foo/bar%202.pdf", Pattern.quote("<< /Type /Filespec /F (/foo/bar 2.pdf)")},
+
+            // Web links
+            {"https://xmlgraphics.apache.org/fop/", Pattern.quote("<< /URI (https://xmlgraphics.apache.org/fop/)")}, //170
+            {"http://xmlgraphics.apache.org/fop/", Pattern.quote("<< /URI (http://xmlgraphics.apache.org/fop/)")},
+            {"https://xmlgraphics.apache.org/fop/examples.html", Pattern.quote("<< /URI (https://xmlgraphics.apache.org/fop/examples.html)")},
+            {"https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf", Pattern.quote("<< /URI (https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf)")},
+            {"https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf#page=2", Pattern.quote("<< /URI (https://xmlgraphics.apache.org/fop/fo/fonts.fo.pdf#page=2)")},
+            {"https://xmlgraphics.apache.org/fop/fo/fonts.fo", Pattern.quote("<< /URI (https://xmlgraphics.apache.org/fop/fo/fonts.fo)")},
+
+            // HTML files
+            {"examples.html#foo", Pattern.quote("<< /URI (examples.html#foo)")}, //177
+            {"examples.html?foo#bar", Pattern.quote("/URI (examples.html?foo#bar)")},
+            {"examples.html", Pattern.quote("<< /URI (examples.html)")},
+            {"file:examples.html", Pattern.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());
+    }
+}
