001: /**
002: * Copyright (c) 2005, 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.interactive.documentnavigation.outline;
031:
032: import java.awt.Color;
033: import java.io.IOException;
034: import java.util.List;
035:
036: import org.pdfbox.cos.COSArray;
037: import org.pdfbox.cos.COSDictionary;
038: import org.pdfbox.cos.COSFloat;
039: import org.pdfbox.exceptions.OutlineNotLocalException;
040: import org.pdfbox.pdmodel.PDDestinationNameTreeNode;
041: import org.pdfbox.pdmodel.PDDocument;
042: import org.pdfbox.pdmodel.PDDocumentNameDictionary;
043: import org.pdfbox.pdmodel.PDPage;
044: import org.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
045: import org.pdfbox.pdmodel.graphics.color.PDColorSpaceInstance;
046: import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
047: import org.pdfbox.pdmodel.interactive.action.type.PDAction;
048: import org.pdfbox.pdmodel.interactive.action.type.PDActionGoTo;
049: import org.pdfbox.pdmodel.interactive.action.PDActionFactory;
050: import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
051: import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
052: import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageDestination;
053: import org.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
054: import org.pdfbox.util.BitFlagHelper;
055:
056: /**
057: * This represents an outline in a pdf document.
058: *
059: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
060: * @version $Revision: 1.7 $
061: */
062: public class PDOutlineItem extends PDOutlineNode {
063:
064: private static final int ITALIC_FLAG = 1;
065: private static final int BOLD_FLAG = 2;
066:
067: /**
068: * Default Constructor.
069: */
070: public PDOutlineItem() {
071: super ();
072: }
073:
074: /**
075: * Constructor for an existing outline item.
076: *
077: * @param dic The storage dictionary.
078: */
079: public PDOutlineItem(COSDictionary dic) {
080: super (dic);
081: }
082:
083: /**
084: * Insert a sibling after this node.
085: *
086: * @param item The item to insert.
087: */
088: public void insertSiblingAfter(PDOutlineItem item) {
089: item.setParent(getParent());
090: PDOutlineItem next = getNextSibling();
091: setNextSibling(item);
092: item.setPreviousSibling(this );
093: if (next != null) {
094: item.setNextSibling(next);
095: next.setPreviousSibling(item);
096: }
097: updateParentOpenCount(1);
098: }
099:
100: /**
101: * Return the previous sibling or null if there is no sibling.
102: *
103: * @return The previous sibling.
104: */
105: public PDOutlineItem getPreviousSibling() {
106: PDOutlineItem last = null;
107: COSDictionary lastDic = (COSDictionary) node
108: .getDictionaryObject("Prev");
109: if (lastDic != null) {
110: last = new PDOutlineItem(lastDic);
111: }
112: return last;
113: }
114:
115: /**
116: * Set the previous sibling, this will be maintained by this class.
117: *
118: * @param outlineNode The new previous sibling.
119: */
120: protected void setPreviousSibling(PDOutlineNode outlineNode) {
121: node.setItem("Prev", outlineNode);
122: }
123:
124: /**
125: * Return the next sibling or null if there is no next sibling.
126: *
127: * @return The next sibling.
128: */
129: public PDOutlineItem getNextSibling() {
130: PDOutlineItem last = null;
131: COSDictionary lastDic = (COSDictionary) node
132: .getDictionaryObject("Next");
133: if (lastDic != null) {
134: last = new PDOutlineItem(lastDic);
135: }
136: return last;
137: }
138:
139: /**
140: * Set the next sibling, this will be maintained by this class.
141: *
142: * @param outlineNode The new next sibling.
143: */
144: protected void setNextSibling(PDOutlineNode outlineNode) {
145: node.setItem("Next", outlineNode);
146: }
147:
148: /**
149: * Get the title of this node.
150: *
151: * @return The title of this node.
152: */
153: public String getTitle() {
154: return node.getString("Title");
155: }
156:
157: /**
158: * Set the title for this node.
159: *
160: * @param title The new title for this node.
161: */
162: public void setTitle(String title) {
163: node.setString("Title", title);
164: }
165:
166: /**
167: * Get the page destination of this node.
168: *
169: * @return The page destination of this node.
170: * @throws IOException If there is an error creating the destination.
171: */
172: public PDDestination getDestination() throws IOException {
173: return PDDestination.create(node.getDictionaryObject("Dest"));
174: }
175:
176: /**
177: * Set the page destination for this node.
178: *
179: * @param dest The new page destination for this node.
180: */
181: public void setDestination(PDDestination dest) {
182: node.setItem("Dest", dest);
183: }
184:
185: /**
186: * A convenience method that will create an XYZ destination using only the defaults.
187: *
188: * @param page The page to refer to.
189: */
190: public void setDestination(PDPage page) {
191: PDPageXYZDestination dest = null;
192: if (page != null) {
193: dest = new PDPageXYZDestination();
194: dest.setPage(page);
195: }
196: setDestination(dest);
197: }
198:
199: /**
200: * This method will attempt to find the page in this PDF document that this outline points to.
201: * If the outline does not point to anything then this method will return null. If the outline
202: * is an action that is not a GoTo action then this methods will throw the OutlineNotLocationException
203: *
204: * @param doc The document to get the page from.
205: *
206: * @return The page that this outline will go to when activated or null if it does not point to anything.
207: * @throws IOException If there is an error when trying to find the page.
208: */
209: public PDPage findDestinationPage(PDDocument doc)
210: throws IOException {
211: PDPage page = null;
212: PDDestination rawDest = getDestination();
213: if (rawDest == null) {
214: PDAction outlineAction = getAction();
215: if (outlineAction instanceof PDActionGoTo) {
216: rawDest = ((PDActionGoTo) outlineAction)
217: .getDestination();
218: } else if (outlineAction == null) {
219: //if the outline action is null then this outline does not refer
220: //to anything and we will just return null.
221: } else {
222: throw new OutlineNotLocalException(
223: "Error: Outline does not reference a local page.");
224: }
225: }
226:
227: PDPageDestination pageDest = null;
228: if (rawDest instanceof PDNamedDestination) {
229: //if we have a named destination we need to lookup the PDPageDestination
230: PDNamedDestination namedDest = (PDNamedDestination) rawDest;
231: PDDocumentNameDictionary namesDict = doc
232: .getDocumentCatalog().getNames();
233: if (namesDict != null) {
234: PDDestinationNameTreeNode destsTree = namesDict
235: .getDests();
236: if (destsTree != null) {
237: pageDest = (PDPageDestination) destsTree
238: .getValue(namedDest.getNamedDestination());
239: }
240: }
241: } else if (rawDest instanceof PDPageDestination) {
242: pageDest = (PDPageDestination) rawDest;
243: } else if (rawDest == null) {
244: //if the destination is null then we will simply return a null page.
245: } else {
246: throw new IOException("Error: Unknown destination type "
247: + rawDest);
248: }
249:
250: if (pageDest != null) {
251: page = pageDest.getPage();
252: if (page == null) {
253: int pageNumber = pageDest.getPageNumber();
254: if (pageNumber != -1) {
255: List allPages = doc.getDocumentCatalog()
256: .getAllPages();
257: page = (PDPage) allPages.get(pageNumber);
258: }
259: }
260: }
261:
262: return page;
263: }
264:
265: /**
266: * Get the action of this node.
267: *
268: * @return The action of this node.
269: */
270: public PDAction getAction() {
271: return PDActionFactory.createAction((COSDictionary) node
272: .getDictionaryObject("A"));
273: }
274:
275: /**
276: * Set the action for this node.
277: *
278: * @param action The new action for this node.
279: */
280: public void setAction(PDAction action) {
281: node.setItem("A", action);
282: }
283:
284: /**
285: * Get the structure element of this node.
286: *
287: * @return The structure element of this node.
288: */
289: public PDStructureElement getStructureElement() {
290: PDStructureElement se = null;
291: COSDictionary dic = (COSDictionary) node
292: .getDictionaryObject("SE");
293: if (dic != null) {
294: se = new PDStructureElement(dic);
295: }
296: return se;
297: }
298:
299: /**
300: * Set the structure element for this node.
301: *
302: * @param structureElement The new structure element for this node.
303: */
304: public void setStructuredElement(PDStructureElement structureElement) {
305: node.setItem("SE", structureElement);
306: }
307:
308: /**
309: * Get the text color of this node. Default is black and this method
310: * will never return null.
311: *
312: * @return The structure element of this node.
313: */
314: public PDColorSpaceInstance getTextColor() {
315: PDColorSpaceInstance retval = null;
316: COSArray csValues = (COSArray) node.getDictionaryObject("C");
317: if (csValues == null) {
318: csValues = new COSArray();
319: csValues.growToSize(3, new COSFloat(0));
320: node.setItem("C", csValues);
321: }
322: retval = new PDColorSpaceInstance(csValues);
323: retval.setColorSpace(PDDeviceRGB.INSTANCE);
324: return retval;
325: }
326:
327: /**
328: * Set the text color for this node. The colorspace must be a PDDeviceRGB.
329: *
330: * @param textColor The text color for this node.
331: */
332: public void setTextColor(PDColorSpaceInstance textColor) {
333: node.setItem("C", textColor.getCOSColorSpaceValue());
334: }
335:
336: /**
337: * Set the text color for this node. The colorspace must be a PDDeviceRGB.
338: *
339: * @param textColor The text color for this node.
340: */
341: public void setTextColor(Color textColor) {
342: COSArray array = new COSArray();
343: array.add(new COSFloat(textColor.getRed() / 255f));
344: array.add(new COSFloat(textColor.getGreen() / 255f));
345: array.add(new COSFloat(textColor.getBlue() / 255f));
346: node.setItem("C", array);
347: }
348:
349: /**
350: * A flag telling if the text should be italic.
351: *
352: * @return The italic flag.
353: */
354: public boolean isItalic() {
355: return BitFlagHelper.getFlag(node, "F", ITALIC_FLAG);
356: }
357:
358: /**
359: * Set the italic property of the text.
360: *
361: * @param italic The new italic flag.
362: */
363: public void setItalic(boolean italic) {
364: BitFlagHelper.setFlag(node, "F", ITALIC_FLAG, italic);
365: }
366:
367: /**
368: * A flag telling if the text should be bold.
369: *
370: * @return The bold flag.
371: */
372: public boolean isBold() {
373: return BitFlagHelper.getFlag(node, "F", BOLD_FLAG);
374: }
375:
376: /**
377: * Set the bold property of the text.
378: *
379: * @param bold The new bold flag.
380: */
381: public void setBold(boolean bold) {
382: BitFlagHelper.setFlag(node, "F", BOLD_FLAG, bold);
383: }
384:
385: }
|