001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - 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: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.pd;
031:
032: import java.util.List;
033: import de.intarsys.pdf.cds.CDSRectangle;
034: import de.intarsys.pdf.cos.COSArray;
035: import de.intarsys.pdf.cos.COSBasedObject;
036: import de.intarsys.pdf.cos.COSDictionary;
037: import de.intarsys.pdf.cos.COSInteger;
038: import de.intarsys.pdf.cos.COSName;
039: import de.intarsys.pdf.cos.COSObject;
040:
041: /**
042: * An abstract superclass for pages and page tree nodes
043: */
044: public abstract class PDPageNode extends PDObject {
045: /**
046: * The meta class implementation
047: */
048: public static class MetaClass extends PDObject.MetaClass {
049: protected MetaClass(Class instanceClass) {
050: super (instanceClass);
051: }
052:
053: protected COSBasedObject.MetaClass doDetermineClass(
054: COSObject object) {
055: COSName type = ((COSDictionary) object).get(DK_Type)
056: .asName();
057: if (DK_Pages.equals(type)) {
058: return PDPageTree.META;
059: }
060: return PDPage.META;
061: }
062:
063: public Class getRootClass() {
064: return PDPageNode.class;
065: }
066: }
067:
068: /** The meta class instance */
069: public static final MetaClass META = new MetaClass(MetaClass.class
070: .getDeclaringClass());
071:
072: public static final COSName DK_Parent = COSName.constant("Parent");
073:
074: public static final COSName DK_Pages = COSName.constant("Pages");
075:
076: public static final COSName CN_Type_Page = COSName.constant("Page");
077:
078: public static final COSName DK_Rotate = COSName.constant("Rotate"); //$NON-NLS-1$
079:
080: /** the parent node of this, required except in root node */
081: private PDPageTree cachedParent;
082:
083: /**
084: * Create the receiver class from an already defined {@link COSDictionary}.
085: * NEVER use the constructor directly.
086: *
087: * @param object
088: * the PDDocument containing the new object
089: */
090: protected PDPageNode(COSObject object) {
091: super (object);
092: }
093:
094: abstract protected void collectAnnotations(List annotations);
095:
096: /**
097: * @deprecated
098: *
099: */
100: public void dispose() {
101: PDPageTree parent = getParent();
102: if (parent != null) {
103: parent.removeNode(this );
104: }
105: }
106:
107: /**
108: * The total number of pages represented by this node.
109: *
110: * @return The total number of pages represented by this node.
111: */
112: public abstract int getCount();
113:
114: /**
115: * The rectangle in user space coordinates defining the visible region of
116: * the page. User space is measured in 1/72 inch initially
117: *
118: * @return The rectangle in user space coordinates defining the visible
119: * region of the page
120: */
121: public CDSRectangle getCropBox() {
122: COSArray array = cosGetFieldInheritable(PDPage.DK_CropBox)
123: .asArray();
124: if (array == null) {
125: return getMediaBox(); // default for CropBox
126: }
127: return CDSRectangle.createFromCOS(array);
128: }
129:
130: /**
131: * The first {@link PDAnnotation} linked on this page.
132: *
133: * @return The first {@link PDAnnotation} linked on this page.
134: */
135: public PDAnnotation getFirstAnnotation() {
136: PDPage currentPage = getFirstPage();
137: while (currentPage != null) {
138: PDAnnotation annotation = currentPage.getFirstAnnotation();
139: if (annotation != null) {
140: return annotation;
141: }
142: currentPage = currentPage.getNextPage();
143: }
144: return null;
145: }
146:
147: /**
148: * Get the first node within the receiver or the receiver if it is not a
149: * collection (page tree).
150: *
151: * <p>
152: * This may return null if the receiver is an empty collection.
153: * </p>
154: *
155: * @return Get the first node within the receiver or the receiver if it is
156: * not a collection (page tree).
157: */
158: public abstract PDPageNode getFirstNode();
159:
160: /**
161: * Get the first page (leaf node) within the receiver hierarchy.
162: *
163: * @return Get the first page (leaf node) within the receiver hierarchy.
164: */
165: public abstract PDPage getFirstPage();
166:
167: /*
168: * (non-Javadoc)
169: *
170: * @see de.intarsys.pdf.pd.PDObject#getGenericParent()
171: */
172: public PDObject getGenericParent() {
173: return getParent();
174: }
175:
176: /**
177: * The last {@link PDAnnotation} linked on this page.
178: *
179: * @return The last {@link PDAnnotation} linked on this page.
180: */
181: public PDAnnotation getLastAnnotation() {
182: PDPage currentPage = getLastPage();
183: while (currentPage != null) {
184: PDAnnotation annotation = currentPage.getLastAnnotation();
185: if (annotation != null) {
186: return annotation;
187: }
188: currentPage = currentPage.getPreviousPage();
189: }
190: return null;
191: }
192:
193: /**
194: * Get the last node within the receiver or the receiver if it is not a
195: * collection (page tree).
196: *
197: * <p>
198: * This may return null if the receiver is an empty collection.
199: * </p>
200: *
201: * @return Get the last node within the receiver or the receiver if it is
202: * not a collection (page tree).
203: */
204: public abstract PDPageNode getLastNode();
205:
206: /**
207: * Get the last page (leaf node) within the receiver hierarchy.
208: *
209: * @return Get the last page (leaf node) within the receiver hierarchy.
210: */
211: public PDPage getLastPage() {
212: PDPageNode last = getLastNode();
213: if (last == null) {
214: return null;
215: }
216: return last.getLastPage();
217: }
218:
219: /**
220: * The rectangle in user space coordinates defining the physical page
221: * boundaries. user space is measured in 1/72 inch initially
222: *
223: * @return The rectangle in user space coordinates defining the physical
224: * page boundaries
225: */
226: public CDSRectangle getMediaBox() {
227: COSArray array = cosGetFieldInheritable(PDPage.DK_MediaBox)
228: .asArray();
229: if (array == null) {
230: // tood 1 @lazy
231: // todo 3 this can't happen: MediaBox is a required attribute
232: setMediaBox(new CDSRectangle(CDSRectangle.SIZE_A4));
233: array = (COSArray) cosGetField(PDPage.DK_MediaBox);
234: }
235: return CDSRectangle.createFromCOS(array);
236: }
237:
238: /**
239: * Get the next node after the receiver.
240: *
241: * @return Get the next node after the receiver.
242: */
243: public PDPageNode getNextNode() {
244: PDPageTree tree = getParent();
245: if (tree == null) {
246: return null;
247: }
248: return tree.getNextNode(this );
249: }
250:
251: /**
252: * The zero based index of <code>this</code> within the document.
253: *
254: * @return The zero based index of <code>this</code> within the document.
255: */
256: public int getNodeIndex() {
257: PDPageTree myParent = getParent();
258: if (myParent == null) {
259: return 0;
260: }
261: return myParent.getNodeIndex(this );
262: }
263:
264: /**
265: * The page at <code>index</code> within the receivers subtree.
266: *
267: * @param index
268: * The page index
269: * @return The page at <code>index</code> within the receivers subtree.
270: */
271: public PDPage getPageAt(int index) {
272: if (index >= getCount()) {
273: PDPageNode next = getNextNode();
274: if (next == null) {
275: return null;
276: }
277: return next.getPageAt(index - getCount());
278: }
279: if (index < 0) {
280: PDPageNode previous = getPreviousNode();
281: if (previous == null) {
282: return null;
283: }
284: return previous.getPageAt(previous.getCount() + index);
285: }
286: PDPageNode first = getFirstNode();
287: if (first == null) {
288: return null;
289: }
290: return first.getPageAt(index);
291: }
292:
293: /**
294: * The parent node if available. The root tree node of the document has no
295: * parent.
296: *
297: * @return The parent node if available.
298: */
299: public PDPageTree getParent() {
300: if (cachedParent == null) {
301: COSDictionary parentDict = cosGetField(DK_Parent)
302: .asDictionary();
303: if (parentDict != null) {
304: cachedParent = (PDPageTree) PDPageNode.META
305: .createFromCos(parentDict);
306: }
307: }
308: return cachedParent;
309: }
310:
311: /**
312: * The previous node .
313: *
314: * @return The previous node.
315: */
316: public PDPageNode getPreviousNode() {
317: PDPageTree tree = getParent();
318: if (tree == null) {
319: return null;
320: }
321: return tree.getPreviousNode(this );
322: }
323:
324: /**
325: * The number of degrees by which the page should be rotated clockwise when
326: * displayed or printed. The value must be a multiple of 90. Default value:
327: * 0.
328: *
329: * <p>
330: * <b>Notice: the value is inheritable</b>
331: * </p>
332: *
333: * @return Rotation as a multiple of 90
334: */
335: public int getRotate() {
336: COSInteger rotate = cosGetFieldInheritable(DK_Rotate)
337: .asInteger();
338: if (rotate == null) {
339: return 0;
340: }
341: return rotate.intValue();
342: }
343:
344: /*
345: * (non-Javadoc)
346: *
347: * @see de.intarsys.pdf.cos.COSBasedObject#invalidateCaches()
348: */
349: public void invalidateCaches() {
350: super .invalidateCaches();
351: cachedParent = null;
352: }
353:
354: /**
355: * Answer <code>true</code> if this is a single page node.
356: *
357: * @return Answer <code>true</code> if this is a single page node.
358: */
359: public abstract boolean isPage();
360:
361: /**
362: * Set the rectangle in user space coordinates defining the visible region
363: * of the page. user space is measured in 1/72 inch initially
364: *
365: * @param rect
366: * The rectangle defining the visible page region
367: */
368: public void setCropBox(CDSRectangle rect) {
369: setFieldObject(PDPage.DK_CropBox, rect);
370: }
371:
372: /*
373: * (non-Javadoc)
374: *
375: * @see de.intarsys.pdf.pd.PDObject#setGenericParent(de.intarsys.pdf.pd.PDObject)
376: */
377: public void setGenericParent(PDObject newParent) {
378: if (!(newParent instanceof PDPageTree)) {
379: throw new IllegalArgumentException(
380: "parent must be a PDPAgeTree");
381: }
382: setParent((PDPageTree) newParent);
383: }
384:
385: /**
386: * Set the rectangle in user space coordinates defining the physical page
387: * boundaries. user space is measured in 1/72 inch initially
388: *
389: * @param rect
390: * The rectangle defining the physical page boundaries
391: */
392: public void setMediaBox(CDSRectangle rect) {
393: setFieldObject(PDPage.DK_MediaBox, rect);
394: }
395:
396: /**
397: * Sets the parent.
398: *
399: * @param parent
400: * The parent to set
401: */
402: protected void setParent(PDPageTree parent) {
403: setFieldObject(DK_Parent, parent);
404: }
405:
406: /**
407: * The number of degrees by which the page should be rotated clockwise when
408: * displayed or printed. The value must be a multiple of 90. If a value of 0
409: * is set, which is the default, the field will be cleared.
410: *
411: * <p>
412: * <b>Notice: This object and its children are affected on a change!</b>
413: * </p>
414: *
415: * @param rotate
416: * A multiple of 90, the value is <b>not</b> checked for a legal
417: * value
418: */
419: public void setRotate(int rotate) {
420: COSObject newObject = COSInteger.create(rotate);
421: COSObject inheritedObject = cosGetFieldInherited(DK_Rotate);
422: if (newObject.equals(inheritedObject)) {
423: // same as parent - remove
424: cosSetFieldInheritable(DK_Rotate, null);
425: } else if (inheritedObject.isNull() && rotate == 0) {
426: // default
427: cosSetFieldInheritable(DK_Rotate, null);
428: } else {
429: cosSetFieldInheritable(DK_Rotate, newObject);
430: }
431: }
432: }
|