001: /**
002: * Copyright (c) 2003-2006, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.pdmodel;
031:
032: import org.pdfbox.cos.COSArray;
033: import org.pdfbox.cos.COSBase;
034: import org.pdfbox.cos.COSDictionary;
035: import org.pdfbox.cos.COSName;
036: import org.pdfbox.cos.COSNumber;
037: import org.pdfbox.cos.COSInteger;
038:
039: import org.pdfbox.pdmodel.common.COSArrayList;
040: import org.pdfbox.pdmodel.common.COSObjectable;
041: import org.pdfbox.pdmodel.common.PDRectangle;
042:
043: import java.util.ArrayList;
044: import java.util.Iterator;
045: import java.util.List;
046:
047: /**
048: * This represents a page node in a pdf document.
049: *
050: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
051: * @version $Revision: 1.8 $
052: */
053: public class PDPageNode implements COSObjectable {
054: private COSDictionary page;
055:
056: /**
057: * Creates a new instance of PDPage.
058: */
059: public PDPageNode() {
060: page = new COSDictionary();
061: page.setItem(COSName.TYPE, COSName.PAGES);
062: page.setItem(COSName.KIDS, new COSArray());
063: page.setItem(COSName.COUNT, new COSInteger(0));
064: }
065:
066: /**
067: * Creates a new instance of PDPage.
068: *
069: * @param pages The dictionary pages.
070: */
071: public PDPageNode(COSDictionary pages) {
072: page = pages;
073: }
074:
075: /**
076: * This will update the count attribute of the page node. This only needs to
077: * be called if you add or remove pages. The PDDocument will call this for you
078: * when you use the PDDocumnet persistence methods. So, basically most clients
079: * will never need to call this.
080: *
081: * @return The update count for this node.
082: */
083: public long updateCount() {
084: long totalCount = 0;
085: List kids = getKids();
086: Iterator kidIter = kids.iterator();
087: while (kidIter.hasNext()) {
088: Object next = kidIter.next();
089: if (next instanceof PDPage) {
090: totalCount++;
091: } else {
092: PDPageNode node = (PDPageNode) next;
093: totalCount += node.updateCount();
094: }
095: }
096: page.setItem(COSName.COUNT, new COSInteger(totalCount));
097: return totalCount;
098: }
099:
100: /**
101: * This will get the count of descendent page objects.
102: *
103: * @return The total number of descendent page objects.
104: */
105: public long getCount() {
106: return ((COSNumber) page.getDictionaryObject(COSName.COUNT))
107: .intValue();
108: }
109:
110: /**
111: * This will get the underlying dictionary that this class acts on.
112: *
113: * @return The underlying dictionary for this class.
114: */
115: public COSDictionary getDictionary() {
116: return page;
117: }
118:
119: /**
120: * This is the parent page node.
121: *
122: * @return The parent to this page.
123: */
124: public PDPageNode getParent() {
125: PDPageNode parent = null;
126: COSDictionary parentDic = (COSDictionary) page
127: .getDictionaryObject("Parent", "P");
128: if (parentDic != null) {
129: parent = new PDPageNode(parentDic);
130: }
131: return parent;
132: }
133:
134: /**
135: * This will set the parent of this page.
136: *
137: * @param parent The parent to this page node.
138: */
139: public void setParent(PDPageNode parent) {
140: page.setItem(COSName.PARENT, parent.getDictionary());
141: }
142:
143: /**
144: * {@inheritDoc}
145: */
146: public COSBase getCOSObject() {
147: return page;
148: }
149:
150: /**
151: * This will return all kids of this node, either PDPageNode or PDPage.
152: *
153: * @return All direct descendents of this node.
154: */
155: public List getKids() {
156: List actuals = new ArrayList();
157: COSArray kids = getAllKids(actuals, page, false);
158: return new COSArrayList(actuals, kids);
159: }
160:
161: /**
162: * This will return all kids of this node as PDPage.
163: *
164: * @param result All direct and indirect descendents of this node are added to this list.
165: */
166: public void getAllKids(List result) {
167: getAllKids(result, page, true);
168: }
169:
170: /**
171: * This will return all kids of the given page node as PDPage.
172: *
173: * @param result All direct and optionally indirect descendents of this node are added to this list.
174: * @param page Page dictionary of a page node.
175: * @param recurse if true indirect descendents are processed recursively
176: */
177: private static COSArray getAllKids(List result, COSDictionary page,
178: boolean recurse) {
179: COSArray kids = (COSArray) page
180: .getDictionaryObject(COSName.KIDS);
181:
182: for (int i = 0; i < kids.size(); i++) {
183: COSBase obj = kids.getObject(i);
184: if (obj instanceof COSDictionary) {
185: COSDictionary kid = (COSDictionary) obj;
186: if (COSName.PAGE.equals(kid
187: .getDictionaryObject(COSName.TYPE))) {
188: result.add(new PDPage(kid));
189: } else {
190: if (recurse) {
191: getAllKids(result, kid, recurse);
192: } else {
193: result.add(new PDPageNode(kid));
194: }
195: }
196: }
197: }
198: return kids;
199: }
200:
201: /**
202: * This will get the resources at this page node and not look up the hierarchy.
203: * This attribute is inheritable, and findResources() should probably used.
204: * This will return null if no resources are available at this level.
205: *
206: * @return The resources at this level in the hierarchy.
207: */
208: public PDResources getResources() {
209: PDResources retval = null;
210: COSDictionary resources = (COSDictionary) page
211: .getDictionaryObject(COSName.RESOURCES);
212: if (resources != null) {
213: retval = new PDResources(resources);
214: }
215: return retval;
216: }
217:
218: /**
219: * This will find the resources for this page by looking up the hierarchy until
220: * it finds them.
221: *
222: * @return The resources at this level in the hierarchy.
223: */
224: public PDResources findResources() {
225: PDResources retval = getResources();
226: PDPageNode parent = getParent();
227: if (retval == null && parent != null) {
228: retval = parent.findResources();
229: }
230: return retval;
231: }
232:
233: /**
234: * This will set the resources for this page.
235: *
236: * @param resources The new resources for this page.
237: */
238: public void setResources(PDResources resources) {
239: if (resources == null) {
240: page.removeItem(COSName.RESOURCES);
241: } else {
242: page.setItem(COSName.RESOURCES, resources
243: .getCOSDictionary());
244: }
245: }
246:
247: /**
248: * This will get the MediaBox at this page and not look up the hierarchy.
249: * This attribute is inheritable, and findMediaBox() should probably used.
250: * This will return null if no MediaBox are available at this level.
251: *
252: * @return The MediaBox at this level in the hierarchy.
253: */
254: public PDRectangle getMediaBox() {
255: PDRectangle retval = null;
256: COSArray array = (COSArray) page
257: .getDictionaryObject(COSName.MEDIA_BOX);
258: if (array != null) {
259: retval = new PDRectangle(array);
260: }
261: return retval;
262: }
263:
264: /**
265: * This will find the MediaBox for this page by looking up the hierarchy until
266: * it finds them.
267: *
268: * @return The MediaBox at this level in the hierarchy.
269: */
270: public PDRectangle findMediaBox() {
271: PDRectangle retval = getMediaBox();
272: PDPageNode parent = getParent();
273: if (retval == null && parent != null) {
274: retval = parent.findMediaBox();
275: }
276: return retval;
277: }
278:
279: /**
280: * This will set the mediaBox for this page.
281: *
282: * @param mediaBox The new mediaBox for this page.
283: */
284: public void setMediaBox(PDRectangle mediaBox) {
285: if (mediaBox == null) {
286: page.removeItem(COSName.MEDIA_BOX);
287: } else {
288: page.setItem(COSName.MEDIA_BOX, mediaBox.getCOSArray());
289: }
290: }
291:
292: /**
293: * This will get the CropBox at this page and not look up the hierarchy.
294: * This attribute is inheritable, and findCropBox() should probably used.
295: * This will return null if no CropBox is available at this level.
296: *
297: * @return The CropBox at this level in the hierarchy.
298: */
299: public PDRectangle getCropBox() {
300: PDRectangle retval = null;
301: COSArray array = (COSArray) page
302: .getDictionaryObject(COSName.CROP_BOX);
303: if (array != null) {
304: retval = new PDRectangle(array);
305: }
306: return retval;
307: }
308:
309: /**
310: * This will find the CropBox for this page by looking up the hierarchy until
311: * it finds them.
312: *
313: * @return The CropBox at this level in the hierarchy.
314: */
315: public PDRectangle findCropBox() {
316: PDRectangle retval = getCropBox();
317: PDPageNode parent = getParent();
318: if (retval == null && parent != null) {
319: retval = findParentCropBox(parent);
320: }
321:
322: //default value for cropbox is the media box
323: if (retval == null) {
324: retval = findMediaBox();
325: }
326: return retval;
327: }
328:
329: /**
330: * This will search for a crop box in the parent and return null if it is not
331: * found. It will NOT default to the media box if it cannot be found.
332: *
333: * @param node The node
334: */
335: private PDRectangle findParentCropBox(PDPageNode node) {
336: PDRectangle rect = node.getCropBox();
337: PDPageNode parent = node.getParent();
338: if (rect == null && parent != null) {
339: rect = findParentCropBox(node);
340: }
341: return rect;
342: }
343:
344: /**
345: * This will set the CropBox for this page.
346: *
347: * @param cropBox The new CropBox for this page.
348: */
349: public void setCropBox(PDRectangle cropBox) {
350: if (cropBox == null) {
351: page.removeItem(COSName.CROP_BOX);
352: } else {
353: page.setItem(COSName.CROP_BOX, cropBox.getCOSArray());
354: }
355: }
356:
357: /**
358: * A value representing the rotation. This will be null if not set at this level
359: * The number of degrees by which the page should
360: * be rotated clockwise when displayed or printed. The value must be a multiple
361: * of 90.
362: *
363: * This will get the rotation at this page and not look up the hierarchy.
364: * This attribute is inheritable, and findRotation() should probably used.
365: * This will return null if no rotation is available at this level.
366: *
367: * @return The rotation at this level in the hierarchy.
368: */
369: public Integer getRotation() {
370: Integer retval = null;
371: COSNumber value = (COSNumber) page
372: .getDictionaryObject(COSName.ROTATE);
373: if (value != null) {
374: retval = new Integer(value.intValue());
375: }
376: return retval;
377: }
378:
379: /**
380: * This will find the rotation for this page by looking up the hierarchy until
381: * it finds them.
382: *
383: * @return The rotation at this level in the hierarchy.
384: */
385: public int findRotation() {
386: int retval = 0;
387: Integer rotation = getRotation();
388: if (rotation != null) {
389: retval = rotation.intValue();
390: } else {
391: PDPageNode parent = getParent();
392: if (parent != null) {
393: retval = parent.findRotation();
394: }
395: }
396:
397: return retval;
398: }
399:
400: /**
401: * This will set the rotation for this page.
402: *
403: * @param rotation The new rotation for this page.
404: */
405: public void setRotation(int rotation) {
406: page.setItem(COSName.ROTATE, new COSInteger(rotation));
407: }
408: }
|