]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Added basic support for PDF page labels.
authorJeremias Maerki <jeremias@apache.org>
Thu, 10 Jan 2008 07:38:47 +0000 (07:38 +0000)
committerJeremias Maerki <jeremias@apache.org>
Thu, 10 Jan 2008 07:38:47 +0000 (07:38 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@610704 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/pdf/PDFDictionary.java
src/java/org/apache/fop/pdf/PDFFactory.java
src/java/org/apache/fop/pdf/PDFNumberTreeNode.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFNumsArray.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFPageLabels.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFRoot.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
status.xml

index 71393cace64d671743635960492ec6062aaf96fc..c183871b555f4ccbf5f533f9b047813a56f4d397 100644 (file)
@@ -101,16 +101,26 @@ public class PDFDictionary extends PDFObject {
      */
     protected void writeDictionary(StringBuffer sb) {
         sb.append("<<");
+        boolean compact = (this.order.size() <= 2);
         Iterator iter = this.order.iterator();
         while (iter.hasNext()) {
             String key = (String)iter.next();
-            sb.append("\n  /");
-            sb.append(key);
-            sb.append(" ");
+            if (compact) {
+                sb.append(' ');
+            } else {
+                sb.append("\n  ");
+            }
+            sb.append('/').append(key);
+            sb.append(' ');
             Object obj = this.entries.get(key);
             formatObject(obj, sb);
         }
-        sb.append("\n>>\n");
+        if (compact) {
+            sb.append(' ');
+        } else {
+            sb.append('\n');
+        }
+        sb.append(">>\n");
     }
 
 }
index e1546baedd8858c262f8c278beef65c300f9cb51..f851abae712dab579a42f6d10e6cfed0170244f3 100644 (file)
@@ -848,6 +848,17 @@ public class PDFFactory {
         return names;
     }
 
+    /**
+     * Make a names dictionary (the /PageLabels object).
+     * @return the new PDFPageLabels object
+     */
+    public PDFPageLabels makePageLabels() {
+        PDFPageLabels pageLabels = new PDFPageLabels();
+        getDocument().assignObjectNumber(pageLabels);
+        getDocument().addTrailerObject(pageLabels);
+        return pageLabels;
+    }
+
     /**
      * Make a the head object of the name dictionary (the /Dests object).
      *
diff --git a/src/java/org/apache/fop/pdf/PDFNumberTreeNode.java b/src/java/org/apache/fop/pdf/PDFNumberTreeNode.java
new file mode 100644 (file)
index 0000000..4e69c01
--- /dev/null
@@ -0,0 +1,121 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+/**\r
+ * Class representing a PDF number tree node.\r
+ */\r
+public class PDFNumberTreeNode extends PDFDictionary {\r
+\r
+    private static final String KIDS = "Kids";\r
+    private static final String NUMS = "Nums";\r
+    private static final String LIMITS = "Limits";\r
+\r
+    /**\r
+     * create a named destination\r
+     */\r
+    public PDFNumberTreeNode() {\r
+        /* generic creation of PDF object */\r
+        super();\r
+    }\r
+\r
+    /**\r
+     * Sets the Kids array.\r
+     * @param kids the Kids array\r
+     */\r
+    public void setKids(PDFArray kids) {\r
+        put(KIDS, kids);\r
+    }\r
+    \r
+    /**\r
+     * Returns the Kids array.\r
+     * @return the Kids array\r
+     */\r
+    public PDFArray getKids() {\r
+        return (PDFArray)get(KIDS);\r
+    }\r
+    \r
+    /**\r
+     * Sets the Nums array.\r
+     * @param nums the Nums array\r
+     */\r
+    public void setNums(PDFNumsArray nums) {\r
+        put(NUMS, nums);\r
+    }\r
+    \r
+    /**\r
+     * Returns the Nums array.\r
+     * @return the Nums array\r
+     */\r
+    public PDFNumsArray getNums() {\r
+        return (PDFNumsArray)get(NUMS);\r
+    }\r
+    \r
+    /**\r
+     * Sets the lower limit value of the Limits array.\r
+     * @param key the lower limit value\r
+     */\r
+    public void setLowerLimit(Integer key) {\r
+        PDFArray limits = prepareLimitsArray();\r
+        limits.set(0, key);\r
+    }\r
+\r
+    /**\r
+     * Returns the lower limit value of the Limits array.\r
+     * @return the lower limit value\r
+     */\r
+    public Integer getLowerLimit() {\r
+        PDFArray limits = prepareLimitsArray();\r
+        return (Integer)limits.get(0);\r
+    }\r
+\r
+    /**\r
+     * Sets the upper limit value of the Limits array.\r
+     * @param key the upper limit value\r
+     */\r
+    public void setUpperLimit(Integer key) {\r
+        PDFArray limits = prepareLimitsArray();\r
+        limits.set(1, key);\r
+    }\r
+\r
+    /**\r
+     * Returns the upper limit value of the Limits array.\r
+     * @return the upper limit value\r
+     */\r
+    public Integer getUpperLimit() {\r
+        PDFArray limits = prepareLimitsArray();\r
+        return (Integer)limits.get(1);\r
+    }\r
+\r
+\r
+    private PDFArray prepareLimitsArray() {\r
+        PDFArray limits = (PDFArray)get(LIMITS);\r
+        if (limits == null) {\r
+            limits = new PDFArray(new Object[2]);\r
+            put(LIMITS, limits);\r
+        }\r
+        if (limits.length() != 2) {\r
+            throw new IllegalStateException("Limits array must have 2 entries");\r
+        }\r
+        return limits;\r
+    }\r
+    \r
+}\r
+\r
diff --git a/src/java/org/apache/fop/pdf/PDFNumsArray.java b/src/java/org/apache/fop/pdf/PDFNumsArray.java
new file mode 100644 (file)
index 0000000..55f973c
--- /dev/null
@@ -0,0 +1,94 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id: PDFArray.java 588547 2007-10-26 07:48:14Z jeremias $ */\r
\r
+package org.apache.fop.pdf;\r
+\r
+import java.util.Iterator;\r
+import java.util.Map;\r
+import java.util.SortedMap;\r
+\r
+/**\r
+ * Class representing an "Nums" array object (for Number Trees).\r
+ */\r
+public class PDFNumsArray extends PDFObject {\r
+    \r
+    /** Sorted Map holding the values of this array. */\r
+    protected SortedMap map = new java.util.TreeMap();\r
+\r
+    /**\r
+     * Create a new, empty array object\r
+     */\r
+    public PDFNumsArray() {\r
+        /* generic creation of PDF object */\r
+        super();\r
+    }\r
+\r
+    /**\r
+     * Returns the length of the array\r
+     * @return the length of the array\r
+     */\r
+    public int length() {\r
+        return this.map.size();\r
+    }\r
+    \r
+    /**\r
+     * Sets an entry.\r
+     * @param key the key of the value to set\r
+     * @param obj the new value\r
+     */\r
+    public void put(int key, Object obj) {\r
+        this.map.put(new Integer(key), obj);\r
+    }\r
+    \r
+    /**\r
+     * Gets an entry.\r
+     * @param key the key of requested value\r
+     * @return the requested value\r
+     */\r
+    public Object get(int key) {\r
+        return this.map.get(new Integer(key));\r
+    }\r
+    \r
+    /** {@inheritDoc} */\r
+    public String toPDFString() {\r
+        StringBuffer p = new StringBuffer(64);\r
+        if (hasObjectNumber()) {\r
+            p.append(getObjectID());\r
+        }\r
+        p.append("[");\r
+        boolean first = true;\r
+        Iterator iter = this.map.entrySet().iterator();\r
+        while (iter.hasNext()) {\r
+            Map.Entry entry = (Map.Entry)iter.next();\r
+            if (!first) {\r
+                p.append(" ");\r
+            }\r
+            first = false;\r
+            formatObject(entry.getKey(), p);\r
+            p.append(" ");\r
+            formatObject(entry.getValue(), p);\r
+        }\r
+        p.append("]");\r
+        if (hasObjectNumber()) {\r
+            p.append("\nendobj\n");\r
+        }\r
+        return p.toString();\r
+    }\r
+\r
+}\r
diff --git a/src/java/org/apache/fop/pdf/PDFPageLabels.java b/src/java/org/apache/fop/pdf/PDFPageLabels.java
new file mode 100644 (file)
index 0000000..bb02a3c
--- /dev/null
@@ -0,0 +1,48 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ * \r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
\r
+package org.apache.fop.pdf;\r
+\r
+/**\r
+ * Class representing a PDF /PageLabels dictionary.\r
+ */\r
+public class PDFPageLabels extends PDFNumberTreeNode {\r
+    \r
+    /**\r
+     * Create the /PageLabels dictionary\r
+     */\r
+    public PDFPageLabels() {\r
+        super();\r
+    }\r
+\r
+    /**\r
+     * Returns the Nums object\r
+     * @return the Nums object (an empty PDFNumsArray for the "/Nums" entry is created\r
+     *       if it doesn't exist)\r
+     */\r
+    public PDFNumsArray getNums() {\r
+        PDFNumsArray nums = super.getNums();\r
+        if (nums == null) {\r
+            nums = new PDFNumsArray();\r
+            setNums(nums);\r
+        }\r
+        return nums;\r
+    }\r
+    \r
+}\r
index 64103df22806a2bd43a19f4e77ab576726dc3926..1a54a209ffa6e8ce9cf41a7952d896e8e158e1ae 100644 (file)
  
 package org.apache.fop.pdf;
 
-import java.util.List;
-
 /**
- * class representing a Root (/Catalog) object
+ * Class representing a Root (/Catalog) object.
  */
-public class PDFRoot extends PDFObject {
+public class PDFRoot extends PDFDictionary {
 
     /**
      * Use no page mode setting, default
@@ -46,27 +44,13 @@ public class PDFRoot extends PDFObject {
      */
     public static final int PAGEMODE_FULLSCREEN = 3;
 
-    /**
-     * the /Pages object that is root of the Pages hierarchy
-     */
-    protected PDFPages rootPages;
-
-    /**
-     * Root outline object
-     */
-    private PDFOutline outline;
-
-    /** Optional Metadata object */
-    private PDFMetadata metadata;
+    private static final PDFName[] PAGEMODE_NAMES = new PDFName[] {
+        new PDFName("UseNone"),
+        new PDFName("UseOutlines"),
+        new PDFName("UseThumbs"),
+        new PDFName("FullScreen"),
+    };
     
-    /** The array of OutputIntents */
-    private List outputIntents;
-    
-    /** the /Dests object, if this PDF has a Names Dictionary */
-    private PDFNames names;
-
-    private int pageMode = PAGEMODE_USENONE;
-
     /**
      * create a Root (/Catalog) object. NOTE: The PDFRoot
      * object must be created before the PDF document is
@@ -80,25 +64,45 @@ public class PDFRoot extends PDFObject {
     public PDFRoot(int objnum, PDFPages pages) {
         super();
         setObjectNumber(objnum);
+        put("Type", new PDFName("Catalog"));
         setRootPages(pages);
     }
 
     /**
      * Set the page mode for the PDF document.
      *
-     * @param mode the page mode
+     * @param mode the page mode (one of PAGEMODE_*)
      */
     public void setPageMode(int mode) {
-        pageMode = mode;
+        put("PageMode", PAGEMODE_NAMES[mode]);
     }
 
+    /**
+     * Returns the currently active /PageMode.
+     * @return the /PageMode (one of PAGEMODE_*)
+     */
+    public int getPageMode() {
+        PDFName mode = (PDFName)get("PageMode");
+        if (mode != null) {
+            for (int i = 0; i < PAGEMODE_NAMES.length; i++) {
+                if (PAGEMODE_NAMES[i].equals(mode)) {
+                    return i;
+                }
+            }
+            throw new IllegalStateException("Unknown /PageMode encountered: " + mode);
+        } else {
+            return PAGEMODE_USENONE;
+        }
+    }
+    
     /**
      * add a /Page object to the root /Pages object
      *
      * @param page the /Page object to add
      */
     public void addPage(PDFPage page) {
-        this.rootPages.addPage(page);
+        PDFPages pages = getRootPages();
+        pages.addPage(page);
     }
 
     /**
@@ -107,16 +111,50 @@ public class PDFRoot extends PDFObject {
      * @param pages the /Pages object to set as root
      */
     public void setRootPages(PDFPages pages) {
-        this.rootPages = pages;
+        put("Pages", pages.makeReference());
     }
 
+    /**
+     * Returns the /PageLabels object.
+     * @return the /PageLabels object if set, null otherwise.
+     * @since PDF 1.3
+     */
+    public PDFPages getRootPages() {
+        PDFReference ref = (PDFReference)get("Pages");
+        return (ref != null ? (PDFPages)ref.getObject() : null);
+    }
+    
+    /**
+     * Sets the /PageLabels object.
+     * @param pageLabels the /PageLabels object
+     */
+    public void setPageLabels(PDFPageLabels pageLabels) {
+        put("PageLabels", pageLabels.makeReference());
+    }
+    
+    /**
+     * Returns the /PageLabels object.
+     * @return the /PageLabels object if set, null otherwise.
+     * @since PDF 1.3
+     */
+    public PDFPageLabels getPageLabels() {
+        PDFReference ref = (PDFReference)get("PageLabels");
+        return (ref != null ? (PDFPageLabels)ref.getObject() : null);
+    }
+    
     /**
      * Set the root outline for the PDF document.
      *
      * @param out the root PDF Outline
      */
     public void setRootOutline(PDFOutline out) {
-        outline = out;
+        put("Outlines", out.makeReference());
+        
+        //Set /PageMode to /UseOutlines by default if no other mode has been set
+        PDFName mode = (PDFName)get("PageMode");
+        if (mode == null) {
+            setPageMode(PAGEMODE_USEOUTLINES);
+        }
     }
 
     /**
@@ -125,24 +163,27 @@ public class PDFRoot extends PDFObject {
      * @return the root PDF Outline
      */
     public PDFOutline getRootOutline() {
-        return outline;
+        PDFReference ref = (PDFReference)get("Outlines");
+        return (ref != null ? (PDFOutline)ref.getObject() : null);
     }
     
     /**
-     * Set the Names object.
+     * Set the /Names object.
      * @param names the Names object
      * @since PDF 1.2
      */
     public void setNames(PDFNames names) {
-        this.names = names;
+        put("Names", names.makeReference());
     }
     
     /**
+     * Returns the /Names object.
      * @return the Names object if set, null otherwise.
      * @since PDF 1.2
      */
     public PDFNames getNames() {
-        return this.names;
+        PDFReference ref = (PDFReference)get("Names");
+        return (ref != null ? (PDFNames)ref.getObject() : null);
     }
     
     /**
@@ -151,78 +192,44 @@ public class PDFRoot extends PDFObject {
      * @since PDF 1.4
      */
     public void setMetadata(PDFMetadata meta) {
-        this.metadata = meta;
+        if (getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) {
+            put("Metadata", meta.makeReference());
+        }
     }
     
     /**
-     * @return the Metadata object if set, null otherwise.
+     * Returns the /Metadata object
+     * @return the /Metadata object if set, null otherwise.
      * @since PDF 1.4
      */
     public PDFMetadata getMetadata() {
-        return this.metadata;
+        PDFReference ref = (PDFReference)get("Metadata");
+        return (ref != null ? (PDFMetadata)ref.getObject() : null);
     }
 
     /**
-     * Adds an OutputIntent to the PDF
-     * @param outputIntent the OutputIntent dictionary
+     * Returns the /OutputIntents array.
+     * @return the /OutputIntents array or null if it doesn't exist
+     * @since PDF 1.4
      */
-    public void addOutputIntent(PDFOutputIntent outputIntent) {
-        if (this.outputIntents == null) {
-            this.outputIntents = new java.util.ArrayList();
-        }
-        this.outputIntents.add(outputIntent);
+    public PDFArray getOutputIntents() {
+        return (PDFArray)get("OutputIntents");
     }
     
     /**
-     * {@inheritDoc}
-     */
-    public String toPDFString() {
-        StringBuffer p = new StringBuffer(128);
-        p.append(getObjectID());
-        p.append("<< /Type /Catalog\n /Pages "
-                + this.rootPages.referencePDF()
-                + "\n");
-        if (outline != null) {
-            p.append(" /Outlines " + outline.referencePDF() + "\n");
-            p.append(" /PageMode /UseOutlines\n");
-        } else {
-            switch (pageMode) {
-                case PAGEMODE_USEOUTLINES:
-                    p.append(" /PageMode /UseOutlines\n");
-                break;
-                case PAGEMODE_USETHUMBS:
-                    p.append(" /PageMode /UseThumbs\n");
-                break;
-                case PAGEMODE_FULLSCREEN:
-                    p.append(" /PageMode /FullScreen\n");
-                break;
-                case PAGEMODE_USENONE:
-                default:
-                break;
-            }
-        }
-        if (getDocumentSafely().hasDestinations() && getNames() != null) {
-            p.append(" /Names " + getNames().referencePDF() + "\n");
-        }
-        if (getMetadata() != null 
-                && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) {
-            p.append(" /Metadata " + getMetadata().referencePDF() + "\n");
-        }
-        if (this.outputIntents != null 
-                && this.outputIntents.size() > 0
-                && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) {
-            p.append(" /OutputIntents [");
-            for (int i = 0, c = this.outputIntents.size(); i < c; i++) {
-                PDFOutputIntent outputIntent = (PDFOutputIntent)this.outputIntents.get(i);
-                if (i > 0) {
-                    p.append(" ");
-                }
-                p.append(outputIntent.referencePDF());
+     * Adds an OutputIntent to the PDF
+     * @param outputIntent the OutputIntent dictionary
+     * @since PDF 1.4
+     */
+    public void addOutputIntent(PDFOutputIntent outputIntent) {
+        if (getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) {
+            PDFArray outputIntents = getOutputIntents(); 
+            if (outputIntents == null) {
+                outputIntents = new PDFArray();
+                put("OutputIntents", outputIntents);
             }
-            p.append("]\n");
+            outputIntents.add(outputIntent);
         }
-        p.append(">>\nendobj\n");
-        return p.toString();
     }
-
+    
 }
index a031796924e984110215c43b89f25ebb7dcfb78e..2d8e7d5bac06efadc90c86e7d3f833c3030c44c8 100644 (file)
@@ -73,6 +73,7 @@ import org.apache.fop.pdf.PDFAction;
 import org.apache.fop.pdf.PDFAnnotList;
 import org.apache.fop.pdf.PDFColor;
 import org.apache.fop.pdf.PDFConformanceException;
+import org.apache.fop.pdf.PDFDictionary;
 import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFEncryptionManager;
 import org.apache.fop.pdf.PDFEncryptionParams;
@@ -88,6 +89,7 @@ import org.apache.fop.pdf.PDFNumber;
 import org.apache.fop.pdf.PDFOutline;
 import org.apache.fop.pdf.PDFOutputIntent;
 import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFPageLabels;
 import org.apache.fop.pdf.PDFResourceContext;
 import org.apache.fop.pdf.PDFResources;
 import org.apache.fop.pdf.PDFState;
@@ -715,6 +717,19 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
             page.getPageIndex());
         pageReferences.put(page.getKey(), currentPage.referencePDF());
         pvReferences.put(page.getKey(), page);
+        
+        //Produce page labels
+        PDFPageLabels pageLabels = this.pdfDoc.getRoot().getPageLabels();
+        if (pageLabels == null) {
+            //Set up PageLabels
+            pageLabels = this.pdfDoc.getFactory().makePageLabels();
+            this.pdfDoc.getRoot().setPageLabels(pageLabels);
+        }
+        PDFDictionary dict = new PDFDictionary();
+        dict.put("P", page.getPageNumberString());
+        //TODO If the sequence of generated page numbers were inspected, this could be
+        //expressed in a more space-efficient way
+        pageLabels.getNums().put(page.getPageIndex(), dict);
     }
     
     /**
index 1c2f18b4bc10b6aa44e3a938c0c9d38794709367..3b88812115011f241dde0a7175b6c066834ea166 100644 (file)
@@ -28,6 +28,9 @@
 
   <changes>
     <release version="FOP Trunk">
+      <action context="Code" dev="JM" type="add">
+        Added support for PDF page labels.
+      </action>
       <action context="Code" dev="JM" type="add" fixes-bug="44176" due-to="Patrick Jaromin">
         Added support for custom fonts in Java2DRenderer and derived renderers.
       </action>