edu.harvard.hul.ois.jhove.module.pdf.PageLabelNode Maven / Gradle / Ivy
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove.module.pdf;
import edu.harvard.hul.ois.jhove.module.PdfModule;
import java.util.*;
/**
* Class for nodes of a PDF number tree.
*/
public class PageLabelNode
{
/** The PdfModule this node is associated with. */
protected PdfModule _module;
/** The parent node of this node. */
protected PageLabelNode _parent;
/** The dictionary which defines this node. */
protected PdfDictionary _dict;
/** Set to true when all subnodes of this node
* have been iterated through following a StartWalk. */
protected boolean _walkFinished;
private List _descendants;
private Iterator _descendantsIter;
private int _currentKey; // Key most recently obtained in traversing tree
private PdfObject _currentValue; // Value most recently obtained in traversing tree
private int _prevKey; // Key previously obtained in traversing tree
private PdfObject _prevValue; // Value previously obtained in traversing tree
private int _currentNumsIndex; // Current index into Nums entry
private int _currentNumsLength; // Length of current Nums entry
private Vector _currentNumsVec; // Vector from the Nums entry
private PageLabelNode _currentDescendant;
private PageLabelNode _currentLeaf;
/**
* Superclass constructor.
* @param module The PdfModule under which we're operating
* @param parent The parent node in the document tree;
* may be null only for the root node
* @param dict The dictionary object on which this node
* is based
*/
public PageLabelNode (PdfModule module,
PageLabelNode parent,
PdfDictionary dict)
{
_module = module;
_parent = parent;
_dict = dict;
}
/**
* Build the subtree of descendants of this node, using
* the Kids entry in the dictionary. Leaf nodes are
* recognized by not having a Kids entry.
*/
public void buildSubtree () throws PdfException
{
PdfArray kids = null;
try {
kids = (PdfArray) _dict.get("Kids");
if (kids != null) {
Vector kidsVec = kids.getContent ();
_descendants = new ArrayList (kidsVec.size ());
for (int i = 0; i < kidsVec.size (); i++) {
PdfDictionary kid = (PdfDictionary)
_module.resolveIndirectObject
((PdfObject) kidsVec.elementAt (i));
PageLabelNode nodeObj =
new PageLabelNode (_module, this, kid);
nodeObj.buildSubtree ();
_descendants.add(nodeObj);
}
}
else _descendants = null;
}
catch (PdfException pe) {
throw pe;
}
catch (Exception e) {
throw new PdfInvalidException (MessageConstants.ERR_PAGE_LABEL_NODE_INVALID);
}
}
/**
* Initialize an iterator through the descendants of this node.
*/
public void startWalk ()
{
if (_descendants != null) {
_descendantsIter = _descendants.listIterator ();
_walkFinished = false;
}
else {
_descendantsIter = null; // leaf node, or root in isolation
_walkFinished = true;
}
_currentDescendant = null;
_currentLeaf = null;
_currentKey = -1;
_currentValue = null;
_prevKey = -1;
_prevValue = null;
}
/**
* Get the next leaf object which is under this node. This function
* is designed such that calling startWalk() and then repeatedly
* calling nextLeafObject() will return all the leaf objects in the tree
* under this node, and finally will return null when there are no more.
* A leaf object is one which has no Kids; it is required to have a
* Nums entry.
*/
public PageLabelNode nextLeafObject ()
{
if (_walkFinished) {
return null;
}
// _currentDescendant == null and _walkFinished == false indicates
// we're at the start.
if (_currentDescendant == null) {
if (_descendantsIter == null) {
// No descendants. This is a root node which functions as its
// only leaf.
_walkFinished = true;
return this;
}
// Get first descendant
_currentDescendant = (PageLabelNode) _descendantsIter.next ();
_currentDescendant.startWalk ();
}
PageLabelNode retval = _currentDescendant.nextLeafObject ();
if (retval == null) {
if (_descendantsIter.hasNext ()) {
_currentDescendant = (PageLabelNode) _descendantsIter.next ();
_currentDescendant.startWalk ();
return _currentDescendant.nextLeafObject ();
}
// We've gone through all our descendants.
_walkFinished = true;
return null;
}
return retval;
}
/**
* Obtain the next key-value pair from the tree. This returns true
* if a pair is available, false if not. After this is called,
* getCurrentKey and getCurrentValue may be called to retrieve the
* key and value thus found. Each time this is called,
* currentKey and currentValue get copied into prevKey and
* prevValue.
*/
public boolean findNextKeyValue () throws PdfException
{
try {
if (_currentLeaf == null || _currentNumsIndex >= _currentNumsLength) {
_currentLeaf = nextLeafObject ();
if (_currentLeaf == null) {
_prevKey = _currentKey;
_prevValue = _currentValue;
_currentKey = Integer.MAX_VALUE;
return false; // all done
}
_currentNumsIndex = 0;
PdfArray pairArray = (PdfArray)
_module.resolveIndirectObject (_currentLeaf._dict.get ("Nums"));
if (pairArray == null) {
throw new PdfInvalidException(MessageConstants.ERR_PAGE_NUMBER_DICT_ELEMENT_MISSING);
}
_currentNumsVec = pairArray.getContent ();
_currentNumsLength = _currentNumsVec.size ();
}
// The key and the value are in two successive positions in the
// array, which is of the form [key value key value ... ]
PdfSimpleObject keyObj = (PdfSimpleObject)
_currentNumsVec.elementAt (_currentNumsIndex);
// Save the previous key-value pair
_prevKey = _currentKey;
_prevValue = _currentValue;
_currentKey = keyObj.getIntValue ();
_currentValue = (PdfObject)
_currentNumsVec.elementAt (_currentNumsIndex + 1);
_currentNumsIndex += 2;
return true;
}
catch (PdfInvalidException e) {
throw e;
}
catch (Exception e) {
e.printStackTrace();
throw new PdfInvalidException (MessageConstants.ERR_PAGE_NUMBER_TREE_DATE_INVALID);
}
}
/**
* Returns key at current position in traversing tree
*/
public int getCurrentKey ()
{
return _currentKey;
}
/**
* Returns value associated with current key
*/
public PdfObject _getCurrentValue ()
{
return _currentValue;
}
/**
* Returns key previously obtained in traversing tree
*/
public int getPrevKey ()
{
return _prevKey;
}
/**
* Returns value associated with key previously obtained
* in traversing tree
*/
public PdfObject getPrevValue ()
{
return _prevValue;
}
/**
* A convenience method to turn integers into Roman
* numerals, for the generation of page labels.
*/
public static String intToRoman (int n, boolean upperCase)
{
StringBuffer buf = new StringBuffer ();
// Numbers of a thousand or more start with an "M" for
// each full thousand.
while (n >= 1000) {
buf.append ("M");
n -= 1000;
}
// treat "CM" as a special case.
if (n >= 900) {
buf.append ("CM");
n -= 900;
}
// 500 through 899 uses D, DC, DCC, DCCC
if (n >= 500) {
buf.append ("D");
while (n >= 600) {
buf.append ("C");
n -= 100;
}
n -= 500;
}
// 400 through 499 is CD
if (n >= 400) {
buf.append ("CD");
n -= 400;
}
// 100 through 399 is C, CC, CCC
while (n >= 100) {
buf.append ("C");
n -= 100;
}
// 90 through 99 is XC
if (n >= 90) {
buf.append ("XC");
n -= 90;
}
// 50 through 89 is L, LX, LXX, LXXX
if (n >= 50) {
buf.append ("L");
while (n >= 60) {
buf.append ("X");
n -= 10;
}
n -= 50;
}
// 40 through 49 is XL
if (n >= 40) {
buf.append ("XL");
n -= 40;
}
// 10 through 39 is X, XX, XXX
while (n >= 10) {
buf.append ("X");
n -= 10;
}
// From here on, nitpick it out with a switch statement.
switch (n) {
case 1:
buf.append ("I");
break;
case 2:
buf.append ("II");
break;
case 3:
buf.append ("III");
break;
case 4:
buf.append ("IV");
break;
case 5:
buf.append ("V");
break;
case 6:
buf.append ("VI");
break;
case 7:
buf.append ("VII");
break;
case 8:
buf.append ("VIII");
break;
case 9:
buf.append ("IX");
break;
}
String val = buf.toString ();
if (upperCase) {
return val;
}
return val.toLowerCase ();
}
/**
* A convenience method to turn integers into
* "letter" page numbers as defined for PDF.
* The first 26 pages are A-Z, the next 26 AA-ZZ,
* etc.
*/
public static String intToBase26 (int n, boolean upperCase)
{
int repeatCount = ((n - 1) / 26) + 1;
StringBuffer buf = new StringBuffer ();
int ch;
// Have ch be the appropriate character to repeat
if (upperCase) {
ch = 65 + ((n - 1) % 26);
}
else {
ch = 97 + ((n - 1) % 26);
}
while (--repeatCount >= 0) {
buf.append ((char) ch);
}
return buf.toString ();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy