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:[email protected]">Ben Litchfield</a>,
+ * <a href="[email protected]">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:[email protected]">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:[email protected]">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 >= 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