You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2015/02/12 22:38:55 UTC

svn commit: r1659414 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline: PDDocumentOutline.java PDOutlineItem.java PDOutlineNode.java

Author: tilman
Date: Thu Feb 12 21:38:55 2015
New Revision: 1659414

URL: http://svn.apache.org/r1659414
Log:
PDFBOX-2677: redesign / fix bugs in outlines, by Andrea Vacondio

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java?rev=1659414&r1=1659413&r2=1659414&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java Thu Feb 12 21:38:55 2015
@@ -25,7 +25,7 @@ import org.apache.pdfbox.cos.COSName;
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  * @version $Revision: 1.2 $
  */
-public class PDDocumentOutline extends PDOutlineNode
+public final class PDDocumentOutline extends PDOutlineNode
 {
 
     /**
@@ -33,8 +33,7 @@ public class PDDocumentOutline extends P
      */
     public PDDocumentOutline()
     {
-        super();
-        node.setName( COSName.TYPE, "Outlines" );
+    	getCOSDictionary().setName(COSName.TYPE, COSName.OUTLINES.getName());
     }
 
     /**
@@ -45,5 +44,24 @@ public class PDDocumentOutline extends P
     public PDDocumentOutline( COSDictionary dic )
     {
         super( dic );
+        getCOSDictionary().setName(COSName.TYPE, COSName.OUTLINES.getName());
+    }
+
+    @Override
+    public boolean isNodeOpen()
+    {
+        return true;
+    }
+
+    @Override
+    public void openNode()
+    {
+        // The root of the outline hierarchy is not an OutlineItem and cannot be opened or closed
+    }
+
+    @Override
+    public void closeNode()
+    {
+        // The root of the outline hierarchy is not an OutlineItem and cannot be opened or closed
     }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java?rev=1659414&r1=1659413&r2=1659414&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java Thu Feb 12 21:38:55 2015
@@ -39,11 +39,11 @@ import org.apache.pdfbox.pdmodel.interac
 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
 
 /**
- * This represents an outline in a pdf document.
+ * This represents an outline item in a pdf document.
  *
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  */
-public class PDOutlineItem extends PDOutlineNode
+public final class PDOutlineItem extends PDOutlineNode
 {
     private static final int ITALIC_FLAG = 1;
     private static final int BOLD_FLAG = 2;
@@ -67,31 +67,57 @@ public class PDOutlineItem extends PDOut
     }
 
     /**
-     * Insert a sibling after this node.
+     * Insert a single sibling after this node.
      *
-     * @param item The item to insert.
+     * @param newSibling The item to insert.
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    public void insertSiblingAfter( PDOutlineItem item )
+    public void insertSiblingAfter(PDOutlineItem newSibling)
     {
-        item.setParent( getParent() );
+        requireSingleNode(newSibling);
+        PDOutlineNode parent = getParent();
+        newSibling.setParent(parent);
         PDOutlineItem next = getNextSibling();
-        setNextSibling( item );
-        item.setPreviousSibling( this );
-        if( next != null )
+        setNextSibling(newSibling);
+        newSibling.setPreviousSibling(this);
+        if (next != null)
         {
-            item.setNextSibling( next );
-            next.setPreviousSibling( item );
+            newSibling.setNextSibling(next);
+            next.setPreviousSibling(newSibling);
         }
-        updateParentOpenCount( 1 );
+        else if (parent != null)
+        {
+            getParent().setLastChild(newSibling);
+        }
+        updateParentOpenCountForAddedChild(newSibling);
     }
 
     /**
-     * {@inheritDoc}
+     * Insert a single sibling before this node.
+     *
+     * @param newSibling The item to insert.
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    @Override
-    public PDOutlineNode getParent()
+    public void insertSiblingBefore(PDOutlineItem newSibling)
     {
-        return super.getParent();
+        requireSingleNode(newSibling);
+        PDOutlineNode parent = getParent();
+        newSibling.setParent(parent);
+        PDOutlineItem previous = getPreviousSibling();
+        setPreviousSibling(newSibling);
+        newSibling.setNextSibling(this);
+        if (previous != null)
+        {
+            previous.setNextSibling(newSibling);
+            newSibling.setPreviousSibling(previous);
+        }
+        else if (parent != null)
+        {
+            getParent().setFirstChild(newSibling);
+        }
+        updateParentOpenCountForAddedChild(newSibling);
     }
 
     /**
@@ -101,13 +127,7 @@ public class PDOutlineItem extends PDOut
      */
     public PDOutlineItem getPreviousSibling()
     {
-        PDOutlineItem last = null;
-        COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( COSName.PREV );
-        if( lastDic != null )
-        {
-            last = new PDOutlineItem( lastDic );
-        }
-        return last;
+        return getOutlineItem(COSName.PREV);
     }
 
     /**
@@ -115,25 +135,17 @@ public class PDOutlineItem extends PDOut
      *
      * @param outlineNode The new previous sibling.
      */
-    protected void setPreviousSibling( PDOutlineNode outlineNode )
+    void setPreviousSibling(PDOutlineNode outlineNode)
     {
-        node.setItem( COSName.PREV, outlineNode );
+    	getCOSDictionary().setItem(COSName.PREV, outlineNode);
     }
 
     /**
-     * Return the next sibling or null if there is no next sibling.
-     *
-     * @return The next sibling.
+     * @return The next sibling or null if there is no next sibling.
      */
     public PDOutlineItem getNextSibling()
     {
-        PDOutlineItem last = null;
-        COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( COSName.NEXT );
-        if( lastDic != null )
-        {
-            last = new PDOutlineItem( lastDic );
-        }
-        return last;
+        return getOutlineItem(COSName.NEXT);
     }
 
     /**
@@ -141,9 +153,9 @@ public class PDOutlineItem extends PDOut
      *
      * @param outlineNode The new next sibling.
      */
-    protected void setNextSibling( PDOutlineNode outlineNode )
+    void setNextSibling(PDOutlineNode outlineNode)
     {
-        node.setItem( COSName.NEXT, outlineNode );
+    	getCOSDictionary().setItem(COSName.NEXT, outlineNode);
     }
 
     /**
@@ -153,7 +165,7 @@ public class PDOutlineItem extends PDOut
      */
     public String getTitle()
     {
-        return node.getString( COSName.TITLE );
+        return getCOSDictionary().getString(COSName.TITLE);
     }
 
     /**
@@ -161,9 +173,9 @@ public class PDOutlineItem extends PDOut
      *
      * @param title The new title for this node.
      */
-    public void setTitle( String title )
+    public void setTitle(String title)
     {
-        node.setString( COSName.TITLE, title );
+    	getCOSDictionary().setString(COSName.TITLE, title);
     }
 
     /**
@@ -174,7 +186,7 @@ public class PDOutlineItem extends PDOut
      */
     public PDDestination getDestination() throws IOException
     {
-        return PDDestination.create( node.getDictionaryObject( COSName.DEST ) );
+        return PDDestination.create(getCOSDictionary().getDictionaryObject(COSName.DEST));
     }
 
     /**
@@ -182,9 +194,9 @@ public class PDOutlineItem extends PDOut
      *
      * @param dest The new page destination for this node.
      */
-    public void setDestination( PDDestination dest )
+    public void setDestination(PDDestination dest)
     {
-        node.setItem( COSName.DEST, dest );
+    	getCOSDictionary().setItem(COSName.DEST, dest);
     }
 
     /**
@@ -192,7 +204,7 @@ public class PDOutlineItem extends PDOut
      *
      * @param page The page to refer to.
      */
-    public void setDestination( PDPage page )
+    public void setDestination(PDPage page)
     {
         PDPageXYZDestination dest = null;
         if( page != null )
@@ -285,7 +297,7 @@ public class PDOutlineItem extends PDOut
      */
     public PDAction getAction()
     {
-        return PDActionFactory.createAction( (COSDictionary)node.getDictionaryObject( COSName.A ) );
+        return PDActionFactory.createAction((COSDictionary) getCOSDictionary().getDictionaryObject(COSName.A));
     }
 
     /**
@@ -295,7 +307,7 @@ public class PDOutlineItem extends PDOut
      */
     public void setAction( PDAction action )
     {
-        node.setItem( COSName.A, action );
+    	getCOSDictionary().setItem(COSName.A, action);
     }
 
     /**
@@ -306,7 +318,7 @@ public class PDOutlineItem extends PDOut
     public PDStructureElement getStructureElement()
     {
         PDStructureElement se = null;
-        COSDictionary dic = (COSDictionary)node.getDictionaryObject( COSName.SE );
+        COSDictionary dic = (COSDictionary) getCOSDictionary().getDictionaryObject(COSName.SE);
         if( dic != null )
         {
             se = new PDStructureElement( dic );
@@ -321,7 +333,7 @@ public class PDOutlineItem extends PDOut
      */
     public void setStructuredElement( PDStructureElement structureElement )
     {
-        node.setItem( COSName.SE, structureElement );
+        getCOSDictionary().setItem(COSName.SE, structureElement);
     }
 
     /**
@@ -333,12 +345,12 @@ public class PDOutlineItem extends PDOut
     public PDColor getTextColor()
     {
         PDColor retval = null;
-        COSArray csValues = (COSArray)node.getDictionaryObject( COSName.C );
+        COSArray csValues = (COSArray) getCOSDictionary().getDictionaryObject(COSName.C);
         if( csValues == null )
         {
             csValues = new COSArray();
             csValues.growToSize( 3, new COSFloat( 0 ) );
-            node.setItem( COSName.C, csValues );
+            getCOSDictionary().setItem( COSName.C, csValues );
         }
         retval = new PDColor(csValues.toFloatArray(), PDDeviceRGB.INSTANCE);
         return retval;
@@ -351,7 +363,7 @@ public class PDOutlineItem extends PDOut
      */
     public void setTextColor( PDColor textColor )
     {
-        node.setItem( COSName.C, textColor.toCOSArray() );
+    	getCOSDictionary().setItem( COSName.C, textColor.toCOSArray() );
     }
 
     /**
@@ -365,7 +377,7 @@ public class PDOutlineItem extends PDOut
         array.add( new COSFloat( textColor.getRed()/255f));
         array.add( new COSFloat( textColor.getGreen()/255f));
         array.add( new COSFloat( textColor.getBlue()/255f));
-        node.setItem( COSName.C, array );
+        getCOSDictionary().setItem( COSName.C, array );
     }
 
     /**
@@ -375,7 +387,7 @@ public class PDOutlineItem extends PDOut
      */
     public boolean isItalic()
     {
-        return node.getFlag( COSName.F, ITALIC_FLAG );
+        return getCOSDictionary().getFlag( COSName.F, ITALIC_FLAG );
     }
 
     /**
@@ -385,7 +397,7 @@ public class PDOutlineItem extends PDOut
      */
     public void setItalic( boolean italic )
     {
-        node.setFlag( COSName.F, ITALIC_FLAG, italic );
+    	getCOSDictionary().setFlag( COSName.F, ITALIC_FLAG, italic );
     }
 
     /**
@@ -395,7 +407,7 @@ public class PDOutlineItem extends PDOut
      */
     public boolean isBold()
     {
-        return node.getFlag( COSName.F, BOLD_FLAG );
+        return getCOSDictionary().getFlag( COSName.F, BOLD_FLAG );
     }
 
     /**
@@ -405,7 +417,7 @@ public class PDOutlineItem extends PDOut
      */
     public void setBold( boolean bold )
     {
-        node.setFlag( COSName.F, BOLD_FLAG, bold );
+    	getCOSDictionary().setFlag( COSName.F, BOLD_FLAG, bold );
     }
 
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java?rev=1659414&r1=1659413&r2=1659414&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java Thu Feb 12 21:38:55 2015
@@ -16,156 +16,180 @@
  */
 package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline;
 
-import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
-
-import org.apache.pdfbox.pdmodel.common.COSObjectable;
+import org.apache.pdfbox.pdmodel.common.PDDictionaryWrapper;
 
 /**
- * This represents an node in an outline in a pdf document.
+ * Base class for a node in the outline of a PDF document.
  *
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  * @version $Revision: 1.3 $
  */
-public class PDOutlineNode implements COSObjectable
+public abstract class PDOutlineNode extends PDDictionaryWrapper
 {
-    /**
-     * The dictionary for this node.
-     */
-    protected COSDictionary node;
 
-    /**
-     * Default Constructor.
-     */
     public PDOutlineNode()
     {
-        node = new COSDictionary();
+        super();
     }
 
     /**
-     * Default Constructor.
-     *
      * @param dict The dictionary storage.
      */
-    public PDOutlineNode( COSDictionary dict)
+    public PDOutlineNode(COSDictionary dict)
     {
-        node = dict;
+        super(dict);
     }
 
+    @Override
+    public COSDictionary getCOSDictionary()
+    {
+        return super.getCOSDictionary();
+    }
+    
     /**
-     * Convert this standard java object to a COS object.
-     *
-     * @return The cos object that matches this Java object.
+     * @return The parent of this node or null if there is no parent.
      */
-    @Override
-    public COSBase getCOSObject()
+    PDOutlineNode getParent()
+    {
+        COSDictionary item = (COSDictionary) getCOSDictionary().getDictionaryObject(COSName.PARENT);
+        if (item != null)
+        {
+            if (COSName.OUTLINES.equals(item.getCOSName(COSName.TYPE)))
+            {
+                return new PDDocumentOutline(item);
+            }
+            return new PDOutlineItem(item);
+        }
+        return null;
+    }
+
+    void setParent(PDOutlineNode parent)
     {
-        return node;
+    	getCOSDictionary().setItem(COSName.PARENT, parent);
     }
 
     /**
-     * Convert this standard java object to a COS object.
+     * Adds the given node to the bottom of the children list.
      *
-     * @return The cos object that matches this Java object.
+     * @param newChild The node to add.
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    public COSDictionary getCOSDictionary()
+    public void addLast(PDOutlineItem newChild)
     {
-        return node;
+        requireSingleNode(newChild);
+        append(newChild);
+        updateParentOpenCountForAddedChild(newChild);
     }
 
     /**
-     * Get the parent of this object.  This will either be a DocumentOutline or an OutlineItem.
-     *
-     * @return The parent of this object, or null if this is the document outline and there
-     * is no parent.
+     * Adds the given node to the top of the children list.
+     * 
+     * @param newChild
+     * @return the newly added child
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    protected PDOutlineNode getParent()
+    public void addFirst(PDOutlineItem newChild)
     {
-        PDOutlineNode retval = null;
-        COSDictionary parent = (COSDictionary) node.getDictionaryObject(COSName.PARENT);
-        if (parent != null)
+        requireSingleNode(newChild);
+        prepend(newChild);
+        updateParentOpenCountForAddedChild(newChild);
+    }
+
+    /**
+     * @param node
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
+     */
+    void requireSingleNode(PDOutlineItem node)
+    {
+        if (node.getNextSibling() != null || node.getPreviousSibling() != null)
         {
-            if (parent.getDictionaryObject(COSName.PARENT) == null)
-            {
-                retval = new PDDocumentOutline(parent);
-            }
-            else
-            {
-                retval = new PDOutlineItem(parent);
-            }
+            throw new IllegalArgumentException("A single node with no siblings is required");
         }
-
-        return retval;
     }
 
     /**
-     * Set the parent of this object, this is maintained by these objects and should not
-     * be called by any clients of PDFBox code.
-     *
-     * @param parent The parent of this object.
+     * Appends the child to the linked list of children. This method only adjust pointers but doesn't take care of the
+     * Count key in the parent hierarchy.
+     * 
+     * @param newChild
      */
-    protected void setParent( PDOutlineNode parent )
+    private void append(PDOutlineItem newChild)
     {
-        node.setItem(COSName.PARENT, parent );
+        newChild.setParent(this);
+        if (!hasChildren())
+        {
+            setFirstChild(newChild);
+        }
+        else
+        {
+            PDOutlineItem previousLastChild = getLastChild();
+            previousLastChild.setNextSibling(newChild);
+            newChild.setPreviousSibling(previousLastChild);
+        }
+        setLastChild(newChild);
     }
 
     /**
-     * append a child node to this node.
-     *
-     * @param outlineNode The node to add.
+     * Prepends the child to the linked list of children. This method only adjust pointers but doesn't take care of the
+     * Count key in the parent hierarchy.
+     * 
+     * @param newChild
      */
-    public void appendChild( PDOutlineItem outlineNode )
+    private void prepend(PDOutlineItem newChild)
     {
-        outlineNode.setParent( this );
-        if( getFirstChild() == null )
+        newChild.setParent(this);
+        if (!hasChildren())
         {
-            int currentOpenCount = getOpenCount();
-            setFirstChild( outlineNode );
-            //1 for the the item we are adding;
-            int numberOfOpenNodesWeAreAdding = 1;
-            if( outlineNode.isNodeOpen() )
-            {
-                numberOfOpenNodesWeAreAdding += outlineNode.getOpenCount();
-            }
-            if( isNodeOpen() )
-            {
-                setOpenCount( currentOpenCount + numberOfOpenNodesWeAreAdding );
-            }
-            else
-            {
-                setOpenCount( currentOpenCount - numberOfOpenNodesWeAreAdding );
-            }
-            updateParentOpenCount( numberOfOpenNodesWeAreAdding );
+            setLastChild(newChild);
         }
         else
         {
-            PDOutlineItem previousLastChild = getLastChild();
-            previousLastChild.insertSiblingAfter( outlineNode );
+            PDOutlineItem previousFirstChild = getFirstChild();
+            newChild.setNextSibling(previousFirstChild);
+            previousFirstChild.setPreviousSibling(newChild);
         }
-        
-        PDOutlineItem lastNode = outlineNode;
-        while(lastNode.getNextSibling() != null)
+        setFirstChild(newChild);
+    }
+
+    void updateParentOpenCountForAddedChild(PDOutlineItem newChild)
+    {
+        int delta = 1;
+        if (newChild.isNodeOpen())
         {
-            lastNode = lastNode.getNextSibling();
+            delta += newChild.getOpenCount();
         }
-        setLastChild( lastNode );
+        newChild.updateParentOpenCount(delta);
     }
 
     /**
-     * Return the first child or null if there is no child.
-     *
-     * @return The first child.
+     * @return true if the node has at least one child
      */
-    public PDOutlineItem getFirstChild()
+    public boolean hasChildren()
+    {
+        return getFirstChild() != null;
+    }
+
+    PDOutlineItem getOutlineItem(COSName name)
     {
-        PDOutlineItem first = null;
-        COSDictionary firstDic = (COSDictionary)node.getDictionaryObject( "First" );
-        if( firstDic != null )
+        COSDictionary item = (COSDictionary) getCOSDictionary().getDictionaryObject(name);
+        if (item != null)
         {
-            first = new PDOutlineItem( firstDic );
+            return new PDOutlineItem(item);
         }
-        return first;
+        return null;
+    }
+
+    /**
+     * @return The first child or null if there is no child.
+     */
+    public PDOutlineItem getFirstChild()
+    {
+        return getOutlineItem(COSName.FIRST);
     }
 
     /**
@@ -173,25 +197,17 @@ public class PDOutlineNode implements CO
      *
      * @param outlineNode The new first child.
      */
-    protected void setFirstChild( PDOutlineNode outlineNode )
+    void setFirstChild(PDOutlineNode outlineNode)
     {
-        node.setItem(COSName.FIRST, outlineNode);
+    	getCOSDictionary().setItem(COSName.FIRST, outlineNode);
     }
 
     /**
-     * Return the last child or null if there is no child.
-     *
-     * @return The last child.
+     * @return The last child or null if there is no child.
      */
     public PDOutlineItem getLastChild()
     {
-        PDOutlineItem last = null;
-        COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Last" );
-        if( lastDic != null )
-        {
-            last = new PDOutlineItem( lastDic );
-        }
-        return last;
+        return getOutlineItem(COSName.LAST);
     }
 
     /**
@@ -199,59 +215,43 @@ public class PDOutlineNode implements CO
      *
      * @param outlineNode The new last child.
      */
-    protected void setLastChild( PDOutlineNode outlineNode )
+    void setLastChild(PDOutlineNode outlineNode)
     {
-        node.setItem( "Last", outlineNode );
+    	getCOSDictionary().setItem(COSName.LAST, outlineNode);
     }
 
     /**
-     * Get the number of open nodes.  Or a negative number if this node
-     * is closed.  See PDF Reference for more details.  This value
-     * is updated as you append children and siblings.
+     * Get the number of open nodes or a negative number if this node is closed.
+     * See PDF Reference 32000-1:2008 table 152 and 153 for more details. This
+     * value is updated as you append children and siblings.
      *
      * @return The Count attribute of the outline dictionary.
      */
     public int getOpenCount()
     {
-        return node.getInt( "Count", 0 );
+        return getCOSDictionary().getInt(COSName.COUNT, 0);
     }
 
     /**
-     * Set the open count.  This number is automatically managed for you
-     * when you add items to the outline.
+     * Set the open count. This number is automatically managed for you when you add items to the outline.
      *
-     * @param openCount The new open cound.
+     * @param openCount The new open count.
      */
-    protected void setOpenCount( int openCount )
+    void setOpenCount(int openCount)
     {
-        node.setInt( "Count", openCount );
+    	getCOSDictionary().setInt(COSName.COUNT, openCount);
     }
 
     /**
-     * This will set this node to be open when it is shown in the viewer.  By default, when
-     * a new node is created it will be closed.
-     * This will do nothing if the node is already open.
+     * This will set this node to be open when it is shown in the viewer. By default, when a new node is created it will
+     * be closed. This will do nothing if the node is already open.
      */
     public void openNode()
     {
         //if the node is already open then do nothing.
         if( !isNodeOpen() )
         {
-            int openChildrenCount = 0;
-            PDOutlineItem currentChild = getFirstChild();
-            while( currentChild != null )
-            {
-                //first increase by one for the current child
-                openChildrenCount++;
-                //then increase by the number of open nodes the child has
-                if( currentChild.isNodeOpen() )
-                {
-                    openChildrenCount += currentChild.getOpenCount();
-                }
-                currentChild = currentChild.getNextSibling();
-            }
-            setOpenCount( openChildrenCount );
-            updateParentOpenCount( openChildrenCount );
+            switchNodeCount();
         }
     }
 
@@ -261,18 +261,21 @@ public class PDOutlineNode implements CO
      */
     public void closeNode()
     {
-        //if the node is already closed then do nothing.
-        if( isNodeOpen() )
+        if (isNodeOpen())
         {
-            int openCount = getOpenCount();
-            updateParentOpenCount( -openCount );
-            setOpenCount( -openCount );
+            switchNodeCount();
         }
     }
 
+    private void switchNodeCount()
+    {
+        int openCount = getOpenCount();
+        setOpenCount(-openCount);
+        updateParentOpenCount(-openCount);
+    }
+
     /**
-     * Node is open if the open count is greater than zero.
-     * @return true if this node is open.
+     * @return true if this node count is greater than zero, false otherwise.
      */
     public boolean isNodeOpen()
     {
@@ -280,34 +283,23 @@ public class PDOutlineNode implements CO
     }
 
     /**
-     * The count parameter needs to be updated when you add or remove elements to
-     * the outline.  When you add an element at a lower level then you need to
-     * increase all of the parents.
+     * The count parameter needs to be updated when you add, remove, open or close outline items.
      *
-     * @param amount The amount to update by.
+     * @param delta The amount to update by.
      */
-    protected void updateParentOpenCount( int amount )
+    void updateParentOpenCount(int delta)
     {
         PDOutlineNode parent = getParent();
-        if( parent != null )
+        if (parent != null)
         {
-            int currentCount = parent.getOpenCount();
-            //if the currentCount is negative or it is absent then
-            //we will treat it as negative.  The default is to be negative.
-            boolean negative = currentCount < 0 ||
-                parent.getCOSDictionary().getDictionaryObject( "Count" ) == null;
-            currentCount = Math.abs( currentCount );
-            currentCount += amount;
-            if( negative )
+            if (parent.isNodeOpen())
             {
-                currentCount = -currentCount;
+                parent.setOpenCount(parent.getOpenCount() + delta);
+                parent.updateParentOpenCount(delta);
             }
-            parent.setOpenCount( currentCount );
-            //recursively call parent to update count, but the parents count is only
-            //updated if this is an open node
-            if( !negative )
+            else
             {
-                parent.updateParentOpenCount( amount );
+                parent.setOpenCount(parent.getOpenCount() - delta);
             }
         }
     }