ADF 11g : Printing Directly From Your Application

Last week I was asked this question : “Can we print directly from within our ADF Application, without invoking the printer dialog ?” I wasn’t sure but after some investigation the answer was clear. Yes you can ! Here is how
I decided to create a print start up form where I can select printers and print the document. Most of the functionality needed is provided by the Java Print Service API.

Selecting available printers

I start with a way to show all printers available to the session. For that I simply use the PrinterServiceLookup.

PrintService[] printers = PrintServiceLookup.lookupPrintServices(null, null);

The result I can now use to create an Array of SelectItems in order to make the list available in the application.

public SelectItem[] getAllPrinters() {
   if (allPrinters == null) {
       PrintService[] printers =  PrintServiceLookup.lookupPrintServices(null, null);
       allPrinters = new SelectItem[printers.length];
       for (int i = 0; i < printers.length; i++) {
                 SelectItem printer =
                 new SelectItem(printers[i].getName(), printers[i].getName());
                 allPrinters[i] = printer;
       }
    }
 return allPrinters;
}

On the pagefragment I use a selectOneChoice component getting the values from the list created above. The code for the pagefragment is like this:

<af:selectOneChoice label="Available Printers" partialTriggers="cb1"
                                 value="#{pageFlowScope.applicationPrinterBean.selectedPrinter}"
                                 id="soc1"
                                autoSubmit="true">
                                <f:selectItems value="#{pageFlowScope.applicationPrinterBean.allPrinters}" id="si1"/>
</af:selectOneChoice>

When I run the application I see a list of all printers defined om my local machine and I can select any printer I like …

printerList

What is the default printer ?

Usually every there is a default printer defined. That is the one I want to use by default in this application. For that to work I need to get hold of the default printer defined. This can be found by invoking lookupDefaultPrintService().

public String getDefaultPrinter() {
   PrintService defaultPrinter =
   PrintServiceLookup.lookupDefaultPrintService();
   return defaultPrinter.getName();
 }

If no printer is selected yet in the ADF Application, I use the default printer. I need to make an adjustment to the getter of the selected printer to return the default printer.

public String getSelectedPrinter() {
  if (selectedPrinter == null) {
     return getDefaultPrinter();
   } else {
      return selectedPrinter;
   }
 }

Final requirement for the print startup form is to be able to reset the selected printer to the default if another printer was selected previously. For that I use a CommandButton that invokes an actionListener where I simply set the currently selected printer to be the default printer.

public void resetToDefault(ActionEvent actionEvent) {
    // Add event code here...
    setSelectedPrinter(getDefaultPrinter());
 }

By using this button, the default printer will be selected again.

allDefault

Now what about the actual printing ?

Printing can be done by using the DocPrintJob, FileInputStream and Doc objects. First is to create a FileInputStream based on the file that I want to print. Next step is to create a Doc object based on the FileInputStream. Finally create a PrintJob based on the Doc object. I also show the user a message when something goes wrong. All this functionality is implemented in an actionListener behind a command button.

public void invokePrintJob(ActionEvent actionEvent) {
   // Add event code here...
    PrintService[] printers =
    PrintServiceLookup.lookupPrintServices(null, null);
    Boolean printerFound = false;
    for (int i = 0; i < printers.length && !printerFound; i++) {
       if (printers[i].getName().equalsIgnoreCase(getSelectedPrinter())) {
          printerFound=true;
          PrintService printer = printers[i];
          FileInputStream input;
          try {
             input = new FileInputStream(theFile);
             Doc doc = new SimpleDoc(input, DocFlavor.INPUT_STREAM.PNG, null);
             PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
             DocPrintJob job = printer.createPrintJob();
             job.print(doc, attrs);
         } catch (PrintException e) {
                    System.out.println(e.getMessage());
                    addFacesErrorMessage(e.getMessage());
         } catch (FileNotFoundException e) {
                    System.out.println(e.getMessage());
                    addFacesErrorMessage(e.getMessage());
         }
      }
   }
}

The code for the page fragment is simple.

<af:toolbar>
   <af:commandToolbarButton text="Reset to Default"
                                               actionListener="#{pageFlowScope.applicationPrinterBean.resetToDefault}"
                                               partialSubmit="true" id="cb1"/>
   <af:commandToolbarButton text="Print" id="ctb1"
                                                actionListener="#{pageFlowScope.applicationPrinterBean.invokePrintJob}"/>
</af:toolbar>

And the page now has a print button.

print

When the print button is pushed, the document is sent directly to the printer.

ThereItis

It is also possible to set job properties such as number of copies, the orientation of the print, and many other properties.

attrs.add(new Copies(2));
attrs.add(OrientationRequested.LANDSCAPE);

I can check the result of this in the print properties of the printjob.
copyCount
Now one last thing to do is to give the printjob a decent name. As you can see, by default, all jobs have the same name, and that has no relation whatsoever with the document name, neither can it be related to a user or client.

wrongName-

I want to use a jobname that is more descriptive. I can do that by adding the JobName to the PrintRequestAttributeSet.

PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(new Copies(2));
attrs.add(OrientationRequested.LANDSCAPE);
JobName name = new JobName(file ,null);
attrs.add(name);

The result is obvious.

GoodName

Conclusion

Printing can be done directly from within the ADF Application. You can use almost any of the properties that can be used in the printer dialog of your browser or operating system. This example can be extended and I will probably create an ADF taskflow library to deploy this functionality.

Resources

Documentation can be found here : Javax.print javadoc
This post was originally published on lucbors.blogspot.com
Zipfile with the project can be found here.

2 Comments

  1. Rao December 25, 2014
  2. Shahzad May 15, 2014