PHP and Java: Using Java Print Service with Zend Server Community Edition

PHP and Java:

Using Java Print
Service with Zend Server Community Edition

If one were to survey the extensions available in PHP one
might come to the conclusion that we actually lived in a paperless society.
However, we all know that isn’t true. So why does PHP not have good printer
support? One option that explains why this might be the case is simply that
printing documents is contrary to what PHP does well; handle short requests as
quickly as possible. Printing is not conducive to this.

But what if you still need to have printing support in your
PHP application? With Zend
, Zend’s integrated PHP application stack, the answer might be not
what you were expecting. Use the PHP Java Bridge, available in both the community and commercial editions of Zend

First, you might be thinking “Java?!” What’s this guy
smoking? Well, Padron when I am able to, but Java is quite a legitimate
option. The reason why I am highlighting Java here is because it fits into the
same realm as PHP; environment independence and this is a supportable solution.
Many other printing options in PHP require operating system dependent methods,
they are not supported or they are not built for PHP 5. As a reader of this
article, it is likely that you need one, or all.

Printing in Java is done through the Java Print Service
(JPS) API. It has been available in Java since 1997 as part of AWT (Abstract
Windows Toolkit), prior to the advent of the JPS API. Using the AWT mechanism,
a document could be printed via AWT or, later on, the 2D Graphics interface.
But that was about it. The JPS API added more functionality than the original
printing API. One example of this is printer discovery. Prior to JPS, the
default printer was the only option.

In this article we will take a look at how you can use the
Zend Server Community Edition Java Bridge to connect in with the Java Print

Step One: Finding your printers

Prior to printing any documents you will need to find a
printer to print to. In Java, this is done by calling the static method PrintServiceLookup.lookupPrintServices. However,
this class is a static class which means when we try to access it in PHP, the
Java Bridge will try to create a new instance of it. This will cause an
exception to be thrown.

Attempting to browse installed printers

$jps = new java(‘javax.print.PrintServiceLookup’);

$printers = $jps->lookupPrintServices();

The solution to this problem is to write a very simple
implementation of PrintServiceLookup. You can, of course, write a lot of
custom functionality in your implementation, but for this example, we just want
to make it simple.

Figure 1: com.zend.Printer

package com.zend;






