You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by je...@apache.org on 2010/08/16 11:39:35 UTC

svn commit: r985843 - in /xmlgraphics/fop/trunk/src/java/org/apache/fop: pdf/ render/pdf/

Author: jeremias
Date: Mon Aug 16 09:39:34 2010
New Revision: 985843

URL: http://svn.apache.org/viewvc?rev=985843&view=rev
Log:
Improvements/Bugfixes for PDF attachments (embedded files):
- Acrobat doesn't like indirect objects for the /Length entry of embedded files (make on-the-fly generation more easily configurable).
- Added the /Size entry for embedded files to keep Acrobat happy.
- Moved the embedded files' Names tree to the end of the PDF to be on the safe side.
- Made hyperlinks to embedded files work by using a JavaScript Action (thanks to Matthias Reischenbacher for that idea).
- Added sorting to the names tree node because Acrobat relies on it.
- Added some filename manipulation code to work around possible encoding problems with special characters in the filenames.

Added:
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java   (with props)
Modified:
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/AbstractPDFStream.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFactory.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFileSpec.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFGoToRemote.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFNameTreeNode.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFText.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/AbstractPDFStream.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/AbstractPDFStream.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/AbstractPDFStream.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/AbstractPDFStream.java Mon Aug 16 09:39:34 2010
@@ -191,7 +191,7 @@ public abstract class AbstractPDFStream 
         StreamCache encodedStream = null;
         PDFNumber refLength = null;
         final Object lengthEntry;
