/* * $Id: PDFPage.java,v 1.5 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.awt.Dimension; import java.awt.Rectangle; import java.awt.print.PageFormat; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.util.Vector; /** * <p>This class defines a single page within a document. It is linked to a * single PDFGraphics object</p> * * @author Peter T Mount * @author Eric Z. Beard, ericzbeard@hotmail.com * @author Gilbert DeLeeuw, gil1@users.sourceforge.net * @version $Revision: 1.5 $, $Date: 2007/09/22 12:58:40 $ * * */ public class PDFPage 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. */ /** * Default page format (Letter size with 1 inch margins and * Portrait orientation) */ private static final PageFormat DEF_FORMAT = new PageFormat(); /** * This is this page format, ie the size of the page, margins, and rotation */ protected PageFormat pageFormat; /** * This is the pages object id that this page belongs to. * It is set by the pages object when it is added to it. */ protected PDFObject pdfPageList; /** * This holds the contents of the page. */ protected Vector<PDFObject> contents; /** * Object ID that contains a thumbnail sketch of the page. * -1 indicates no thumbnail. */ protected PDFObject thumbnail; /** * This holds any Annotations contained within this page. */ protected Vector<PDFObject> annotations; /** * This holds any resources for this page */ protected Vector<String> resources; // JM protected Vector<String> imageResources; /** * The fonts associated with this page */ protected Vector<PDFFont> fonts; /** * The xobjects or other images in the pdf */ // protected Vector xobjects; /** * These handle the procset for this page. * Refer to page 140 of the PDF Reference manual * NB: Text is handled when the fonts Vector is null, and a font is created * refer to getFont() to see where it's defined */ protected boolean hasImageB,hasImageC,hasImageI; protected procset procset; /** * This constructs a Page object, which will hold any contents for this * page. * * <p>Once created, it is added to the document via the PDF.add() method. * (For Advanced use, via the PDFPages.add() method). * * <p>This defaults to a4 media. */ public PDFPage() { super("/Page"); pageFormat = DEF_FORMAT; contents = new Vector<PDFObject>(); thumbnail = null; annotations = new Vector<PDFObject>(); resources = new Vector<String>(); // JM imageResources = new Vector<String>(); fonts = new Vector<PDFFont>(); procset = null; } /** * Constructs a page using A4 media, but using the supplied orientation. * @param orientation Orientation: 0, 90 or 270 * @see PageFormat#PORTRAIT * @see PageFormat#LANDSCAPE * @see PageFormat#REVERSE_LANDSCAPE */ public PDFPage(int orientation) { this(); setOrientation(orientation); } /** * Constructs a page using the supplied media size and orientation. * @param pageFormat PageFormat describing the page size */ public PDFPage(PageFormat pageFormat) { this(); this.pageFormat = pageFormat; } /** * Adds to procset. * @param proc the String to be added. */ public void addToProcset(String proc) { if (procset == null) { addProcset(); } procset.add(proc); } /** * This returns a PDFGraphics object, which can then be used to render * on to this page. If a previous PDFGraphics object was used, this object * is appended to the page, and will be drawn over the top of any previous * objects. * * @return a new PDFGraphics object to be used to draw this page. */ public PDFGraphics getGraphics() { try { PDFGraphics g = new PDFGraphics(); g.init(this); return g; } catch(Exception ex) { ex.printStackTrace(); } return null; } /** * Returns a PDFFont, creating it if not yet used. * @param type Font type, usually /Type1 * @param font Font name * @param style java.awt.Font style, ie Font.NORMAL * @return a PDFFont object. */ public PDFFont getFont(String type,String font,int style) { // Search the fonts on this page, and return one that matches this // font. // This keeps the number of font definitions down to one per font/style for(PDFFont ft : fonts) { if(ft.equals(type,font,style)) return ft; } // Ok, the font isn't in the page, so create one. // We need a procset if we are using fonts, so create it (if not // already created, and add to our resources if(fonts.size()==0) { addProcset(); procset.add("/Text"); } // finally create and return the font PDFFont f = pdfDocument.getFont(type,font,style); fonts.addElement(f); return f; } /** * Returns the page's PageFormat. * @return PageFormat describing the page size in device units (72dpi) */ public PageFormat getPageFormat() { return pageFormat; } /** * Gets the dimensions of the page. * @return a Dimension object containing the width and height of the page. */ public Dimension getDimension() { return new Dimension((int) pageFormat.getWidth(), (int) pageFormat .getHeight()); } /** * Gets the imageable area of the page. * @return a Rectangle containing the bounds of the imageable area. */ public Rectangle getImageableArea() { return new Rectangle((int) pageFormat.getImageableX(), (int) pageFormat .getImageableY(), (int) (pageFormat.getImageableX() + pageFormat .getImageableWidth()), (int) (pageFormat .getImageableY() + pageFormat.getImageableHeight())); } /** * Sets the page's orientation. * * <p>Normally, this should be done when the page is created, to avoid * problems. * * @param orientation a PageFormat orientation constant: * PageFormat.PORTRAIT, PageFormat.LANDSACPE or PageFromat.REVERSE_LANDSACPE */ public void setOrientation(int orientation) { pageFormat.setOrientation(orientation); } /** * Returns the pages orientation: * PageFormat.PORTRAIT, PageFormat.LANDSACPE or PageFromat.REVERSE_LANDSACPE * * @see java.awt.print.PageFormat * @return current orientation of the page */ public int getOrientation() { return pageFormat.getOrientation(); } /** * This adds an object that describes some content to this page. * * <p><b>Note:</b> Objects that describe contents must be added using this * method _AFTER_ the PDF.add() method has been called. * * @param ob PDFObject describing some contents */ public void add(PDFObject ob) { contents.addElement(ob); } /** * This adds an Annotation to the page. * * <p>As with other objects, the annotation must be added to the pdf * document using PDF.add() before adding to the page. * * @param ob Annotation to add. */ public void addAnnotation(PDFObject ob) { annotations.addElement(ob); } /** * This method adds a text note to the document. * @param note Text of the note * @param x Coordinate of note * @param y Coordinate of note * @param w Width of the note * @param h Height of the note * @return Returns the annotation, so other settings can be changed. */ public PDFAnnot addNote(String note,int x,int y,int w,int h) { int xy1[] = cxy(x,y+h); int xy2[] = cxy(x+w,y); PDFAnnot ob = new PDFAnnot(xy1[0],xy1[1], xy2[0],xy2[1], note); pdfDocument.add(ob); annotations.addElement(ob); return ob; } /** * Adds a hyperlink to the document. * @param x Coordinate of active area * @param y Coordinate of active area * @param w Width of the active area * @param h Height of the active area * @param dest Page that will be displayed when the link is activated. When * displayed, the zoom factor will be changed to fit the display. * @return Returns the annotation, so other settings can be changed. */ public PDFAnnot addLink(int x,int y,int w,int h,PDFObject dest) { int xy1[] = cxy(x,y+h); int xy2[] = cxy(x+w,y); PDFAnnot ob = new PDFAnnot(xy1[0],xy1[1], xy2[0],xy2[1], dest ); pdfDocument.add(ob); annotations.addElement(ob); return ob; } /** * Adds a hyperlink to the document. * @param x Coordinate of active area * @param y Coordinate of active area * @param w Width of the active area * @param h Height of the active area * @param dest Page that will be displayed when the link is activated * @param vx Coordinate of view area * @param vy Coordinate of view area * @param vw Width of the view area * @param vh Height of the view area * @return Returns the annotation, so other settings can be changed. */ public PDFAnnot addLink(int x,int y,int w,int h, PDFObject dest, int vx,int vy,int vw,int vh) { int xy1[] = cxy(x,y+h); int xy2[] = cxy(x+w,y); int xy3[] = cxy(vx,vy+vh); int xy4[] = cxy(vx+vw,vy); PDFAnnot ob = new PDFAnnot(xy1[0],xy1[1], xy2[0],xy2[1], dest, xy3[0],xy3[1], xy4[0],xy4[1] ); pdfDocument.add(ob); annotations.addElement(ob); return ob; } /** Contains the text strings for the xobjects. */ private Vector<String> xobjects = new Vector<String>(); /** * This adds an XObject resource to the page. * The string should be of the format * /Name ObjectNumber RevisionNumber R as in /Image1 13 0 R . * @param inxobject the XObject resource to be added. */ public void addXObject(String inxobject){ xobjects.addElement(inxobject); } /** * This adds a resource to the page. * @param resource String defining the resource */ public void addResource(String resource) { resources.addElement(resource); } // JM /** * This adds an image resource to the page. * @param resource the XObject resource to be added. */ public void addImageResource(String resource) { imageResources.addElement(resource); } /** * This adds an object that describes a thumbnail for this page. * <p><b>Note:</b> The object must already exist in the PDF, as only the * object ID is stored. * @param thumbnail PDFObject containing the thumbnail */ public void setThumbnail(PDFObject thumbnail) { this.thumbnail = thumbnail; } /** * This method attaches an outline to the current page being generated. When * selected, the outline displays the top of the page. * @param title Outline title to attach * @return PDFOutline object created, for addSubOutline if required. */ public PDFOutline addOutline(String title) { PDFOutline outline = new PDFOutline(title,this); pdfDocument.add(outline); pdfDocument.getOutline().add(outline); return outline; } /** * This method attaches an outline to the current page being generated. * When selected, the outline displays the top of the page. * * <p>Note: If the outline is not in the top level (ie below another * outline) then it must <b>not</b> be passed to this method. * * @param title Outline title to attach * @param x Left coordinate of region * @param y Bottom coordinate of region * @param w Width of region * @param h Height coordinate of region * @return PDFOutline object created, for addSubOutline if required. */ public PDFOutline addOutline(String title,int x,int y,int w,int h) { int xy1[] = cxy(x,y+h); int xy2[] = cxy(x+w,y); PDFOutline outline = new PDFOutline(title,this, xy1[0],xy1[1], xy2[0],xy2[1]); pdfDocument.add(outline); pdfDocument.getOutline().add(outline); return outline; } /** * @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 // the /Parent pages object os.write("/Parent ".getBytes()); os.write(pdfPageList.toString().getBytes()); os.write("\n".getBytes()); // the /MediaBox for the page size os.write("/MediaBox [".getBytes()); os.write(Integer.toString(0).getBytes()); os.write(" ".getBytes()); os.write(Integer.toString(0).getBytes()); os.write(" ".getBytes()); os.write(Integer.toString((int)pageFormat.getWidth()).getBytes()); os.write(" ".getBytes()); os.write(Integer.toString((int)pageFormat.getHeight()).getBytes()); os.write("]\n".getBytes()); // Rotation (if not zero) // if(rotate!=0) { // os.write("/Rotate ".getBytes()); // os.write(Integer.toString(rotate).getBytes()); // os.write("\n".getBytes()); // } // Now the resources os.write("/Resources << ".getBytes()); // fonts if(fonts.size()>0) { //os.write("/Font << ".getBytes()); os.write("\n/Font << ".getBytes()); for(PDFFont font : fonts) { os.write(font.getName().getBytes()); os.write(" ".getBytes()); os.write(font.toString().getBytes()); os.write(" ".getBytes()); } os.write(">> ".getBytes()); } // Now the XObjects if (xobjects.size() > 0){ os.write("\n/XObject << ".getBytes()); for(String str : xobjects) { os.write(str.getBytes()); os.write(" ".getBytes()); } os.write(">> ".getBytes()); } // Any other resources for(String str : resources) { os.write(str.getBytes()); os.write(" ".getBytes()); } // JM if(imageResources.size() > 0) { os.write("/XObject << ".getBytes()); for(String str : imageResources) { os.write(str.getBytes()); os.write(" ".getBytes()); } os.write(" >> ".getBytes()); } os.write(">>\n".getBytes()); // The thumbnail if(thumbnail!=null) { os.write("/Thumb ".getBytes()); os.write(thumbnail.toString().getBytes()); os.write("\n".getBytes()); } // the /Contents pages object if(contents.size()>0) { if(contents.size()==1) { PDFObject ob = (PDFObject)contents.elementAt(0); os.write("/Contents ".getBytes()); os.write(ob.toString().getBytes()); os.write("\n".getBytes()); } else { os.write("/Contents [".getBytes()); os.write(PDFObject.toArray(contents).getBytes()); os.write("\n".getBytes()); } } // The /Annots object if(annotations.size()>0) { os.write("/Annots ".getBytes()); os.write(PDFObject.toArray(annotations).getBytes()); os.write("\n".getBytes()); } // finish off with its footer writeEnd(os); } /** * This creates a procset and sets up the page to reference it */ private void addProcset() { if(procset==null) { pdfDocument.add(procset = new procset()); resources.addElement("/ProcSet "+procset); } } /** * This defines a procset */ public class procset extends PDFObject { private Vector<String> set; /** * Creates a new procset object. */ public procset() { super(null); set = new Vector<String>(); // Our default procset (use addElement not add, as we dont want a // leading space) set.addElement("/PDF"); } /** * @param proc Entry to add to the procset */ public void add(String proc) { set.addElement(" "+proc); } /** * @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); os.write(Integer.toString(objser).getBytes()); os.write(" 0 obj\n".getBytes()); // now the objects body os.write("[".getBytes()); for(String str : set) os.write(str.getBytes()); os.write("]\n".getBytes()); // finish off with its footer //writeEnd(os); os.write("endobj\n".getBytes()); } } /** * This utility method converts the y coordinate from Java to User space * within the page. * @param x Coordinate in Java space * @param y Coordinate in Java space * @return y Coordinate in User space */ public int cy(int x,int y) { return cxy(x,y)[1]; } /** * This utility method converts the y coordinate from Java to User space * within the page. * @param x Coordinate in Java space * @param y Coordinate in Java space * @return x Coordinate in User space */ public int cx(int x, int y) { return cxy(x,y)[0]; } /** * This utility method converts the Java coordinates to User space * within the page. * @param x Coordinate in Java space * @param y Coordinate in Java space * @return array containing the x & y Coordinate in User space */ public int[] cxy(int x,int y) { int r[] = new int[2]; r[0] = x; r[1] = (int) pageFormat.getHeight() - y; return r; } }