/* * $Id: PDFOutline.java,v 1.3 2007/09/22 12:58:40 gil1 Exp $ * * $Date: 2007/09/22 12:58:40 $ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package gnu.jpdf; import java.io.*; import java.util.*; /** * This class manages the documents outlines (also known as bookmarks). * * @author Peter T Mount http://www.retep.org.uk/pdf/ * @author Eric Z. Beard, ericzbeard@hotmail.com * @version $Revision: 1.3 $, $Date: 2007/09/22 12:58:40 $ */ public class PDFOutline extends PDFObject implements Serializable { /* * NOTE: The original class is the work of Peter T. Mount, who released it * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as * follows: * The package name was changed to gnu.pdf. * The formatting was changed a little bit. * It is still licensed under the LGPL. */ /** * This holds any outlines below us */ private Vector<PDFOutline> outlines; /** * For subentries, this points to it's parent outline */ protected PDFOutline parent; /** * This is this outlines Title */ private String title; /** * The destination page */ PDFPage dest; /** * The region on the destination page */ int l,b,r,t; /** * How the destination is handled */ boolean destMode; /** * When jumping to the destination, display the whole page */ static final boolean FITPAGE = false; /** * When jumping to the destination, display the specified region */ static final boolean FITRECT = true; /** * Constructs a PDF Outline object. This method is used internally only. */ protected PDFOutline() { super("/Outlines"); outlines = new Vector<PDFOutline>(); title = null; dest = null; destMode = FITPAGE; } /** * Constructs a PDF Outline object. When selected, the whole page is * displayed. * * @param title Title of the outline * @param dest The destination page */ public PDFOutline(String title,PDFPage dest) { this(); this.title = title; this.dest = dest; } /** * Constructs a PDF Outline object. When selected, the specified region * is displayed. * * @param title Title of the outline * @param dest The destination page * @param l left coordinate * @param b bottom coordinate * @param r right coordinate * @param t top coordinate */ public PDFOutline(String title,PDFPage dest,int l,int b,int r,int t) { this(title,dest); this.destMode = FITRECT; this.l = l; this.b = b; this.r = r; this.t = t; } /** * This method creates an outline, and attaches it to this one. * When the outline is selected, the entire page is displayed. * * <p>This allows you to have an outline for say a Chapter, * then under the chapter, one for each section. You are not really * limited on how deep you go, but it's best not to go below say 6 levels, * for the reader's sake. * * @param title Title of the outline * @param dest The destination page * @return PDFOutline object created, for creating sub-outlines */ public PDFOutline add(String title,PDFPage dest) { PDFOutline outline = new PDFOutline(title,dest); pdfDocument.add(outline); // add to the pdf first! add(outline); return outline; } /** * This method creates an outline, and attaches it to this one. * When the outline is selected, the supplied region is displayed. * * <p>Note: the coordiates are in Java space. They are converted to User * space. * * <p>This allows you to have an outline for say a Chapter, * then under the chapter, one for each section. You are not really * limited on how deep you go, but it's best not to go below say 6 levels, * for the reader's sake. * * @param title Title of the outline * @param dest The destination page * @param x coordinate of region in Java space * @param y coordinate of region in Java space * @param w width of region in Java space * @param h height of region in Java space * @return PDFOutline object created, for creating sub-outlines */ public PDFOutline add(String title,PDFPage dest, int x,int y,int w,int h) { int xy1[] = dest.cxy(x,y+h); int xy2[] = dest.cxy(x+w,y); PDFOutline outline = new PDFOutline(title,dest, xy1[0],xy1[1], xy2[0],xy2[1]); pdfDocument.add(outline); // add to the pdf first! add(outline); return outline; } /** * This adds an already existing outline to this one. * * <p>Note: the outline must have been added to the PDF document before * calling this method. Normally the other add methods are used. * * @param outline PDFOutline to add */ public void add(PDFOutline outline) { outlines.addElement(outline); // Tell the outline of ourselves outline.parent = this; } /** * @param os OutputStream to send the object to * @exception IOException on error */ public void write(OutputStream os) throws IOException { // Write the object header writeStart(os); // now the objects body // These are for kids only if(parent!=null) { os.write("/Title ".getBytes()); os.write(PDFStringHelper.makePDFString(title).getBytes()); os.write("\n/Dest [".getBytes()); os.write(dest.toString().getBytes()); if(destMode==FITPAGE) { //os.write(" null null null]\n/Parent ".getBytes()); os.write(" /Fit]\n/Parent ".getBytes()); } else { os.write(" /FitR ".getBytes()); os.write(Integer.toString(l).getBytes()); os.write(" ".getBytes()); os.write(Integer.toString(b).getBytes()); os.write(" ".getBytes()); os.write(Integer.toString(r).getBytes()); os.write(" ".getBytes()); os.write(Integer.toString(t).getBytes()); os.write("]\n/Parent ".getBytes()); } os.write(parent.toString().getBytes()); os.write("\n".getBytes()); } // the number of outlines in this document if(parent==null) { // were the top level node, so all are open by default os.write("/Count ".getBytes()); os.write(Integer.toString(outlines.size()).getBytes()); os.write("\n".getBytes()); } else { // were a decendent, so by default we are closed. Find out how many // entries are below us int c = descendants(); if(c>0) { os.write("/Count ".getBytes()); os.write(Integer.toString(-c).getBytes()); os.write("\n".getBytes()); } } // These only valid if we have children if(outlines.size()>0) { // the number of the first outline in list os.write("/First ".getBytes()); os.write(outlines.elementAt(0).toString().getBytes()); os.write("\n".getBytes()); // the number of the last outline in list os.write("/Last ".getBytes()); os.write(outlines.elementAt(outlines.size()-1).toString().getBytes()); os.write("\n".getBytes()); } if(parent!=null) { int index = parent.getIndex(this); if(index>0) { // Now if were not the first, then we have a /Prev node os.write("/Prev ".getBytes()); os.write(parent.getNode(index-1).toString().getBytes()); os.write("\n".getBytes()); } if(index<parent.getLast()) { // We have a /Next node os.write("/Next ".getBytes()); os.write(parent.getNode(index+1).toString().getBytes()); os.write("\n".getBytes()); } } // finish off with its footer writeEnd(os); } /** * This is called by children to find their position in this outlines * tree. * * @param outline PDFOutline to search for * @return index within Vector */ protected int getIndex(PDFOutline outline) { return outlines.indexOf(outline); } /** * Returns the last index in this outline * @return last index in outline */ protected int getLast() { return outlines.size()-1; } /** * Returns the outline at a specified position. * @param i index * @return the node at index i */ protected PDFOutline getNode(int i) { return (PDFOutline)(outlines.elementAt(i)); } /** * Returns all outlines directly below this one. * @return Enumeration of child elements */ public Enumeration<PDFOutline> elements() { return outlines.elements(); } /** * Returns the total number of descendants below this one. * @return the number of descendants below this one */ protected int descendants() { int c = outlines.size(); // initially the number of kids // now call each one for their descendants for(PDFOutline o : outlines) { c += o.descendants(); } return c; } } // end class PDFOutline