From the time that comp.lang.java.* was just comp.lang.java, the question, "How do I print in Java?", has been a common one. In this article, I describe several approaches to printing in both JDK 1.0.2 and JDK 1.1. Printing with JDK 1.0.2 Printer support was not included with JDK 1.0.2. However, the need for printing was still there, so several workarounds were developed in the Java community to allow printing even in the absence of direct support. Two of the most common are described below. 1) Printing from applets in JDK 1.0.2 -- the CGI approach This approach is most useful if you have an applet that collects or generates tabular data, and you'd like to produce a hard-copy suitable for printing. The general approach is to send the data from your applet back to a CGI script residing on the server, which in turn generates an HTML page containing your data. 1) Open a connection back to the server from which the applet was downloaded. The connection can either be a straight socket connection to the web server's port (usually port 80), or a URL connection. 2) Send the applet's data to a CGI script listening on the server. 3) The CGI script generates an HTML page containing the applet's data. 4) The user then prints out the HTML page. An example of an applet that uses this approach can be found at: http://www.vis.colostate.edu/~marciot/java/timesheet/ This applet generates a PostScript page rather than an HTML page, but the general approach used is the same. 2) Printing from applications in JDK 1.0.2. -- the printer-as-file approach On most operating systems, the printer can be treated as if it were a normal file device. To use this approach, you do the following: 1) Determine the name of the printer. On Windows 95 systems, the default printer is usually named "LPT1". On Unix systems, it'll generally be called /dev/lp. To determine the name of the printer, it will likely be necessary to prompt the user for the name of the printer, either during the install process or at the time of the print request. 2) Open an output stream to the print device. 3) Print your data to the output stream. Here's a sample class that does this: //class that opens the printer as a file and writes "Hello World" to it import java.io.*; public class lpt { public static void main (String[] argv) { //check for argument if (argv.length != 1) { System.out.println("usage: java lpt1 "); System.exit(0); } try { //open printer as if it were a file FileOutputStream os = new FileOutputStream(argv[0]); //wrap stream in "friendly" PrintStream PrintStream ps = new PrintStream(os); //print text here ps.println("Hello world!"); //form feed -- this is important //Without the form feed, the text //will simply sit in the print //buffer until something else //gets printed. ps.print("\f"); //flush buffer and close ps.close(); } catch (Exception e) { System.out.println("Exception occurred: " + e); } } } This approach works best for text data. If you're feeling really ambitious and have a good knowledge of PostScript, you could potentially send PostScript commands to the printer, though. Other approaches to printing in JDK 1.0.2 You can also print in JDK 1.0.2 by making use of the Jlpr classes. Jlpr was developed by Ernst Friedman-Hill of Sandia National Laboratories, and allows PostScript printing of applets, using the CGI approach described above. For further information on Jlpr, see: http://herzberg.ca.sandia.gov/jlpr/ Printing in JDK 1.1 JDK 1.1 includes print support. Here's a simple method that prints whatever frame it happens to be a part of. public void printSelf() { //This assumes an applet environment; //for applications, you'd use //Toolkit.getDefaultToolkit().getPrintJob() PrintJob pjob = getToolkit().getPrintJob(this, "Printing Test", (Properties)null); if (pjob != null) { //get a graphics object Graphics pg = pjob.getGraphics(); if (pg != null) { //draw this frame and all contained objects //to the graphics context printAll(pg); //finish the page pg.dispose(); } //finish the print job pjob.end(); } } The call Toolkit.getPrintJob() takes three parameters: 1) a reference to the frame being printed (this _must_ inherit from the Frame class). 2) the name of the print job ("Printing Test" in this case), 3) a reference to an object of class Properties (this parameter is currently undocumented, and is ignored on the Windows 95 version of JDK 1.1.x). This call will return null if unsuccessful (or if the user canceled the print job when presented with the standard print dialog); if successful, it returns an object of type PrintJob. You can use the getGraphics() method of this object to return a Graphics object, which will allow you to draw to the printer. You can use the Component.print() or Component.printAll() methods to print a given component (print()) or a given component and all the components it contains (printAll()). The dispose() method of the Graphics object gets rid of the Graphics object; in this context, it also signifies the end of the page -- if you intend to print several pages, you must create and dispose of a new Graphics object for each page. The end() method of class PrintJob signifies that the print job has finished. This method of printing is also possible within Applets, however, the applet must be authorized by the browser's security manager to access the printer. In itself, this is not much of a headache; the problem is that Netscape Navigator, Microsoft Internet Explorer, and Sun's HotJava all take different approaches to security. 1) The HotJava approach HotJava uses signed JAR files for authentication. To sign a JAR file, you first must either possess a certificate from a certificate authority such as VeriSign, or generate one using the javakey utility supplied with JDK 1.1. To create a certificate, take the following steps: 1) Create a new signer in your security database javakey cs YourNameHere 2) Make the signer trusted javakey -t YourNameHere true 3) Generate a key for the signer javakey -gk YourNameHere DSA 1024 4) Export the public key to a file. In this example, the file is named YOURSIG. javakey -ek YourNameHere YOURSIG 5) Create a file with all required security info. In this example, the file is called "sigdirect". echo issuer.name=YourNameHere>sigdirect echo subject.name=YourNameHere>>sigdirect echo subject.real.name=YourRealNameHere>>sigdirect echo subject.country=USA>>sigdirect echo subject.org=YourOrganizationHere>>sigdirect echo subject.org.unit=YourUnit>>sigdirect echo start.date=8/11/97>>sigdirect echo end.date=8/30/97>>sigdirect echo serial.number=111111111>>sigdirect Obviously, you should fill in the values appropriate to yourself and your organization. 6) Generate certificate from file "sigdirect" javakey -gc c:\tom\signed\sigdirect Once you have obtained or generated a certificate, you can then use the certificate to sign your JAR file. 1) Create a JAR file that contains all your applet code and .class files jar cf hello.jar HelloWorld.class 2) Create file used to generate signature echo signer=YourNameHere> sig echo cert=1>> sig echo chain=0>> sig echo signature.file=YOURSIG>>sig 3) Use the javakey utility to sign the JAR file. javakey -gs sig hello.jar This will generate a file called hello.jar.sig. You can then reference this in the ARCHIVE tag within the tag. For example: You are now the proud parent of a signed applet. Users of this applet can then configure their browsers to grant (or not grant, as they wish) your applet the permission to print. Note: for "real-world" usage, it would be best to obtain a certificate from a recognized certificate authority, such as VeriSign. 2) The Netscape Navigator approach Like HotJava, Netscape uses signed JAR files. However, Netscape uses its own JAR signing facility, zigbert. You can download zigbert from: http://developer.netscape.com/software/signedobj/jarpack.html Netscape requires that the certificate be issued by a valid Certificate Authority. In addition to signing your code, Netscape requires you to make use of their Capabilities classes in order to perform "outside-the-sandbox" operations such as printing. This is done by making use of the class netscape.security.PrivilegeManager. To enable printing, you'd say PrivilegeManager.enablePrivilege("UniversalPrintJobAccess"); This method throws a netscape.security.ForbiddenTargetException if the privilege is not allowed for this applet. Javadoc-format documentation for this class is available at: http://developer.netscape.com/library/documentation/signedobj/javadoc/Package-netscape_security.html So, for Netscape's approach, the steps to take are: 1) Obtain a certificate from a Certificate Authority. 2) Obtain Netscape's Capabilities API classes and place them in your compiler's classpath. (You can download a .zip file containing these classes from http://developer.netscape.com/library/documentation/signedobj/capsapi.html 3) Write your functions so that they call PrivilegeManager.enablePrivilege() with the appropriate argument before attempting to print, and write appropriate exception handlers in case your request is rejected. 4) Package the code with Netscape's "zigbert" JAR file packager, including the certificate. You can now use your signed JAR file as you would any other JAR file. Do not use the .sig file suffix, however, as Netscape does not recognize it. 3) The Microsoft Internet Explorer approach Microsoft's approach is to use a signed .CAB file. A .CAB file is Microsoft's answer to a JAR file; it's a compressed archive of code and other files. CAB files are only recognized by Internet Explorer. To use this approach, you must have Microsoft's Java SDK 2.0 Beta 2 or better, or the Internet Explorer 4.0 Internet Client SDK Preview 2 or better. For Microsoft's approach, the steps are: 1) Create a .CAB file containing your code, using the "cabarc" utility. 2) Use the "makecert" program to create a certificate. 3) Use the "cert2spc" program to turn your certificate into a Software Publisher Certificate(SPC). Note: steps 2 and 3 are required only if you have not yet obtained a valid SPC from a Certificate Authority (CA). 4) Create an .ini file to be used by signcode (Microsoft's code signing utility). For an example .ini file, see: http://www.microsoft.com/java/sdk/20/tools/sampleini.htm 5) Run signcode to sign your .CAB file. 6) Use the "chkjava" program to verify that you've set the appropriate permissions. To tell Internet Explorer to use a CAB file, say where "yourfile.cab" is the name of your file. For an overview of Microsoft's approach to code signing, see: http://www.microsoft.com/java/security/secfaq.htm For a more detailed discussion of Microsoft's approach to code signing, see: http://www.microsoft.com/java/sdk/20/tools/signcode.htm This page contains some useful links to more detailed information. Difficulties with printing Once you manage to get printing working, you're still not out of the woods yet. You may want to scan the bug database at http://developer.javasoft.com/developer/bugParade/ for bugs related to printing. Some of the more interesting bugs still in effect at the time of this writing include: Bug ID number 4084038 -- Objects printed under Windows 95 appear smaller than they should. Bug ID number 4036068 -- The "properties" parameter of Toolkit.getPrintJob() is non-functioning and undocumented. Other sources of info. Bug ID number 4081141 -- Attempting to use the drawString() method on a Graphics object returned by PrintJob.getGraphics() fails. Other sources of information: Bill Bilow has some examples of printing from both applets and applications in his "Java Table" at: http://www.geocities.com/SiliconValley/Park/9841/ Brian Modra has made it his quest to find out everything there is to know about printing from an applet. You can peruse the results of his research at: http://www.adelaide.net.au/~bmodra/PrintJob.html Conclusion Printing from Java is still not "all there", but it is possible (though a bit painful at times) to get printed output from an applet or application using the various strategies described above.