pdfbox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r908623 - in /pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel: PDDocumentCatalog.java common/PDNumberTreeNode.java common/PDPageLabelRange.java common/PDPageLabels.java
Date Wed, 10 Feb 2010 18:33:08 GMT
Author: jukka
Date: Wed Feb 10 18:32:58 2010
New Revision: 908623

URL: http://svn.apache.org/viewvc?rev=908623&view=rev
Log:
PDFBOX-90: Support explicit retrieval of page labels

Patch by Igor Podolskiy.

Added:
    pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java   (with
props)
    pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabelRange.java   (with
props)
    pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java   (with
props)
Modified:
    pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java

Modified: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java?rev=908623&r1=908622&r2=908623&view=diff
==============================================================================
--- pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java (original)
+++ pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/PDDocumentCatalog.java Wed Feb 10
18:32:58 2010
@@ -31,6 +31,7 @@
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.pdmodel.common.PDDestinationOrAction;
 import org.apache.pdfbox.pdmodel.common.PDMetadata;
+import org.apache.pdfbox.pdmodel.common.PDPageLabels;
 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
 import org.apache.pdfbox.pdmodel.interactive.action.type.PDURIDictionary;
@@ -549,4 +550,33 @@
     {
         root.setString( "Lang", language );
     }
+
+    /**
+     * Returns the page labels descriptor of the document.
+     * 
+     * @return the page labels descriptor of the document.
+     * 
+     * @throws IOException If there is a problem retrieving the page labels.
+     */
+    public PDPageLabels getPageLabels() throws IOException 
+    {
+        PDPageLabels labels = null;
+        COSDictionary dict = (COSDictionary) root.getDictionaryObject("PageLabels");
+        if (dict != null) 
+        {
+            labels = new PDPageLabels(document, dict);
+        }
+        return labels;
+    }
+
+    /**
+     * Set the page label descriptor for the document.
+     *
+     * @param labels the new page label descriptor to set.
+     */
+    public void setPageLabels(PDPageLabels labels) 
+    {
+        root.setItem("PageLabels", labels);
+    }
+
 }

