001: /*
002: * $Id: PDFOutline.java,v 1.1.1.1 2001/10/29 19:51:08 ezb Exp $
003: *
004: * $Date: 2001/10/29 19:51:08 $
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package gnu.jpdf;
021:
022: import java.io.*;
023: import java.util.*;
024:
025: /**
026: * This class manages the documents outlines (also known as bookmarks).
027: *
028: * @author Peter T Mount http://www.retep.org.uk/pdf/
029: * @author Eric Z. Beard, ericzbeard@hotmail.com
030: * @author $Author: ezb $
031: * @version $Revision: 1.1.1.1 $, $Date: 2001/10/29 19:51:08 $
032: */
033: public class PDFOutline extends PDFObject implements Serializable {
034:
035: /*
036: * NOTE: The original class is the work of Peter T. Mount, who released it
037: * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as
038: * follows:
039: * The package name was changed to gnu.pdf.
040: * The formatting was changed a little bit.
041: * It is still licensed under the LGPL.
042: */
043:
044: /**
045: * This holds any outlines below us
046: */
047: private Vector outlines;
048:
049: /**
050: * For subentries, this points to it's parent outline
051: */
052: protected PDFOutline parent;
053:
054: /**
055: * This is this outlines Title
056: */
057: private String title;
058:
059: /**
060: * The destination page
061: */
062: PDFPage dest;
063:
064: /**
065: * The region on the destination page
066: */
067: int l, b, r, t;
068:
069: /**
070: * How the destination is handled
071: */
072: boolean destMode;
073:
074: /**
075: * When jumping to the destination, display the whole page
076: */
077: static final boolean FITPAGE = false;
078:
079: /**
080: * When jumping to the destination, display the specified region
081: */
082: static final boolean FITRECT = true;
083:
084: /**
085: * Constructs a PDF Outline object. This method is used internally only.
086: */
087: protected PDFOutline() {
088: super ("/Outlines");
089: outlines = new Vector();
090: title = null;
091: dest = null;
092: destMode = FITPAGE;
093: }
094:
095: /**
096: * Constructs a PDF Outline object. When selected, the whole page is
097: * displayed.
098: *
099: * @param title Title of the outline
100: * @param dest The destination page
101: */
102: public PDFOutline(String title, PDFPage dest) {
103: this ();
104: this .title = title;
105: this .dest = dest;
106: }
107:
108: /**
109: * Constructs a PDF Outline object. When selected, the specified region
110: * is displayed.
111: *
112: * @param title Title of the outline
113: * @param dest The destination page
114: * @param l left coordinate
115: * @param b bottom coordinate
116: * @param r right coordinate
117: * @param t top coordinate
118: */
119: public PDFOutline(String title, PDFPage dest, int l, int b, int r,
120: int t) {
121: this (title, dest);
122: this .destMode = FITRECT;
123: this .l = l;
124: this .b = b;
125: this .r = r;
126: this .t = t;
127: }
128:
129: /**
130: * This method creates an outline, and attaches it to this one.
131: * When the outline is selected, the entire page is displayed.
132: *
133: * <p>This allows you to have an outline for say a Chapter,
134: * then under the chapter, one for each section. You are not really
135: * limited on how deep you go, but it's best not to go below say 6 levels,
136: * for the reader's sake.
137: *
138: * @param title Title of the outline
139: * @param dest The destination page
140: * @return PDFOutline object created, for creating sub-outlines
141: */
142: public PDFOutline add(String title, PDFPage dest) {
143: PDFOutline outline = new PDFOutline(title, dest);
144: pdfDocument.add(outline); // add to the pdf first!
145: add(outline);
146: return outline;
147: }
148:
149: /**
150: * This method creates an outline, and attaches it to this one.
151: * When the outline is selected, the supplied region is displayed.
152: *
153: * <p>Note: the coordiates are in Java space. They are converted to User
154: * space.
155: *
156: * <p>This allows you to have an outline for say a Chapter,
157: * then under the chapter, one for each section. You are not really
158: * limited on how deep you go, but it's best not to go below say 6 levels,
159: * for the reader's sake.
160: *
161: * @param title Title of the outline
162: * @param dest The destination page
163: * @param x coordinate of region in Java space
164: * @param y coordinate of region in Java space
165: * @param w width of region in Java space
166: * @param h height of region in Java space
167: * @return PDFOutline object created, for creating sub-outlines
168: */
169: public PDFOutline add(String title, PDFPage dest, int x, int y,
170: int w, int h) {
171: int xy1[] = dest.cxy(x, y + h);
172: int xy2[] = dest.cxy(x + w, y);
173: PDFOutline outline = new PDFOutline(title, dest, xy1[0],
174: xy1[1], xy2[0], xy2[1]);
175: pdfDocument.add(outline); // add to the pdf first!
176: add(outline);
177: return outline;
178: }
179:
180: /**
181: * This adds an already existing outline to this one.
182: *
183: * <p>Note: the outline must have been added to the PDF document before
184: * calling this method. Normally the other add methods are used.
185: *
186: * @param page PDFOutline to add
187: */
188: public void add(PDFOutline outline) {
189: outlines.addElement(outline);
190:
191: // Tell the outline of ourselves
192: outline.parent = this ;
193: }
194:
195: /**
196: * @param os OutputStream to send the object to
197: * @exception IOException on error
198: */
199: public void write(OutputStream os) throws IOException {
200: // Write the object header
201: writeStart(os);
202:
203: // now the objects body
204:
205: // These are for kids only
206: if (parent != null) {
207: os.write("/Title ".getBytes());
208: os.write(PDFStringHelper.makePDFString(title).getBytes());
209: os.write("\n/Dest [".getBytes());
210: os.write(dest.toString().getBytes());
211:
212: if (destMode == FITPAGE) {
213: //os.write(" null null null]\n/Parent ".getBytes());
214: os.write(" /Fit]\n/Parent ".getBytes());
215: } else {
216: os.write(" /FitR ".getBytes());
217: os.write(Integer.toString(l).getBytes());
218: os.write(" ".getBytes());
219: os.write(Integer.toString(b).getBytes());
220: os.write(" ".getBytes());
221: os.write(Integer.toString(r).getBytes());
222: os.write(" ".getBytes());
223: os.write(Integer.toString(t).getBytes());
224: os.write("]\n/Parent ".getBytes());
225: }
226: os.write(parent.toString().getBytes());
227: os.write("\n".getBytes());
228: }
229:
230: // the number of outlines in this document
231: if (parent == null) {
232: // were the top level node, so all are open by default
233: os.write("/Count ".getBytes());
234: os.write(Integer.toString(outlines.size()).getBytes());
235: os.write("\n".getBytes());
236: } else {
237: // were a decendent, so by default we are closed. Find out how many
238: // entries are below us
239: int c = descendants();
240: if (c > 0) {
241: os.write("/Count ".getBytes());
242: os.write(Integer.toString(-c).getBytes());
243: os.write("\n".getBytes());
244: }
245: }
246:
247: // These only valid if we have children
248: if (outlines.size() > 0) {
249: // the number of the first outline in list
250: os.write("/First ".getBytes());
251: os.write(outlines.elementAt(0).toString().getBytes());
252: os.write("\n".getBytes());
253:
254: // the number of the last outline in list
255: os.write("/Last ".getBytes());
256: os.write(outlines.elementAt(outlines.size() - 1).toString()
257: .getBytes());
258: os.write("\n".getBytes());
259: }
260:
261: if (parent != null) {
262: int index = parent.getIndex(this );
263: if (index > 0) {
264: // Now if were not the first, then we have a /Prev node
265: os.write("/Prev ".getBytes());
266: os.write(parent.getNode(index - 1).toString()
267: .getBytes());
268: os.write("\n".getBytes());
269: }
270: if (index < parent.getLast()) {
271: // We have a /Next node
272: os.write("/Next ".getBytes());
273: os.write(parent.getNode(index + 1).toString()
274: .getBytes());
275: os.write("\n".getBytes());
276: }
277: }
278:
279: // finish off with its footer
280: writeEnd(os);
281: }
282:
283: /**
284: * This is called by children to find their position in this outlines
285: * tree.
286: *
287: * @param outline PDFOutline to search for
288: * @return index within Vector
289: */
290: protected int getIndex(PDFOutline outline) {
291: return outlines.indexOf(outline);
292: }
293:
294: /**
295: * Returns the last index in this outline
296: * @return last index in outline
297: */
298: protected int getLast() {
299: return outlines.size() - 1;
300: }
301:
302: /**
303: * Returns the outline at a specified position.
304: * @param i index
305: * @return the node at index i
306: */
307: protected PDFOutline getNode(int i) {
308: return (PDFOutline) (outlines.elementAt(i));
309: }
310:
311: /**
312: * Returns all outlines directly below this one.
313: * @return Enumeration of child elements
314: */
315: public Enumeration elements() {
316: return outlines.elements();
317: }
318:
319: /**
320: * Returns the total number of descendants below this one.
321: * @return the number of descendants below this one
322: */
323: protected int descendants() {
324: int c = outlines.size(); // initially the number of kids
325:
326: // now call each one for their descendants
327: for (Enumeration en = outlines.elements(); en.hasMoreElements();) {
328: c += ((PDFOutline) en.nextElement()).descendants();
329: }
330:
331: return c;
332: }
333: } // end class PDFOutline
|