Java printing PDF with options (staple, duplex, etc)

2.7k views Asked by At

I have a java program that prints PDFs. It uses Apache PDFBox to create a PDDocument object (from a pdf document or from a stream in some cases) and then sends it to the printer using the javax.print API:

private boolean print(File pdf, String printer)
{
    boolean success = false;

    try (PDDocument document = PDDocument.load(pdf))
    {
        PrintService[] printServices = PrinterJob.lookupPrintServices();
        PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPageable(new PDFPageable(document));

        // set printer
        if (printer != null)
        {
            for (PrintService selected : printServices)
            {
                if (selected.getName().equals(printer))
                {
                    printService = selected;
                    break;
                }
            }
        }
        job.setPrintService(printService);
        job.print();
        success = true;
    }
    catch (Exception e)
    {
        myLog.error("Printer error.", e);
    }
    return success;
}

Now I need to be able to tell the printer to staple the thing...


I am familiar with the javax.print.attributes API and use this successfully for specifying the tray or setting duplex, e.g.:

// this works fine
if (duplex != null)
{               
    if (duplex.equalsIgnoreCase("short"))
    {
        myLog.debug("Setting double-sided: Short");
        attr.add(Sides.TWO_SIDED_SHORT_EDGE);
    }
    else
    {
        myLog.debug("Setting double-sided: Long");
        attr.add(Sides.TWO_SIDED_LONG_EDGE);
    }
}

I know there is an attribute for stapling:

attr.add(javax.print.attribute.standard.Finishings.STAPLE);

I have a Xerox Versalink B7035 with a Finisher XL attachment that fully supports stapling (i.e. it works from MS Office document settings) however the printer disregards the STAPLE attribute set from Java. I tried all other variants of staple attributes but soon found that the printer did not support ANY Java finishing attributes.

Or to put it in code, the following prints NO results:

DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
Object finishings = myPrinter.getSupportedAttributeValues(Finishings.class, flavor, null);
if (finishings != null && o.getClass().isArray())
{
    for (Finishings finishing : (Finishings[]) finishings)
    {
        System.out.println(finishing.getValue() + " : " + finishing);
    }
}

After reading this and trying a few different things I concluded the printer will not accept the STAPLE attribute because the finisher is an attachment or simply because Xerox doesn't like Java or something. So now I am attempting to solve this by prepending PJL commands to the pdf before sending it, as covered here. *PJL = Print Job Language

E.g:

<ESC>%-12345X@PJL<CR><LF>
@PJL SET STAPLE=LEFTTOP<CR><LF>
@PJL ENTER LANGUAGE = PDF<CR><LF>
[... all bytes of the PDF file, starting with '%PDF-1.' ...]
[... all bytes of the PDF file ............................]
[... all bytes of the PDF file ............................]
[... all bytes of the PDF file, ending with '%%EOF' .......]
<ESC>%-12345X

At first I assumed there would just be some method in the Apache PDFBox library to do just this, but no luck. Then I checked out the API for Ghost4J and saw nothing for prepending. Has anyone else solved this already?

1

There are 1 answers

1
egerardus On

Reverting to Java socket printing makes PJL a thing:

// this works, it also printed faster than javax.print when tested
private static void print(File document, String printerIpAddress, boolean staple)
{
    try (Socket socket = new Socket(printerIpAddress, 9100))
    {
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
        byte[] bytes = Files.readAllBytes(document.toPath());

        out.write(27); //esc
        out.write("%-12345X@PJL\n".getBytes());
        out.write("@PJL SET DUPLEX=ON\n".getBytes());

        if (staple) 
        {
            out.write("@PJL SET STAPLEOPTION=ONE\n".getBytes());
        }
        out.write("@PJL ENTER LANGUAGE=PDF\n".getBytes());
        out.write(bytes);
        out.write(27); //esc
        out.write("%-12345X".getBytes());
        out.flush();
        out.close();
    }
    catch (Exception e)
    {
        System.out.println(e);
    }
}

The needed PJL commands came from this Xerox datasheet.

It should be noted that the same PJL commands worked for two different Xerox models and a Lexmark printer, that's all I had handy to test with. Dunno if other models will want something different.

Do not need the Apache PDFBox library anymore. Or any external libraries at all.

This might work for other types of documents, aside from PDFs.