2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline;
19 import org.apache.pdfbox.cos.COSBase;
20 import org.apache.pdfbox.cos.COSDictionary;
22 import org.apache.pdfbox.pdmodel.common.COSObjectable;
25 * This represents an node in an outline in a pdf document.
27 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
28 * @version $Revision: 1.3 $
30 public class PDOutlineNode implements COSObjectable
33 * The dictionary for this node.
35 protected COSDictionary node;
38 * Default Constructor.
40 public PDOutlineNode()
42 node = new COSDictionary();
46 * Default Constructor.
48 * @param dict The dictionary storage.
50 public PDOutlineNode( COSDictionary dict)
56 * Convert this standard java object to a COS object.
58 * @return The cos object that matches this Java object.
60 public COSBase getCOSObject()
66 * Convert this standard java object to a COS object.
68 * @return The cos object that matches this Java object.
70 public COSDictionary getCOSDictionary()
76 * Get the parent of this object. This will either be a DocumentOutline or an OutlineItem.
78 * @return The parent of this object, or null if this is the document outline and there
81 protected PDOutlineNode getParent()
83 PDOutlineNode retval = null;
84 COSDictionary parent = (COSDictionary)node.getDictionaryObject( "Parent", "P" );
87 if( parent.getDictionaryObject( "Parent", "P" ) == null )
89 retval = new PDDocumentOutline( parent );
93 retval = new PDOutlineItem( parent );
101 * Set the parent of this object, this is maintained by these objects and should not
102 * be called by any clients of PDFBox code.
104 * @param parent The parent of this object.
106 protected void setParent( PDOutlineNode parent )
108 node.setItem( "Parent", parent );
112 * append a child node to this node.
114 * @param outlineNode The node to add.
116 public void appendChild( PDOutlineItem outlineNode )
118 outlineNode.setParent( this );
119 if( getFirstChild() == null )
121 int currentOpenCount = getOpenCount();
122 setFirstChild( outlineNode );
123 //1 for the the item we are adding;
124 int numberOfOpenNodesWeAreAdding = 1;
125 if( outlineNode.isNodeOpen() )
127 numberOfOpenNodesWeAreAdding += outlineNode.getOpenCount();
131 setOpenCount( currentOpenCount + numberOfOpenNodesWeAreAdding );
135 setOpenCount( currentOpenCount - numberOfOpenNodesWeAreAdding );
137 updateParentOpenCount( numberOfOpenNodesWeAreAdding );
141 PDOutlineItem previousLastChild = getLastChild();
142 previousLastChild.insertSiblingAfter( outlineNode );
145 PDOutlineItem lastNode = outlineNode;
146 while(lastNode.getNextSibling() != null)
148 lastNode = lastNode.getNextSibling();
150 setLastChild( lastNode );
154 * Return the first child or null if there is no child.
156 * @return The first child.
158 public PDOutlineItem getFirstChild()
160 PDOutlineItem last = null;
161 COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "First" );
162 if( lastDic != null )
164 last = new PDOutlineItem( lastDic );
170 * Set the first child, this will be maintained by this class.
172 * @param outlineNode The new first child.
174 protected void setFirstChild( PDOutlineNode outlineNode )
176 node.setItem( "First", outlineNode );
180 * Return the last child or null if there is no child.
182 * @return The last child.
184 public PDOutlineItem getLastChild()
186 PDOutlineItem last = null;
187 COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Last" );
188 if( lastDic != null )
190 last = new PDOutlineItem( lastDic );
196 * Set the last child, this will be maintained by this class.
198 * @param outlineNode The new last child.
200 protected void setLastChild( PDOutlineNode outlineNode )
202 node.setItem( "Last", outlineNode );
206 * Get the number of open nodes. Or a negative number if this node
207 * is closed. See PDF Reference for more details. This value
208 * is updated as you append children and siblings.
210 * @return The Count attribute of the outline dictionary.
212 public int getOpenCount()
214 return node.getInt( "Count", 0 );
218 * Set the open count. This number is automatically managed for you
219 * when you add items to the outline.
221 * @param openCount The new open cound.
223 protected void setOpenCount( int openCount )
225 node.setInt( "Count", openCount );
229 * This will set this node to be open when it is shown in the viewer. By default, when
230 * a new node is created it will be closed.
231 * This will do nothing if the node is already open.
233 public void openNode()
235 //if the node is already open then do nothing.
238 int openChildrenCount = 0;
239 PDOutlineItem currentChild = getFirstChild();
240 while( currentChild != null )
242 //first increase by one for the current child
244 //then increase by the number of open nodes the child has
245 if( currentChild.isNodeOpen() )
247 openChildrenCount += currentChild.getOpenCount();
249 currentChild = currentChild.getNextSibling();
251 setOpenCount( openChildrenCount );
252 updateParentOpenCount( openChildrenCount );
260 public void closeNode()
262 //if the node is already closed then do nothing.
265 int openCount = getOpenCount();
266 updateParentOpenCount( -openCount );
267 setOpenCount( -openCount );
272 * Node is open if the open count is greater than zero.
273 * @return true if this node is open.
275 public boolean isNodeOpen()
277 return getOpenCount() > 0;
281 * The count parameter needs to be updated when you add or remove elements to
282 * the outline. When you add an element at a lower level then you need to
283 * increase all of the parents.
285 * @param amount The amount to update by.
287 protected void updateParentOpenCount( int amount )
289 PDOutlineNode parent = getParent();
292 int currentCount = parent.getOpenCount();
293 //if the currentCount is negative or it is absent then
294 //we will treat it as negative. The default is to be negative.
295 boolean negative = currentCount < 0 ||
296 parent.getCOSDictionary().getDictionaryObject( "Count" ) == null;
297 currentCount = Math.abs( currentCount );
298 currentCount += amount;
301 currentCount = -currentCount;
303 parent.setOpenCount( currentCount );
304 //recursively call parent to update count, but the parents count is only
305 //updated if this is an open node
308 parent.updateParentOpenCount( amount );