Java/2D Graphics GUI/PDF PostScript
Output to PDF file
<source lang="java"> /*
* Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 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.ru/ * @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.ru/ */
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.ru/ */
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(); }
}
</source>
Text to Postscript
<source lang="java"> /*
* Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 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.*; /** Text to PS */ public class PSFormatter {
/** The current input source */ protected BufferedReader br; /** The current page number */ protected int pageNum; /** The current X and Y on the page */ protected int curX, curY; /** The current line number on page */ protected int lineNum; /** The current tab setting */ protected int tabPos = 0; public static final int INCH = 72; // PS constant: 72 pts/inch // Page parameters /** The left margin indent */ protected int leftMargin = 50; /** The top of page indent */ protected int topMargin = 750; /** The bottom of page indent */ protected int botMargin = 50; // FORMATTING PARAMETERS protected int points = 12; protected int leading = 14; public static void main(String[] av) throws IOException { if (av.length == 0) new PSFormatter( new InputStreamReader(System.in)).process(); else for (int i = 0; i < av.length; i++) { new PSFormatter(av[i]).process(); } } public PSFormatter(String fileName) throws IOException { br = new BufferedReader(new FileReader(fileName)); } public PSFormatter(Reader in) throws IOException { if (in instanceof BufferedReader) br = (BufferedReader)in; else br = new BufferedReader(in); } /** Main processing of the current input source. */ protected void process() throws IOException { String line; prologue(); // emit PostScript prologue, once. startPage(); // emit top-of-page (ending previous) while ((line = br.readLine()) != null) { if (line.startsWith("\f") || line.trim().equals(".bp")) { startPage(); continue; } doLine(line); } // finish last page, if not already done. if (lineNum != 0) println("showpage"); } /** Handle start of page details. */ protected void startPage() { if (pageNum++ > 0) println("showpage"); lineNum = 0; moveTo(leftMargin, topMargin); } /** Process one line from the current input */ protected void doLine(String line) { tabPos = 0; // count leading (not imbedded) tabs. for (int i=0; i<line.length(); i++) { if (line.charAt(i)=="\t") tabPos++; else break; } String l = line.trim(); // removes spaces AND tabs if (l.length() == 0) { ++lineNum; return; } moveTo(leftMargin + (tabPos * INCH), topMargin-(lineNum++ * leading)); println("(" + toPSString(l)+ ") show"); // If we just hit the bottom, start a new page if (curY <= botMargin) startPage(); } /** Overly-simplistic conversion to PS, e.g., breaks on "foo\)bar" */ protected String toPSString(String o) { StringBuffer sb = new StringBuffer(); for (int i=0; i<o.length(); i++) { char c = o.charAt(i); switch(c) { case "(": sb.append("\\("); break; case ")": sb.append("\\)"); break; default: sb.append(c); break; } } return sb.toString(); } protected void println(String s) { System.out.println(s); } protected void moveTo(int x, int y) { curX = x; curY = y; println(x + " " + y + " " + "moveto"); } void prologue() { println("%!PS-Adobe"); println("/Courier findfont " + points + " scalefont setfont "); }
}
</source>