Mega Code Archive

 
Categories / Java / 2D Graphics GUI
 

Output to PDF file

/*  * Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 1996-2002.  * All rights reserved. Software written by Ian F. Darwin and others.  * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $  *  * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions  * are met:  * 1. Redistributions of source code must retain the above copyright  *    notice, this list of conditions and the following disclaimer.  * 2. Redistributions in binary form must reproduce the above copyright  *    notice, this list of conditions and the following disclaimer in the  *    documentation and/or other materials provided with the distribution.  *  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  *   * Java, the Duke mascot, and all variants of Sun's Java "steaming coffee  * cup" logo are trademarks of Sun Microsystems. Sun's, and James Gosling's,  * pioneering role in inventing and promulgating (and standardizing) the Java   * language and environment is gratefully acknowledged.  *   * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for  * inventing predecessor languages C and C++ is also gratefully acknowledged.  */ import java.io.*; import java.text.*; import java.util.*; /** A simple text test of SPDF package  */ public class PDFDemo {   public static void main(String[] argv) throws IOException {     PrintWriter pout;     if (argv.length == 0) {       pout = new PrintWriter(System.out);     } else {       if (new File(argv[0]).exists()) {         throw new IOException(         "Output file " + argv[0] + " already exists");       }       pout = new PrintWriter(new FileWriter(argv[0]));     }     PDF p = new PDF(pout);     Page p1 = new Page(p);     p1.add(new MoveTo(p, 100, 600));     p1.add(new Text(p, "Hello world, live on the web."));     p1.add(new Text(p, "Hello world, line 2 on the web."));     p.add(p1);     p.setAuthor("Ian F. Darwin");     p.writePDF();   } } /** The main class for the Darwin Open Systems  * {Simple,Stupid,Simplistic} PDF API.  * PDF is Adobe's Portable Document Format, and is probably a trademark  * of Adobe Systems Inc, Mountain View, California.  * The Adobe PDF Specification which they publish grants everyone  * permission to write code to generate and/or process PDF files.  * A PDF Object represents one PDF file.  * @author Ian F. Darwin, http://www.darwinsys.com/  * @version $Id: PDF.java,v 1.6 2004/02/09 03:34:02 ian Exp $  */ class PDF {   /** The output writer */   protected PrintWriter out;   /** The list of pages */   protected ArrayList pages;   /** The list of object xrefs */   protected ArrayList xrefs;   /** The root object */   PDFObject rootObj = new RootObject(this);   /** The Info object */   InfoObject infoObj = new InfoObject(this);   /** The outlines (outline font) object */   OutlinesObject outlinesObj = new OutlinesObject(this);   /** The Pages object */   PagesObject pagesObj = new PagesObject(this);   /** The Font Dictionary */   FontDict fontDict = new FontDict(this);   /** The object number of the current object */   protected int currObj = 1;   /** A flag to avoid writing twice */   protected boolean startedWriting = false;   /** A magic number that identifies the output as a PDF file */   protected final static String PDF_MAGIC = "%PDF-1.0";   /** Constructor */   public PDF(PrintWriter o) {     out = o;     pages = new ArrayList();     xrefs = new ArrayList();   }   public void add(Page p) {     pages.add(p);   }   public void insertPage(int where, Page p) {     pages.add(where, p);   }   // OUTPUT METHODS -- we provide our own print/println, so we   // can keep track of file offsets (it was either that, or kludgily   // use a RandomAccessFile and the getFilePointer() method).   long offset = 0;   /** Print a String */   protected void print(String s){     out.print(s);     offset += s.length();   }   /** Println a String */   protected void println(String s) {     print(s);     print("\n");   }   /** Print an Object */   protected void print(Object o) {     print(o.toString());   }   /** Println an Object */   protected void println(Object o) {     println(o.toString());   }   /** Print an int */   protected void print(int i) {     String s = Integer.toString(i);     print(s);   }   /** Println an int */   protected void println(int i) {     String s = Integer.toString(i);     print(s);   }   /** Println with no args */   protected void println() {     print("\n");   }   // END OF OUTPUT METHODS    /** Add an entry into the offset table */   protected void addXref() {     xrefs.add(new Long(offset));   }   /** Write the entire output */   public void writePDF() {     if (startedWriting) {       throw new IllegalStateException(         "writePDF() can only be called once.");     }     startedWriting = true;     writePDFHeader();     writePDFbody();     writeXrefs();     writePDFTrailer();     out.flush();     out.close();   }   protected void writePDFHeader() {     println(PDF_MAGIC);     rootObj.print();  // 1     infoObj.print();  // 2     outlinesObj.print(); // 3     pagesObj.print();  // 4   }   protected void writePDFbody() {     for (int i=0; i<pages.size(); i++) {        ((Page)pages.get(i)).print();    // 5, 6     }     addXref();     print(currObj++); println(" 0 obj");     println("[/PDF /Text]");     println("endobj");     fontDict.print();    // 8   }   DecimalFormat nf10 = new DecimalFormat("0000000000");   DecimalFormat nf5 = new DecimalFormat("00000");   /** Write one Xref, in the format 0000000000 65535 f */   protected void printXref(long n, int where, char inUse) {     println(nf10.format(n) + " " +  nf5.format(where) + " " + inUse);   }   long xrefStart;   /** Write all the xrefs, using the format above */   protected void writeXrefs() {     xrefStart = offset;     println("xref");     print(0);     print(" ");     print(xrefs.size() + 1);     println();     // "fake" entry at 0, always 0, -1, f(free).     printXref(0, 65535, 'f');     // Remaining xref entries are for real objects.     for (int i=0; i<xrefs.size(); i++) {       Long lo = (Long)xrefs.get(i);       long l = lo.longValue();       printXref(l, 0, 'n');     }   }   protected void writePDFTrailer() {     println("trailer");     println("<<");     println("/Size " + (xrefs.size() + 1));     println("/Root 1 0 R");     println("/Info 2 0 R");     println(">>");     println("% startxref");     println("% " + xrefStart);     println("%%EOF");   }   class RootObject extends PDFDict {     protected RootObject(PDF m) {       super(m);       dict.put("Type", "/Catalog");       dict.put("Outlines", "3 0 R");       dict.put("Pages", "4 0 R");     }   }   class InfoObject extends PDFDict {     protected InfoObject(PDF m) {       super(m);       dict.put("Title", "(Sample PDF by SPDF)");       dict.put("Creator", "(Darwin Open Systems SPDF Software)");       dict.put("Created", "(D:20000516010203)");     }   }      public void setAuthor(String au) {     infoObj.dict.put("Author", "(" + au + ")");   }   class OutlinesObject extends PDFDict {     protected OutlinesObject(PDF m) {       super(m);       dict.put("Type", "/Outlines");       dict.put("Count", "0");     }   }   class PagesObject extends PDFDict {     protected PagesObject(PDF m) {       super(m);       dict.put("Type", "/Pages");       dict.put("Count", "1");       dict.put("Kids", "[5 0 R]");     }   }   class FontDict extends PDFDict {     protected FontDict(PDF m) {       super(m);       dict.put("Type", "/Font");       dict.put("Subtype", "/Type1");       dict.put("Name", "/F1");       dict.put("BaseFont", "/Helvetica");       dict.put("Encoding", "/MacRomanEncoding");     }   } } /** A PDFDict ias a PDFObject that is all, or mostly, a Dictionary.  * @author Ian Darwin, http://www.darwinsys.com/  */ abstract class PDFDict extends PDFObject {   /** The Dictionary is a HashTable. Put the keys without a     * leading slash, since they always have it. Values can    * be /names, (strings), or whatever.    */   protected Hashtable dict;   PDFDict(PDF m) {     super(m);     dict = new Hashtable();   }   /** Write the object to the Output Writer. The default implementation    * of this method in PDFDict just calls startObj, printDict, and endObj.    */   protected void print() {     startObj();     printDict();     endObj();   }   protected void startObj() {     // Record the starting position of this Obj in the xref table     master.addXref();     // Print out e.g., "42 0 obj"     master.print(master.currObj++);      master.print(" 0 obj");     master.println();   }   protected void endObj() {     master.println("endobj");   }   protected void printDict() {     master.println("<<");     Enumeration e = dict.keys();     while (e.hasMoreElements()) {       master.print("\t/");       String key = (String)e.nextElement();       master.print(key);       master.print(" ");       master.print(dict.get(key));       master.println();     }     master.println(">>");   } } /** Represent one Text object in a PDF file. */ class Text extends PDFObject {   protected int x, y;   protected String text;   public Text(PDF m, String s) {     super(m);     text = s;   }   public void print() {     throw new IllegalStateException("print() called on a Text obj");   }   public void print(StringBuffer sb) {     sb.append("0 -18 Td (");     sb.append(text);  // TODO must substitute escaped characters     sb.append(") Tj\n");   } } /** A PDFObject represents one node in the tree of a PDF file.  * @author Ian Darwin, http://www.darwinsys.com/  */ abstract class PDFObject extends java.lang.Object {   /** The containing PDF file */   protected PDF master;   PDFObject(PDF m) {     master = m;   }   /** Write the object to the Output Writer */   protected abstract void print();   protected void startObj() {     // Record the starting position of this Obj in the xref table     master.addXref();     // Print out e.g., "42 0 obj"     master.print(master.currObj++);      master.print(" 0 obj");     master.println();   }   protected void endObj() {     master.println("endobj");   } } /** Represent one Move object ("moveto") in a PDF file. */ class MoveTo extends PDFObject {   protected int x, y;   public MoveTo(PDF m, int x, int y) {     super(m);     this.x = x;     this.y = y;   }   public void print() {     throw new IllegalStateException("print() called on a Text obj");   }   public void print(StringBuffer sb) {     sb.append(x);     sb.append(' ');     sb.append(y);     sb.append(" Td\n");   } } /** Represent one Page of a PDF file. */ class Page extends PDFDict {   protected ArrayList objects = new ArrayList();   public Page(PDF m) {     super(m);     dict.put("Type", "/Page");     dict.put("Parent", "4 0 R");     dict.put("Resources", "<< /Font << /F1 8 0 R >> /ProcSet 7 0 R >>");     dict.put("MediaBox", "[0 0 612 792]");     dict.put("Contents", "6 0 R");        }   public void add(PDFObject po) {     objects.add(po);   }   /** Print all the objects on the page.    * For now, just print all the Text objects, as one Stream.    */   protected void print() {     // Print the Page object     super.print();     // Now do the Text objects as one PDF obj     master.addXref();     startObj();     StringBuffer sb = new StringBuffer();     sb.append("BT\n");     sb.append("/F1 12 Tf\n");     for (int i=0; i<objects.size(); i++) {       PDFObject po = (PDFObject)objects.get(i);       if (po instanceof Text)         ((Text)po).print(sb);       else if (po instanceof MoveTo)         ((MoveTo)po).print(sb);       // else if (po instanceof Font)       //  ...       else         System.err.println("PDFPage: ignoring " + po);     }     sb.append("ET\n");     master.println("<< /Length " + sb.length() + " >>");     master.println("stream");     master.print(sb);     master.println("endstream");     endObj();   } }