Sunday, November 25, 2012

Printing files directly from web page

Introduction

We want to print "remote" documents directly to our local default printer without having to download the documents manually and without showing a print dialogue box.
In our case the application is integrated with Google Apps, our documents are stored in Google Drive. But the approach below works for all documents that can be accessed via the URL class in Java. In the web application a list of documents is display as shown below:
By clicking on the print icon behind one of the documents the document is printer directly to the default printer of the user without a dialogue box.
A much used technique is using the window.print() method with JavaScript but this approach also prints the web page details in the header and footer. In our case we want to print the document as it is. For this we use a good old Applet, this way we can use JavaScript to trigger the printing by the Applet. The applet accesses the default printer and streams the remote document to it. As we use Google Drive we can choose any format of document to be exported as PDF, this PDF is streamed to the printer.

Print Applet

The actual code that does the printing is pretty straight forward:

PrintService service = PrintServiceLookup.lookupDefaultPrintService();
if (service != null) {
    DocFlavor psFormat = DocFlavor.INPUT_STREAM.PDF;
    PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();    
    DocPrintJob job = service.createPrintJob();
    Doc pdfDoc = new SimpleDoc(new URL(fileUrl).openStream(),psFormat, null);
    job.print(pdfDoc, attributes);         
}
The default printer is fetched by calling PrintServiceLookup.lookupDefaultPrintService(). Since we convert all our documents to PDF before printing we use the DocFlavor.INPUT_STREAM.PDF. Next a print job is created and using the remote document as an input stream.

Integrate Print Applet in web page

The next step is to publish the applet inside the web application, the jar file containing the PrintApplet class is located in a folder /applet in the root of the web application. To include the applet we add the following html code: where:

  • id: id of the applet to be used in JavaScript
  • code: className of applet
  • width / height: dimension of applet, in Chrome the applet will not work if width/height =0
  • codebase: the location of the jar file containing the applet class, this is relative to the page the applet tag is embedded in
  • archive: name of jar containing the applet

Calling the applet from JavaScript

Due to java security settings for applets a dialogue box will pop-up every time you want to print:

To prevent this we have to change two things:
  • sign the jar containing the PrintApplet class
  • change permissions/privileges of the PrintApplet

Sign the jar containing the applet

To sign the jar we first need a certifcate, this can be created using the keytool. First we create a public/private key:
Next we generate a self signed certificate:
Now we have a keystore 'delinet' containg the certificate, we will use this keystore to sign the jar file with maven:
Next we generate a self signed certificate:
After redeploying the application and accessing the page with the applet we get the following warning:
Check both boxes. Calling the JavaScript to print a document still generates a pop up. For this to go away we have to change the permissions or priveleges of the applet.

Change the permissions or priveleges of the applet

There are several options to prevent this:
  • change java.policy file in JRE
  • change .java.policy file in users home directory
  • modify the applet to use AccessControler
For the first two options add the following extract: Since the first two options involve changes on the client side we prefer the third option, wrapping the printing inside the AccessController.doPrivileged method:
After redeploying the application, restarting the web browser, accessing the page again and clicking on the print button the document is printed to the printer without any pop ups.

2 comments: