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