Added: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java?rev=908623&view=auto
==============================================================================
--- pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java (added)
+++ pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java Wed
Feb 10 18:32:58 2010
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.common;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+
+/**
+ * This class represents a PDF Number tree. See the PDF Reference 1.7 section
+ * 7.9.7 for more details.
+ * 
+ * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>,
+ *         <a href="igor.podolskiy@ievvwi.uni-stuttgart.de">Igor Podolskiy</a>
+ * @version $Revision: 1.4 $
+ */
+public class PDNumberTreeNode implements COSObjectable
+{
+    private COSDictionary node;
+    private Class valueType = null;
+
+    /**
+     * Constructor.
+     *
+     * @param valueClass The PD Model type of object that is the value.
+     */
+    public PDNumberTreeNode( Class valueClass )
+    {
+        node = new COSDictionary();
+        valueType = valueClass;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param dict The dictionary that holds the name information.
+     * @param valueClass The PD Model type of object that is the value.
+     */
+    public PDNumberTreeNode( COSDictionary dict, Class valueClass )
+    {
+        node = dict;
+        valueType = valueClass;
+    }
+
+    /**
+     * Convert this standard java object to a COS object.
+     *
+     * @return The cos object that matches this Java object.
+     */
+    public COSBase getCOSObject()
+    {
+        return node;
+    }
+
+    /**
+     * Convert this standard java object to a COS object.
+     *
+     * @return The cos object that matches this Java object.
+     */
+    public COSDictionary getCOSDictionary()
+    {
+        return node;
+    }
+
+    /**
+     * Return the children of this node.  This list will contain PDNumberTreeNode objects.
+     *
+     * @return The list of children or null if there are no children.
+     */
+    public List getKids()
+    {
+
+        List retval = null;
+        COSArray kids = (COSArray)node.getDictionaryObject( "Kids" );
+        if( kids != null )
+        {
+            List pdObjects = new ArrayList();
+            for( int i=0; i<kids.size(); i++ )
+            {
+                pdObjects.add( createChildNode( (COSDictionary)kids.getObject(i) ) );
+            }
+            retval = new COSArrayList(pdObjects,kids);
+        }
+
+        return retval;
+    }
+
+    /**
+     * Set the children of this number tree.
+     *
+     * @param kids The children of this number tree.
+     */
+    public void setKids( List kids )
+    {
+        node.setItem( "Kids", COSArrayList.converterToCOSArray( kids ) );
+    }
+
+    /**
+     * Returns the value corresponding to an index in the number tree.
+     *
+     * @param index The index in the number tree.
+     *
+     * @return The value corresponding to the index.
+     * 
+     * @throws IOException If there is a problem creating the values.
+     */
+    public Object getValue( Integer index ) throws IOException
+    {
+        Object retval = null;
+        Map names = getNumbers();
+        if( names != null )
+        {
+            retval = names.get( index );
+        }
+        else
+        {
+            List kids = getKids();
+            for( int i=0; i<kids.size() && retval == null; i++ )
+            {
+                PDNumberTreeNode childNode = (PDNumberTreeNode)kids.get( i );
+                if( childNode.getLowerLimit().compareTo( index ) <= 0 &&
+                        childNode.getUpperLimit().compareTo( index ) >= 0 )
+                {
+                    retval = childNode.getValue( index );
+                }
+            }
+        }
+        return retval;
+    }
+
+
+    /**
+     * This will return a map of numbers.  The key will be a java.lang.Integer, the value
will
+     * depend on where this class is being used.
+     *
+     * @return A map of COS objects.
+     * 
+     * @throws IOException If there is a problem creating the values.
+     */
+    public Map getNumbers()  throws IOException
+    {
+        Map indices = null;
+        COSArray namesArray = (COSArray)node.getDictionaryObject( "Nums" );
+        if( namesArray != null )
+        {
+            indices = new HashMap();
+            for( int i=0; i<namesArray.size(); i+=2 )
+            {
+                COSInteger key = (COSInteger)namesArray.getObject(i);
+                COSBase cosValue = namesArray.getObject( i+1 );
+                Object pdValue = convertCOSToPD( cosValue );
+
+                indices.put( Integer.valueOf(key.intValue()), pdValue );
+            }
+            indices = Collections.unmodifiableMap(indices);
+        }
+
+        return indices;
+    }
+
+    /**
+     * Method to convert the COS value in the name tree to the PD Model object.  The
+     * default implementation will simply use reflection to create the correct object
+     * type.  Subclasses can do whatever they want.
+     *
+     * @param base The COS object to convert.
+     * @return The converted PD Model object.
+     * @throws IOException If there is an error during creation.
+     */
+    protected Object convertCOSToPD( COSBase base ) throws IOException
+    {
+        Object retval = null;
+        try
+        {
+            Constructor ctor = valueType.getConstructor( new Class[] { base.getClass() }
);
+            retval = ctor.newInstance( new Object[] { base } );
+        }
+        catch( Throwable t )
+        {
+            throw new IOException( "Error while trying to create value in number tree:" +
t.getMessage());
+
+        }
+        return retval;
+    }
+
+    /**
+     * Create a child node object.
+     *
+     * @param dic The dictionary for the child node object to refer to.
+     * @return The new child node object.
+     */
+    protected PDNumberTreeNode createChildNode( COSDictionary dic )
+    {
+        return new PDNumberTreeNode(dic,valueType);
+    }
+
+    /**
+     * Set the names of for this node.  The keys should be java.lang.String and the
+     * values must be a COSObjectable.  This method will set the appropriate upper and lower
+     * limits based on the keys in the map.
+     *
+     * @param numbers The map of names to objects.
+     */
+    public void setNumbers( Map numbers )
+    {
+        if( numbers == null )
+        {
+            node.setItem( "Nums", (COSObjectable)null );
+            node.setItem( "Limits", (COSObjectable)null);
+        }
+        else
+        {
+            List keys = new ArrayList( numbers.keySet() );
+            Collections.sort( keys );
+            COSArray array = new COSArray();
+            for( int i=0; i<keys.size(); i++ )
+            {
+                Integer key = (Integer)keys.get(i);
+                array.add( new COSInteger( key.intValue() ) );
+                COSObjectable obj = (COSObjectable)numbers.get( key );
+                array.add( obj );
+            }
+            Integer lower = null;
+            Integer upper = null;
+            if( keys.size() > 0 )
+            {
+                lower = (Integer)keys.get( 0 );
+                upper = (Integer)keys.get( keys.size()-1 );
+            }
+            setUpperLimit( upper );
+            setLowerLimit( lower );
+            node.setItem( "Names", array );
+        }
+    }
+
+    /**
+     * Get the highest value for a key in the name map.
+     *
+     * @return The highest value for a key in the map.
+     */
+    public Integer getUpperLimit()
+    {
+        Integer retval = null;
+        COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+        if( arr != null )
+        {
+            retval = Integer.valueOf(arr.getInt( 1 ));
+        }
+        return retval;
+    }
+
+    /**
+     * Set the highest value for the key in the map.
+     *
+     * @param upper The new highest value for a key in the map.
+     */
+    private void setUpperLimit( Integer upper )
+    {
+        COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+        if( arr == null )
+        {
+            arr = new COSArray();
+            arr.add( null );
+            arr.add( null );
+        }
+        arr.setInt( 1, upper.intValue() );
+    }
+
+    /**
+     * Get the lowest value for a key in the name map.
+     *
+     * @return The lowest value for a key in the map.
+     */
+    public Integer getLowerLimit()
+    {
+        Integer retval = null;
+        COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+        if( arr != null )
+        {
+            retval = Integer.valueOf(arr.getInt( 0 ));
+        }
+        return retval;
+    }
+
+    /**
+     * Set the lowest value for the key in the map.
+     *
+     * @param lower The new lowest value for a key in the map.
+     */
+    private void setLowerLimit( Integer lower )
+    {
+        COSArray arr = (COSArray)node.getDictionaryObject( "Limits" );
+        if( arr == null )
+        {
+            arr = new COSArray();
+            arr.add( null );
+            arr.add( null );
+        }
+        arr.setInt( 0, lower.intValue() );
+    }
+}

Propchange: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabelRange.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabelRange.java?rev=908623&view=auto
==============================================================================
--- pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabelRange.java (added)
+++ pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabelRange.java Wed
Feb 10 18:32:58 2010
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.common;
+
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+
+/**
+ * Contains information for a page label range.
+ * 
+ * @author <a href="mailto:igor.podolskiy@ievvwi.uni-stuttgart.de">Igor
+ *         Podolskiy</a>
+ * 
+ * @see PDPageLabels
+ * 
+ * @version $Revision$
+ */
+public class PDPageLabelRange implements COSObjectable
+{
+
+    private COSDictionary root;
+
+    // Page label dictonary (PDF32000-1:2008 Section 12.4.2, Table 159)
+    private static final COSName KEY_START = COSName.getPDFName("St");
+    private static final COSName KEY_PREFIX = COSName.getPDFName("P");
+    private static final COSName KEY_STYLE = COSName.getPDFName("S");
+
+    // Style entry values (PDF32000-1:2008 Section 12.4.2, Table 159)
+
+    /**
+     * Decimal page numbering style (1, 2, 3, ...).
+     */
+    public static final String STYLE_DECIMAL = "D";
+
+    /**
+     * Roman numbers (upper case) numbering style (I, II, III, IV, ...).
+     */
+    public static final String STYLE_ROMAN_UPPER = "R";
+
+    /**
+     * Roman numbers (lower case) numbering style (i, ii, iii, iv, ...).
+     */
+    public static final String STYLE_ROMAN_LOWER = "r";
+
+    /**
+     * Letter (upper case) numbering style (A, B, ..., Z, AA, BB, ..., ZZ, AAA,
+     * ...).
+     */
+    public static final String STYLE_LETTERS_UPPER = "A";
+
+    /**
+     * Letter (lower case) numbering style (a, b, ..., z, aa, bb, ..., zz, aaa,
+     * ...).
+     */
+    public static final String STYLE_LETTERS_LOWER = "a";
+
+    /**
+     * Creates a new empty page label range object.
+     */
+    public PDPageLabelRange()
+    {
+        this(new COSDictionary());
+    }
+
+    /**
+     * Creates a new page label range object from the given dictionary.
+     * 
+     * @param dict
+     *            the base dictionary for the new object.
+     */
+    public PDPageLabelRange(COSDictionary dict)
+    {
+        root = dict;
+    }
+
+    /**
+     * Returns the underlying dictionary.
+     * 
+     * @return the underlying dictionary.
+     */
+    public COSDictionary getCOSDictionary()
+    {
+        return root;
+    }
+
+    public COSBase getCOSObject()
+    {
+        return root;
+    }
+
+    /**
+     * Returns the numbering style for this page range.
+     * 
+     * @return one of the STYLE_* constants
+     */
+    public String getStyle()
+    {
+        return root.getNameAsString(KEY_STYLE);
+    }
+
+    /**
+     * Sets the numbering style for this page range.
+     * 
+     * @param style
+     *            one of the STYLE_* constants or {@code null} if no page
+     *            numbering is desired.
+     */
+    public void setStyle(String style)
+    {
+        if (style != null)
+        {
+            root.setName(KEY_STYLE, style);
+        }
+        else
+        {
+            root.removeItem(KEY_STYLE);
+        }
+    }
+
+    /**
+     * Returns the start value for page numbering in this page range.
+     * 
+     * @return a positive integer the start value for numbering.
+     */
+    public int getStart()
+    {
+        return root.getInt(KEY_START, 1);
+    }
+
+    /**
+     * Sets the start value for page numbering in this page range.
+     * 
+     * @param start
+     *            a positive integer representing the start value.
+     * @throws IllegalArgumentException
+     *             if {@code start} is not a positive integer
+     */
+    public void setStart(int start)
+    {
+        if (start <= 0)
+        {
+            throw new IllegalArgumentException(
+                    "The page numbering start value must be a positive integer");
+        }
+        root.setInt(KEY_START, start);
+    }
+
+    /**
+     * Returns the page label prefix for this page range.
+     * 
+     * @return the page label prefix for this page range, or {@code null} if no
+     *         prefix has been defined.
+     */
+    public String getPrefix()
+    {
+        return root.getString(KEY_PREFIX);
+    }
+
+    /**
+     * Sets the page label prefix for this page range.
+     * 
+     * @param prefix
+     *            the page label prefix for this page range, or {@code null} to
+     *            unset the prefix.
+     */
+    public void setPrefix(String prefix)
+    {
+        if (prefix != null)
+        {
+            root.setString(KEY_PREFIX, prefix);
+        }
+        else
+        {
+            root.removeItem(KEY_PREFIX);
+        }
+    }
+}

Propchange: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabelRange.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java?rev=908623&view=auto
==============================================================================
--- pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java (added)
+++ pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java Wed Feb
10 18:32:58 2010
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.common;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.pdmodel.PDDocument;
+
+/**
+ * Represents the page label dictionary of a document.
+ * 
+ * @author <a href="mailto:igor.podolskiy@ievvwi.uni-stuttgart.de">Igor
+ *         Podolskiy</a>
+ * @version $Revision$
+ */
+public class PDPageLabels implements COSObjectable
+{
+
+    private SortedMap<Integer, PDPageLabelRange> labels;
+
+    private PDDocument doc;
+
+    /**
+     * Creates an empty page label dictionary for the given document.
+     * 
+     * <p>
+     * Note that the page label dictionary won't be automatically added to the
+     * document; you will still need to do it manually (see
+     * {@link PDDocumentCatalog#setPageLabels(PDPageLabels)}.
+     * </p>
+     * 
+     * @param document
+     *            The document the page label dictionary is created for.
+     * @see PDDocumentCatalog#setPageLabels(PDPageLabels)
+     */
+    public PDPageLabels(PDDocument document)
+    {
+        labels = new TreeMap<Integer, PDPageLabelRange>();
+        this.doc = document;
+        PDPageLabelRange defaultRange = new PDPageLabelRange();
+        defaultRange.setStyle(PDPageLabelRange.STYLE_DECIMAL);
+        labels.put(0, defaultRange);
+    }
+
+    /**
+     * Creates an page label dictionary for a document using the information in
+     * the given COS dictionary.
+     * 
+     * <p>
+     * Note that the page label dictionary won't be automatically added to the
+     * document; you will still need to do it manually (see
+     * {@link PDDocumentCatalog#setPageLabels(PDPageLabels)}.
+     * </p>
+     * 
+     * @param document
+     *            The document the page label dictionary is created for.
+     * @param dict
+     *            an existing page label dictionary
+     * @see PDDocumentCatalog#setPageLabels(PDPageLabels)
+     * @throws IOException
+     *             If something goes wrong during the number tree conversion.
+     */
+    public PDPageLabels(PDDocument document, COSDictionary dict) throws IOException
+    {
+        this(document);
+        if (dict == null)
+        {
+            return;
+        }
+        PDNumberTreeNode root = new PDNumberTreeNode(dict, COSDictionary.class);
+        Map<Integer, COSDictionary> numbers = root.getNumbers();
+        for (Entry<Integer, COSDictionary> i : numbers.entrySet())
+        {
+            labels.put(i.getKey(), new PDPageLabelRange(i.getValue()));
+        }
+    }
+
+    /**
+     * Returns the number of page label ranges.
+     * 
+     * <p>
+     * This will be always &gt;= 1, as the required default entry for the page
+     * range starting at the first page is added automatically by this
+     * implementation (see PDF32000-1:2008, p. 375).
+     * </p>
+     * 
+     * @return the number of page label ranges.
+     */
+    public int getPageRangeCount()
+    {
+        return labels.size();
+    }
+
+    /**
+     * Returns the page label range starting at the given page, or {@code null}
+     * if no such range is defined.
+     * 
+     * @param startPage
+     *            the 0-based page index representing the start page of the page
+     *            range the item is defined for.
+     * @return the page label range or {@code null} if no label range is defined
+     *         for the given start page.
+     */
+    public PDPageLabelRange getPageLabelRange(int startPage)
+    {
+        return labels.get(startPage);
+    }
+
+    /**
+     * Sets the page label range beginning at the specified start page.
+     * 
+     * @param startPage
+     *            the 0-based index of the page representing the start of the
+     *            page label range.
+     * @param item
+     *            the page label item to set.
+     */
+    public void setLabelItem(int startPage, PDPageLabelRange item)
+    {
+        labels.put(startPage, item);
+    }
+
+    public COSBase getCOSObject()
+    {
+        COSDictionary dict = new COSDictionary();
+        COSArray arr = new COSArray();
+        for (Entry<Integer, PDPageLabelRange> i : labels.entrySet())
+        {
+            arr.add(new COSInteger(i.getKey()));
+            arr.add(i.getValue());
+        }
+        dict.setItem("Nums", arr);
+        return dict;
+    }
+
+    /**
+     * Returns a mapping with computed page labels as keys and corresponding
+     * 0-based page indices as values. The returned map will contain at most as
+     * much entries as the document has pages.
+     * 
+     * <p>
+     * <strong>NOTE:</strong> If the document contains duplicate page labels,
+     * the returned map will contain <em>less</em> entries than the document
has
+     * pages. The page index returned in this case is the <em>highest</em> index
+     * among all pages sharing the same label.
+     * </p>
+     * 
+     * @return a mapping from labels to 0-based page indices.
+     */
+    public Map<String, Integer> getPageIndicesByLabels()
+    {
+        final Map<String, Integer> labelMap = 
+            new HashMap<String, Integer>(doc.getNumberOfPages());
+        computeLabels(new LabelHandler()
+        {
+            public void newLabel(int pageIndex, String label)
+            {
+                labelMap.put(label, pageIndex);
+            }
+        });
+        return labelMap;
+    }
+
+    /**
+     * Returns a mapping with 0-based page indices as keys and corresponding
+     * page labels as values as an array. The array will have exactly as much
+     * entries as the document has pages.
+     * 
+     * @return an array mapping from 0-based page indices to labels.
+     */
+    public String[] getLabelsByPageIndices()
+    {
+        final String[] map = new String[doc.getNumberOfPages()];
+        computeLabels(new LabelHandler()
+        {
+            public void newLabel(int pageIndex, String label)
+            {
+                map[pageIndex] = label;
+            }
+        });
+        return map;
+    }
+
+    /**
+     * Internal interface for the control flow support.
+     * 
+     * @author Igor Podolskiy
+     */
+    private static interface LabelHandler
+    {
+        public void newLabel(int pageIndex, String label);
+    }
+
+    private void computeLabels(LabelHandler handler)
+    {
+        Iterator<Entry<Integer, PDPageLabelRange>> iterator = 
+            labels.entrySet().iterator();
+        if (!iterator.hasNext())
+        {
+            return;
+        }
+        int pageIndex = 0;
+        Entry<Integer, PDPageLabelRange> lastEntry = iterator.next();
+        while (iterator.hasNext())
+        {
+            Entry<Integer, PDPageLabelRange> entry = iterator.next();
+            int numPages = entry.getKey() - lastEntry.getKey();
+            LabelGenerator gen = new LabelGenerator(lastEntry.getValue(),
+                    numPages);
+            while (gen.hasNext())
+            {
+                handler.newLabel(pageIndex, gen.next());
+                pageIndex++;
+            }
+            lastEntry = entry;
+        }
+        LabelGenerator gen = new LabelGenerator(lastEntry.getValue(), 
+                doc.getNumberOfPages() - lastEntry.getKey());
+        while (gen.hasNext())
+        {
+            handler.newLabel(pageIndex, gen.next());
+            pageIndex++;
+        }
+    }
+
+    /**
+     * Generates the labels in a page range.
+     * 
+     * @author Igor Podolskiy
+     * 
+     */
+    private static class LabelGenerator implements Iterator<String>
+    {
+        private PDPageLabelRange labelInfo;
+        private int numPages;
+        private int currentPage;
+
+        public LabelGenerator(PDPageLabelRange label, int pages)
+        {
+            this.labelInfo = label;
+            this.numPages = pages;
+            this.currentPage = 0;
+        }
+
+        public boolean hasNext()
+        {
+            return currentPage < numPages;
+        }
+
+        public String next()
+        {
+            if (!hasNext())
+            {
+                throw new NoSuchElementException();
+            }
+            StringBuilder buf = new StringBuilder();
+            if (labelInfo.getPrefix() != null)
+            {
+                buf.append(labelInfo.getPrefix());
+            }
+            if (labelInfo.getStyle() != null)
+            {
+                buf.append(getNumber(labelInfo.getStart() + currentPage,
+                        labelInfo.getStyle()));
+            }
+            currentPage++;
+            return buf.toString();
+        }
+
+        private String getNumber(int pageIndex, String style)
+        {
+            if (PDPageLabelRange.STYLE_DECIMAL.equals(style))
+            {
+                return Integer.toString(pageIndex);
+            }
+            else if (PDPageLabelRange.STYLE_LETTERS_LOWER.equals(style))
+            {
+                return makeLetterLabel(pageIndex);
+            }
+            else if (PDPageLabelRange.STYLE_LETTERS_UPPER.equals(style))
+            {
+                return makeLetterLabel(pageIndex).toUpperCase();
+            }
+            else if (PDPageLabelRange.STYLE_ROMAN_LOWER.equals(style))
+            {
+                return makeRomanLabel(pageIndex);
+            }
+            else if (PDPageLabelRange.STYLE_ROMAN_UPPER.equals(style))
+            {
+                return makeRomanLabel(pageIndex).toUpperCase();
+            }
+            else
+            {
+                // Fall back to decimals.
+                return Integer.toString(pageIndex);
+            }
+        }
+
+        private static final String[][] ROMANS = new String[][]
+        {
+        { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" },
+        { "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" },
+        { "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }, };
+
+        private static String makeRomanLabel(int pageIndex)
+        {
+            StringBuilder buf = new StringBuilder();
+            int power = 0;
+            while (power < 3 && pageIndex > 0)
+            {
+                buf.insert(0, ROMANS[power][pageIndex % 10]);
+                pageIndex = pageIndex / 10;
+                power++;
+            }
+            // Prepend as many m as there are thousands (which is
+            // incorrect by the roman numeral rules for numbers > 3999,
+            // but is unbounded and Adobe Acrobat does it this way).
+            // This code is somewhat inefficient for really big numbers,
+            // but those don't occur too often (and the numbers in those cases
+            // would be incomprehensible even if we and Adobe
+            // used strict Roman rules).
+            for (int i = 0; i < pageIndex; i++)
+            {
+                buf.insert(0, 'm');
+            }
+            return buf.toString();
+        }
+
+        /**
+         * A..Z, AA..ZZ, AAA..ZZZ ... labeling as described in PDF32000-1:2008,
+         * Table 159, Page 375.
+         */
+        private static String makeLetterLabel(int num)
+        {
+            StringBuilder buf = new StringBuilder();
+            int numLetters = num / 26 + Integer.signum(num % 26);
+            int letter = num % 26 + 26 * (1 - Integer.signum(num % 26)) + 64;
+            for (int i = 0; i < numLetters; i++)
+            {
+                buf.appendCodePoint(letter);
+            }
+            return buf.toString();
+        }
+
+        public void remove()
+        {
+            // This is a generator, no removing allowed.
+            throw new UnsupportedOperationException();
+        }
+    }
+}
\ No newline at end of file

Propchange: pdfbox/trunk/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message