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