-        if (getDocument().isEncodingOnTheFly()) {
+        if (isEncodingOnTheFly()) {
             refLength = new PDFNumber();
             getDocumentSafely().registerObject(refLength);
             lengthEntry = refLength;
@@ -218,6 +218,15 @@ public abstract class AbstractPDFStream 
     }
 
     /**
+     * Indicates whether encoding may happen without buffering the encoded data. If this method
+     * returns true, the /Length entry will be an indirect object, a direct object otherwise.
+     * @return true if encoding should happen "on the fly"
+     */
+    protected boolean isEncodingOnTheFly() {
+        return getDocument().isEncodingOnTheFly();
+    }
+
+    /**
      * Populates the dictionary with all necessary entries for the stream.
      * Override this method if you need additional entries.
      * @param lengthEntry value for the /Length entry

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFArray.java Mon Aug 16 09:39:34 2010
@@ -172,6 +172,13 @@ public class PDFArray extends PDFObject 
         this.values.add(new Double(value));
     }
 
+    /**
+     * Clears the PDF array.
+     */
+    public void clear() {
+        this.values.clear();
+    }
+
     /** {@inheritDoc} */
     protected int output(OutputStream stream) throws IOException {
         CountingOutputStream cout = new CountingOutputStream(stream);

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java Mon Aug 16 09:39:34 2010
@@ -19,6 +19,7 @@
 
 package org.apache.fop.pdf;
 
+import java.io.IOException;
 import java.util.Date;
 
 /**
@@ -37,4 +38,22 @@ public class PDFEmbeddedFile extends PDF
         put("Params", params);
     }
 
+    /** {@inheritDoc} */
+    protected boolean isEncodingOnTheFly() {
+        //Acrobat doesn't like an indirect /Length object in this case,
+        //but only when the embedded file is a PDF file.
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    protected void populateStreamDict(Object lengthEntry) {
+        super.populateStreamDict(lengthEntry);
+        try {
+            PDFDictionary dict = (PDFDictionary)get("Params");
+            dict.put("Size", new Integer(data.getSize()));
+        } catch (IOException ioe) {
+            //ignore and just skip this entry as it's optional
+        }
+    }
+
 }

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFactory.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFactory.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFactory.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFactory.java Mon Aug 16 09:39:34 2010
@@ -877,7 +877,8 @@ public class PDFFactory {
      */
     public PDFNames makeNames() {
         PDFNames names = new PDFNames();
-        getDocument().registerObject(names);
+        getDocument().assignObjectNumber(names);
+        getDocument().addTrailerObject(names);
         return names;
     }
 
@@ -1067,7 +1068,7 @@ public class PDFFactory {
         if (target.startsWith(EMBEDDED_FILE)) {
             // File Attachments (Embedded Files)
             String filename = target.substring(EMBEDDED_FILE.length());
-            return getLaunchActionForEmbeddedFile(filename);
+            return getActionForEmbeddedFile(filename, newWindow);
         } else if (targetLo.startsWith("http://")) {
             // HTTP URL?
             return new PDFUri(target);
@@ -1094,7 +1095,7 @@ public class PDFFactory {
         }
     }
 
-    private PDFAction getLaunchActionForEmbeddedFile(String filename) {
+    private PDFAction getActionForEmbeddedFile(String filename, boolean newWindow) {
         PDFNames names = getDocument().getRoot().getNames();
         if (names == null) {
             throw new IllegalStateException(
@@ -1107,6 +1108,9 @@ public class PDFFactory {
                     "No /EmbeddedFiles name tree present."
                     + " Cannot create Launch Action for embedded file: " + filename);
         }
+
+        //Find filespec reference for the embedded file
+        filename = PDFText.toPDFString(filename, '_');
         PDFArray files = embeddedFiles.getNames();
         PDFReference embeddedFileRef = null;
         int i = 0;
@@ -1124,8 +1128,23 @@ public class PDFFactory {
             throw new IllegalStateException(
                     "No embedded file with name " + filename + " present.");
         }
-        PDFLaunch launch = new PDFLaunch(embeddedFileRef);
-        return launch;
+
+        //Finally create the action
+        //PDFLaunch action = new PDFLaunch(embeddedFileRef);
+        //This works with Acrobat 8 but not with Acrobat 9
+
+        //The following two options didn't seem to have any effect.
+        //PDFGoToEmbedded action = new PDFGoToEmbedded(embeddedFileRef, 0, newWindow);
+        //PDFGoToRemote action = new PDFGoToRemote(embeddedFileRef, 0, newWindow);
+
+        //This finally seems to work:
+        StringBuffer scriptBuffer = new StringBuffer();
+        scriptBuffer.append("this.exportDataObject({cName:\"");
+        scriptBuffer.append(filename);
+        scriptBuffer.append("\", nLaunch:2});)");
+
+        PDFJavaScriptLaunchAction action = new PDFJavaScriptLaunchAction(scriptBuffer.toString());
+        return action;
     }
 
     /**

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFileSpec.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFileSpec.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFileSpec.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFFileSpec.java Mon Aug 16 09:39:34 2010
@@ -19,10 +19,8 @@
 
 package org.apache.fop.pdf;
 
-
 /**
- * class representing a /FileSpec object.
- *
+ * Class representing a /FileSpec object.
  */
 public class PDFFileSpec extends PDFDictionary {
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFGoToRemote.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFGoToRemote.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFGoToRemote.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFGoToRemote.java Mon Aug 16 09:39:34 2010
@@ -27,7 +27,7 @@ public class PDFGoToRemote extends PDFAc
     /**
      * the file specification
      */
-    private PDFFileSpec pdfFileSpec;
+    private PDFReference pdfFileSpec;
     private int pageReference = 0;
     private String destination = null;
     private boolean newWindow = false;
@@ -43,12 +43,12 @@ public class PDFGoToRemote extends PDFAc
         /* generic creation of object */
         super();
 
-        this.pdfFileSpec = pdfFileSpec;
+        this.pdfFileSpec = pdfFileSpec.makeReference();
         this.newWindow = newWindow;
     }
 
     /**
-     * create an GoToR object.
+     * Create an GoToR object.
      *
      * @param pdfFileSpec the fileSpec associated with the action
      * @param page a page reference within the remote document
@@ -56,7 +56,18 @@ public class PDFGoToRemote extends PDFAc
      *                  displayed in a new window
      */
     public PDFGoToRemote(PDFFileSpec pdfFileSpec, int page, boolean newWindow) {
-        /* generic creation of object */
+        this(pdfFileSpec.makeReference(), page, newWindow);
+    }
+
+    /**
+     * Create an GoToR object.
+     *
+     * @param pdfFileSpec the fileSpec associated with the action
+     * @param page a page reference within the remote document
+     * @param newWindow boolean indicating whether the target should be
+     *                  displayed in a new window
+     */
+    public PDFGoToRemote(PDFReference pdfFileSpec, int page, boolean newWindow) {
         super();
 
         this.pdfFileSpec = pdfFileSpec;
@@ -76,7 +87,7 @@ public class PDFGoToRemote extends PDFAc
         /* generic creation of object */
         super();
 
-        this.pdfFileSpec = pdfFileSpec;
+        this.pdfFileSpec = pdfFileSpec.makeReference();
         this.destination = dest;
         this.newWindow = newWindow;
     }
@@ -97,7 +108,7 @@ public class PDFGoToRemote extends PDFAc
         StringBuffer sb = new StringBuffer(64);
         sb.append(getObjectID());
         sb.append("<<\n/S /GoToR\n/F ");
-        sb.append(pdfFileSpec.referencePDF());
+        sb.append(pdfFileSpec.toString());
         sb.append("\n");
 
         if (destination != null) {
@@ -139,7 +150,7 @@ public class PDFGoToRemote extends PDFAc
 
         PDFGoToRemote remote = (PDFGoToRemote)obj;
 
-        if (!remote.pdfFileSpec.referencePDF().equals(pdfFileSpec.referencePDF())) {
+        if (!remote.pdfFileSpec.toString().equals(pdfFileSpec.toString())) {
             return false;
         }
 

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java?rev=985843&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java Mon Aug 16 09:39:34 2010
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+/**
+ * PDF Action which executes some JavaScript code.
+ * @since PDF 1.3
+ */
+public class PDFJavaScriptLaunchAction extends PDFAction {
+
+    private String script;
+
+    /**
+     * Creates a new /Launch action.
+     * @param script  the script to run when the launch action is triggered
+     */
+    public PDFJavaScriptLaunchAction(String script) {
+        this.script = script;
+    }
+
+    /** {@inheritDoc} */
+    public String getAction() {
+        return this.referencePDF();
+    }
+
+    /** {@inheritDoc} */
+    public String toPDFString() {
+        StringBuffer sb = new StringBuffer(64);
+        sb.append(getObjectID());
+        sb.append("<<\n/S /JavaScript\n/JS (");
+        sb.append(this.script);
+        sb.append(")\n>>\nendobj\n");
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    protected boolean contentEquals(PDFObject obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null || !(obj instanceof PDFJavaScriptLaunchAction)) {
+            return false;
+        }
+
+        PDFJavaScriptLaunchAction launch = (PDFJavaScriptLaunchAction) obj;
+
+        if (!launch.script.toString().equals(script.toString())) {
+            return false;
+        }
+
+        return true;
+    }
+
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFJavaScriptLaunchAction.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFNameTreeNode.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFNameTreeNode.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFNameTreeNode.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFNameTreeNode.java Mon Aug 16 09:39:34 2010
@@ -19,6 +19,14 @@
 
 package org.apache.fop.pdf;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
 /**
  * Class representing a PDF name tree node.
  */
@@ -104,7 +112,6 @@ public class PDFNameTreeNode extends PDF
         return (String)limits.get(1);
     }
 
-
     private PDFArray prepareLimitsArray() {
         PDFArray limits = (PDFArray)get(LIMITS);
         if (limits == null) {
@@ -117,5 +124,29 @@ public class PDFNameTreeNode extends PDF
         return limits;
     }
 
+    /** {@inheritDoc} */
+    protected void writeDictionary(OutputStream out, Writer writer) throws IOException {
+        sortNames(); //Sort the names before writing them out
+        super.writeDictionary(out, writer);
+    }
+
+    private void sortNames() {
+        PDFArray names = getNames();
+        SortedMap map = new TreeMap();
+        int i = 0;
+        int c = names.length();
+        while (i < c) {
+            String key = (String)names.get(i++); //Key must be a String
+            Object value = names.get(i++);
+            map.put(key, value);
+        }
+        names.clear();
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry)iter.next();
+            names.add(entry.getKey());
+            names.add(entry.getValue());
+        }
+    }
 }
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFText.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFText.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFText.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/pdf/PDFText.java Mon Aug 16 09:39:34 2010
@@ -320,5 +320,41 @@ public class PDFText extends PDFObject {
         return bout.toByteArray();
     }
 
+    /**
+     * Converts a text to PDF's "string" data type. Unsupported characters get converted to '?'
+     * characters (similar to what the Java "US-ASCII" encoding does).
+     * @see {@link #toPDFString(CharSequence, char)}
+     * @param text the text to convert
+     * @return the converted string
+     */
+    public static String toPDFString(CharSequence text) {
+        return toPDFString(text, '?');
+    }
+
+    /**
+     * Converts a text to PDF's "string" data type. Unsupported characters get converted to the
+     * given replacement character.
+     * <p>
+     * The PDF library currently doesn't properly distinguish between the PDF
+     * data types "string" and "text string", so we currently restrict "string" to US-ASCII, also
+     * because "string" seems somewhat under-specified concerning the upper 128 bytes.
+     * @param text the text to convert
+     * @param replacement the replacement character used when substituting a character
+     * @return the converted string
+     */
+    public static String toPDFString(CharSequence text, char replacement) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0, c = text.length(); i < c; i++) {
+            char ch = text.charAt(i);
+            if (ch > 127) {
+                //TODO Revisit the restriction to US-ASCII once "string" and "text string" are
+                //"disentangled".
+                sb.append(replacement);
+            } else {
+                sb.append(ch);
+            }
+        }
+        return sb.toString();
+    }
 }
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java?rev=985843&r1=985842&r2=985843&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java Mon Aug 16 09:39:34 2010
@@ -61,6 +61,7 @@ import org.apache.fop.pdf.PDFNumsArray;
 import org.apache.fop.pdf.PDFOutputIntent;
 import org.apache.fop.pdf.PDFPageLabels;
 import org.apache.fop.pdf.PDFReference;
+import org.apache.fop.pdf.PDFText;
 import org.apache.fop.pdf.PDFXMode;
 import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
 import org.apache.fop.util.ColorProfileUtil;
@@ -453,7 +454,8 @@ class PDFRenderingUtil implements PDFCon
         }
         PDFDictionary dict = new PDFDictionary();
         dict.put("F", file);
-        PDFFileSpec fileSpec = new PDFFileSpec(embeddedFile.getFilename());
+        String filename = PDFText.toPDFString(embeddedFile.getFilename(), '_');
+        PDFFileSpec fileSpec = new PDFFileSpec(filename);
         fileSpec.setEmbeddedFile(dict);
         if (embeddedFile.getDesc() != null) {
             fileSpec.setDescription(embeddedFile.getDesc());
@@ -464,7 +466,8 @@ class PDFRenderingUtil implements PDFCon
         PDFNameTreeNode embeddedFiles = names.getEmbeddedFiles();
         if (embeddedFiles == null) {
             embeddedFiles = new PDFNameTreeNode();
-            //this.pdfDoc.registerObject(embeddedFiles);
+            this.pdfDoc.assignObjectNumber(embeddedFiles);
+            this.pdfDoc.addTrailerObject(embeddedFiles);
             names.setEmbeddedFiles(embeddedFiles);
         }
 
@@ -474,7 +477,8 @@ class PDFRenderingUtil implements PDFCon
             nameArray = new PDFArray();
             embeddedFiles.setNames(nameArray);
         }
-        nameArray.add(embeddedFile.getFilename());
+        String name = PDFText.toPDFString(filename);
+        nameArray.add(name);
         nameArray.add(new PDFReference(fileSpec));
     }
 



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org