public class Printer extends
PrintServiceLookup {

PrintService getDefaultPrintService() {

            return lookupDefaultPrintService();


      public MultiDocPrintService[]

AttributeSet attr) {


            return lookupMultiDocPrintServices(flavor,


PrintService[] getPrintServices() {



PrintService[] getPrintServices(DocFlavor flavor,

attr) {




I wrote this code in Zend Studio for Eclipse using a new
Java Project called Printing. As such, the class files are compiled
automatically. So all I need to do now is point my Java Bridge JVM at the
appropriate directory. In the ZendServer/etc directory there is a file called java_bridge_server.ini.
This file contains all of the settings for the Java Bridge. By default, it
contains the following information:

Figure 2: java_bridge_server.ini



What we need to do is add the new class file to the class
path. So append the base directory of the Printing project’s bin directory to
the class path.

Figure 3: Modified java_bridge_server.ini


Files\Zend\ZendServer\bin\javamw.jar;. ;c:\workspace\Printing\bin"

After restarting the Java Bridge we can now test to see if
we can get a list of the installed printers in PHP. The code to test this is
relatively simple.

Figure 4: Test code for printer list

$jps = new java(‘com.zend.Printer’);

foreach ($jps->getPrintServices()
as $printer) {

      echo $printer->getName()."\n";


This code then prints a list of all of the printers
installed on your system

Figure 5: Test code output

WebEx Document Loader



Microsoft XPS Document

Microsoft Office
Document Image Writer

If you are wondering about the overhead of doing this call,
it is 4-7ms once the initial call to the Java Bridge has been made.

Step Two: Printing a simple document

Printing a document can be either complex or simple. It can
be simple if the printer you are working with supports the type of document
that is going to be submitted. With the JPS this is known as a document flavor
and is represented by the class DocFlavor. But prior to
submitting the document, we need to find out if the printer supports it. That
is done by calling the getSupportedDocFlavors()
method on the $printer object.

Figure 6: Getting a list of supported document

$jps = new java(‘com.zend.Printer’);

foreach ($jps->getPrintServices()
as $printer) {

      echo $printer->getName()."
supports\n "

      $flv = array();

      foreach ($printer->getSupportedDocFlavors()
as $docFlavor) {

            $flv[] =


      echo implode("\n
, $flv)."\n\n";


As an example, this script outputs

On my system, this ended up printing 75+ options for
printing once all of the document flavor and printer combinations were done.
However, one thing you may notice is that there is no PDF, Word or other such
complex types. We will look at those in step 3.

Let’s start with the code first and then we’ll explain
what’s going on.

Figure 7: Code to print a simple document

// First select the active

$jps = new java(‘com.zend.Printer’);

$activePrinter = null;

foreach ($jps->getPrintServices()
as $printer) {

      if ($printer->getName()
‘PDFCreator’) {

= $printer;





// We are going to print the
Zend Server product image

$url = new java(




// Create a flavor that uses
an input stream so we can stream

// the results of the URL

$flavor = new java(‘javax.print.DocFlavor$INPUT_STREAM’);

// Create the document

$doc = new java(






// Create the job

$job = $activePrinter->createPrintJob();

// Print!!

$job->print($doc, null);

Everything up until where $flavor is created is
self-explanatory. Creating $flavor can be likened to configuring the printer system
on what type of content to expect. The $INPUT_STREAM is a predefined class
that represents the type of document we’re going to print. Note that we did
not create an object of DocFlavor and then reference INPUT_STREAM->JPEG
in the SimpleDoc
call. i.e.
. This
does not work. So when referencing the predefined class, we need to reference
the first level.

After we have created our DocFlavor reference it is time to
create the actual doc. This is done by creating an instance of javax.print.SimpleDoc.
This class can handle some basic images. We create an instance of it, passing
in the URL object’s InputStream instance with the DocFlavor into the
constructor. The third argument is for printing attributes, such as paper size
or the number of copies. This was omitted by passing in NULL.

Once our document has been created it is time to retrieve a
new job from the printer. That is done by calling createPrintJob() on
our $activePrinter
object. Once we have an object representing the print job all that’s left to
do is print the document. Refreshing this page causes it to submit the job to
my local print spooler and I get a confirmation to print the page (this can be
automated). The result is an obvious PDF-based image retrieved from the URL

Figure 8: The printed image



Step Three: Printing a complex document

printing a complex document we are going to look at printing a PDF document.
There are some options available for printing Word documents in Java, but I did
not have the time to fully research it, nor did I want to spend the money, so
opted for PDF since it’s nice and portable.

first thing you will need to do is find a PDF renderer. If we look at the
output of Figure 6 you will notice that PDF is not listed as one of the
supported flavors. I chose “PDF Renderer” from

let’s start first by creating a form that allows us to upload a PDF document to
our PHP installation.

Figure 9: Code to print the form

require_once ‘Zend/Loader/Autoloader.php’;


$jps = new java(‘com.zend.Printer’);

$printers = array();

$selectedPrinter = null;

foreach ($jps->getPrintServices()
as $printer) {

      $printers[] = $printer->getName();

      if (isset($_POST[‘printer’])

&& $_POST[‘printer’] == $printer->getName())

= $printer;



$form = new Zend_Form();

$form->setView(new Zend_View());


$form->addElement(‘file’, ‘upload’, array(‘label’ => ‘Upload

$form->addElement(‘select’, ‘printer’,

=> $printers, ‘label’ => ‘Choose

$form->addElement(‘submit’, ‘Print’);

if ($form->isValid($_POST)) {

      echo $form->upload->getFileName();



echo $form;

will note that I used Zend Framework to print the form. I did that because I
like other people to do as much work for me as possible. This code creates the
following output:

Figure 10: Form output



I submit this form it will print out the temporary name of the file that was
uploaded. Now I need to pass the contents of that file into the Java Bridge. One
might be tempted to keep this in PHP, but depending on the size of your
document this could be a prohibitive amount of data which, as I found in my
testing, may have some undesirable results and the mechanism needed to actually
do the printing is somewhat cumbersome. So, instead of writing this code in
PHP we are going to write a minimal abstraction layer in the Printer

The base object for the PDF
Renderer is the PDFFile class. Its constructor takes an instance of java.nio.ByteBuffer
as its parameter. ByteBuffer is a class that is used with Java’s New IO
(NIO) package as a container for primitive data types. If you’d like some more
information on the NIO classes you can go to

order to print the PDF document, we will need to create a bridge between the
PDF Renderer and the JPS. PDF Renderer does not directly support doing this,
but the example application that comes with the library has the code written
out. You will find it in Appendix A. That code will take the PDFFile
object and render it using the 2DGraphics java library so the JPS
library can send the binary data to the printer.

the code from Appendix A has been compiled and included in the Java Bridge
class path we can now write our PHP code to send the PDF file to the printer.
First, we will print out the block as a whole and then go through it
line-by-line to explain what we’re doing at each step in the process since
there are a few moving parts here.

Code to print a PDF to a JPS printer

if ($form->isValid($_POST)
$form->upload->receive()) {

$mm = new Java(‘java.nio.channels.FileChannel$MapMode’);

$file = new java(‘’, $form->upload->getFileName());

$fis = new java(‘’, $file);

$fc = $fis->getChannel();

$bb = $fc->map($mm->READ_ONLY,
0, $fc->size());

$pdfFile = new java(‘com.sun.pdfview.PDFFile’, $bb);   

= new java(‘com.zend.PDFPrintRenderer’, $pdfFile);

$job = $selectedPrinter->createPrintJob();

$flavor = new java(‘javax.print.DocFlavor$SERVICE_FORMATTED’);

$doc = new java(







$job->print($doc, null);



So let’s go through this line-by-line now.

Create a reference to the MapMode class so we can access the
fields in the class later on.

= new java(‘java.nio.channels.FileChannel$MapMode’);

Create a new File object that refers back to the
uploaded file name.

= new java(‘’, $form->upload->getFileName());

Create an input stream that we can read from. We will not
read directly from this but rather use it as a gateway to retrieving the NIO channel
which is used to retrieve and buffer the data that will be used to create the PDFFile

= new java(‘’, $file);

Retrieve the NIO channel

= $fis->getChannel();

The map() method will map the file directly into memory.
This method returns a ByteBuffer instance that represents that mapping.

= $fc->map($mm->READ_ONLY, 0, $fc->size());

After we have created the mapping we can now create our PDFFile
object. The PDFFile
constructor requires its parameter to be an instance of ByteBuffer, which is
why we had to go the NIO route instead of standard Java IO.

= new java(‘com.sun.pdfview.PDFFile’, $bb);

Then create the renderer. This is the class that is
available in Appendix A which provides the interface between the PDF Renderer
library and the JPS library. It takes an instance of PDFFile as its
constructor parameter.

= new java(‘com.zend.PDFPrintRenderer’, $pdfFile);

Create a new printer job to work with.

= $selectedPrinter->createPrintJob();

Like the $mm variable, we need to access one of the fields in the DocFlavor
class. However, earlier we referenced the INPUT_STREAM field whereas here we
are referencing SERVICE_FORMATTED. This is the flavor that is used to
send a printable Java object to the JPS. Since our PDFPrinterRenderer
implements the Printable interface, we are referencing the application/x-java-jvm-local-objectref;

= new java(‘javax.print.DocFlavor$SERVICE_FORMATTED’);

Create a new SimpleDoc object, passing in the
renderer object and specifying the PRINTABLE flavor.


$job->print($doc, null);

At this point your hard drive will start spinning, your CPU usage
will jump and after a bit you should have a newly printed document. Note that this
mechanism will, by default, print every page in the document, but there are
ways of controlling this in the JPS library. It is also important to note that
this can be a very CPU intensive operation. If you are printing anything
beyond a few simple pages it is recommended that you take a look at some
queuing options so printing can be done separate from the HTTP request. Zend
Platform’s Job Queue is a great place to start.

I hope this was an informative look at a feature that can be
useful to PHP-based applications.

Kevin Schroeder is a
Technical Consultant for Zend Technologies, and well versed in a wide variety
of technologies pertinent to both small and large scale application
deployments. He primarily works with customers in the United States, providing
PHP-based services for a variety of customers. Kevin is also a proven
instructor for many of Zend’s courses and is involved in training development
as well as speaking at conferences on a variety of subjects pertaining to PHP.
Together with Jeff Olen, he is co-author of the book “The IBM i Programmer’s
Guide to PHP”.

Appendix A

PDFRenderer implementation of Printable

public class PDFPrintRenderer
implements Printable

private PDFFile file;

PDFPrintRenderer(PDFFile file) {

  this.file = file;


public int
print(Graphics g, PageFormat format,
int index)

throws PrinterException {

            int pagenum =
index + 1;


            // don’t bother if the
page number is out of range.

            if ((pagenum
>= 1) && (pagenum <=


// fit the PDFPage into
the printing area

Graphics2D g2 = (Graphics2D) g;

PDFPage page =

double pwidth = format.getImageableWidth();

double pheight = format.getImageableHeight();


double aspect = page.getAspectRatio();


// handle
page orientation matching

double paperaspect = pwidth / pheight;

if (paperaspect < 1.0) {

switch (format.getOrientation()) {


case PageFormat.LANDSCAPE:



case PageFormat.PORTRAIT:




pwidth = format.getImageableWidth();

pheight = format.getImageableHeight();

paperaspect = pwidth / pheight;



Rectangle imgbounds;

int width;

int height;

if (aspect > paperaspect) {

// paper is too tall / pdfpage is too wide

height = (
int) (pwidth / aspect);

width = (
int) pwidth;

else {

// paper is too wide / pdfpage is too tall

width = (
int) (pheight * aspect);

height = (
int) pheight;


imgbounds =
new Rectangle((int) format.getImageableX(),

int) format.getImageableY(),

width, height);


// render
the page

PDFRenderer pgs =

new PDFRenderer(page, g2, imgbounds, null, null);

try {



catch (InterruptedException ie) {



else {

return NO_SUCH_PAGE;