/* * $Id: PDFDocument.java,v 1.4 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.FontFormatException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; /** *

This class is the base of the PDF generator. A PDFDocument class is * created for a document, and each page, object, annotation, * etc is added to the document. * Once complete, the document can be written to an OutputStream, and the PDF * document's internal structures are kept in sync.

* *

Note that most programmers using this package will NEVER access * one of these objects directly. Most everything can be done using * PDFJob and PDFGraphics, so you don't need * to directly instantiate a PDFDocument

* *

ezb - 20011115 - Wondering if the constructors should even be public. * When would someone want to make one of these and manipulate it outside * the context of a job and graphics object?

* * @author Peter T Mount, http://www.retep.org.uk/pdf/ * @author Eric Z. Beard, ericzbeard@hotmail.com * @author Gilbert DeLeeuw, gil1@users.sourceforge.net * @version $Revision: 1.4 $, $Date: 2007/09/22 12:58:40 $ */ public class PDFDocument implements Serializable { /* * NOTE: This class originated in uk.org.retep.pdf, by Peter T. Mount, and it * has been modified by Eric Z. Beard, ericzbeard@hotmail.com. The * package name was changed to gnu.jpdf and several inner classes were * moved out into their own files. */ /** * This is used to allocate objects a unique serial number in the document. */ protected int objser; /** * This vector contains each indirect object within the document. */ protected Vector objects; /** * This is the Catalog object, which is required by each PDF Document */ private PDFCatalog catalog; /** * This is the info object. Although this is an optional object, we * include it. */ private PDFInfo info; /** * This is the Pages object, which is required by each PDF Document */ private PDFPageList pdfPageList; /** * This is the Outline object, which is optional */ private PDFOutline outline; /** * This holds a PDFObject describing the default border for annotations. * It's only used when the document is being written. */ protected PDFObject defaultOutlineBorder; /** *

This page mode indicates that the document * should be opened just with the page visible. This is the default

*/ public static final int USENONE = 0; /** *

This page mode indicates that the Outlines * should also be displayed when the document is opened.

*/ public static final int USEOUTLINES = 1; /** *

This page mode indicates that the Thumbnails should be visible when the * document first opens.

*/ public static final int USETHUMBS = 2; /** *

* This page mode indicates that when the document is opened, it is displayed * in full-screen-mode. There is no menu bar, window controls nor any other * window present.

*/ public static final int FULLSCREEN = 3; /** *

* These map the page modes just defined to the pagemodes setting of PDF. *

*/ public static final String PDF_PAGE_MODES[] = { "/UseNone", "/UseOutlines", "/UseThumbs", "/FullScreen" }; /** * This is used to provide a unique name for a font */ private int fontid = 0; /** *

This is used to provide a unique name for an image

*/ private int imageid = 0; /** * This holds the current fonts */ private Vector fonts; /** *

This creates a PDF document with the default pagemode

*/ public PDFDocument() { this(USENONE); } /** *

This creates a PDF document

* @param pagemode an int, determines how the document will present itself to * the viewer when it first opens. */ public PDFDocument(int pagemode) { objser = 1; objects = new Vector(); fonts = new Vector(); // Now create some standard objects add(pdfPageList = new PDFPageList()); add(catalog = new PDFCatalog(pdfPageList,pagemode)); add(info = new PDFInfo()); // Acroread on linux seems to die if there is no root outline add(getOutline()); } /** * This adds a top level object to the document. * *

Once added, it is allocated a unique serial number. * *

Note: Not all object are added directly using this method. * Some objects which have Kids (in PDF sub-objects or children are * called Kids) will have their own add() method, which will call this * one internally. * * @param obj The PDFObject to add to the document * @return the unique serial number for this object. */ public synchronized int add(PDFObject obj) { objects.addElement(obj); obj.objser=objser++; // create a new serial number obj.pdfDocument = this; // so they can find the document they belong to // If its a page, then add it to the pages collection if(obj instanceof PDFPage) pdfPageList.add((PDFPage)obj); return obj.objser; } /** *

This returns a specific page. It's used mainly when using a * Serialized template file.

* * ?? How does a serialized template file work ??? * * @param page page number to return * @return PDFPage at that position */ public PDFPage getPage(int page) { return pdfPageList.getPage(page); } /** * @return the root outline */ public PDFOutline getOutline() { if(outline==null) { outline = new PDFOutline(); catalog.setOutline(outline); } return outline; } /** * This returns a font of the specified type and font. If the font has * not been defined, it creates a new font in the PDF document, and * returns it. * * @param type PDF Font Type - usually "/Type1" * @param font Java font name * @param style java.awt.Font style (NORMAL, BOLD etc) * @return PDFFont defining this font */ public PDFFont getFont(String type,String font,int style) { for(PDFFont ft : fonts) { if(ft.equals(type,font,style)) return ft; } // the font wasn't found, so create it fontid++; PDFFont ft = new PDFFont("/F"+fontid,type,font,style); add(ft); fonts.addElement(ft); return ft; } public PDFFont getEmbeddedFont(String font, int style, File file) throws FileNotFoundException, IOException { for (PDFFont ft : fonts) { if (ft.equals("/TrueType", font, style)) { return ft; } } PDFStream fontFile2 = new PDFStream() { @Override public void write(OutputStream os) throws IOException { writeStart(os); os.write("/Length1 ".getBytes()); os.write(Integer.toString(buf.size()).getBytes()); os.write("\n".getBytes()); writeStream(os); } }; fontFile2.setDeflate(true); OutputStream ff2Os = fontFile2.getOutputStream(); InputStream is = new FileInputStream(file); byte buf[] = new byte[1024]; int cnt = 0; while ((cnt = is.read(buf)) > 0) { ff2Os.write(buf, 0, cnt); } is.close(); //ff2Os.write("AHOJ".getBytes()); add(fontFile2); // the font wasn't found, so create it fontid++; TtfParser par = new TtfParser(); try { par.loadFromTTF(file, 1024); } catch (FontFormatException ex) { Logger.getLogger(PDFDocument.class.getName()).log(Level.SEVERE, null, ex); } PDFFontDescriptor fontDescriptor = new PDFFontDescriptor(font, par.getAscent(), par.getDescent(), par.getCapHeight(), 80/*?*/, "" + fontFile2.getSerialID() + " 0 R"); add(fontDescriptor); final List widthsList = new ArrayList<>(); List glyphAdvances = par.getAdvanceWidths(); Map cmap = par.getCmap(); for (int i = 32; i <= 255; i++) { if (cmap.containsKey(i)) { widthsList.add(glyphAdvances.get(cmap.get(i))); } else { widthsList.add(0); } } PDFObject widthStream = new PDFObject(null) { @Override public void write(OutputStream os) throws IOException { os.write(Integer.toString(objser).getBytes()); os.write(" 0 obj\n".getBytes()); os.write("[ ".getBytes()); for (int i = 0; i < widthsList.size(); i++) { if (i > 0) { os.write(" ".getBytes()); } os.write(("" + widthsList.get(i)).getBytes()); } os.write(" ]\n".getBytes()); os.write("endobj\n".getBytes()); } }; add(widthStream); PDFFont ft = new PDFEmbeddedFont("/F" + fontid, font, style, "" + fontDescriptor.getSerialID() + " 0 R", widthStream.getSerialID() + " 0 R", 32, 255); add(ft); fonts.addElement(ft); return ft; } /** * Sets a unique name to a PDFImage * @param img PDFImage to set the name of * @return the name given to the image */ public String setImageName(PDFImage img) { imageid++; img.setName("/Image"+imageid); return img.getName(); } /** *

Set the PDFInfo object, which contains author, title, * keywords, etc

* @param info a PDFInof object */ public void setPDFInfo(PDFInfo info) { this.info = info; } /** *

Get the PDFInfo object, which contains author, title, keywords, * etc

* @return the PDFInfo object for this document. */ public PDFInfo getPDFInfo() { return this.info; } /** * This writes the document to an OutputStream. * *

Note: You can call this as many times as you wish, as long as * the calls are not running at the same time. * *

Also, objects can be added or amended between these calls. * *

Also, the OutputStream is not closed, but will be flushed on * completion. It is up to the caller to close the stream. * * @param os OutputStream to write the document to * @exception IOException on error */ public void write(OutputStream os) throws IOException { PDFOutput pos = new PDFOutput(os); // Write each object to the OutputStream. We call via the output // as that builds the xref table for(PDFObject o : objects) { pos.write(o); } // Finally close the output, which writes the xref table. pos.close(); // and flush the output stream to ensure everything is written. os.flush(); } } // end class PDFDocument