code: /*
   * $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 pdf;
  
  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;
      }
  
  }
  