Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print to specific printer (IPP URI) in Java

Is there any way in Java to print to a specific IPP printer? All of the sample code and tutorials I've found focus on how to print a particular type of document, using something like the following:

DocFlavor flavor = DocFlavor.INPUT_STREAM.POSTSCRIPT;
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
PrintService[] pservices =
             PrintServiceLookup.lookupPrintServices(flavor, aset);
if (pservices.length > 0) {
    DocPrintJob pj = pservices[0].createPrintJob();
    try {
        FileInputStream fis = new FileInputStream("test.ps");
        Doc doc = new SimpleDoc(fis, flavor, null);
        pj.print(doc, aset);
    } catch (FileNotFoundException fe) {
    } catch (PrintException e) { 
    }
}

This snippet simply prints to the first printer found that is capable of printing the document. In my case, I want to lookup a printer by its URI, but PrintServiceLookup doesn't seem to support this. I've tried using a PrintServiceAttributeSet, instead of PrintRequestAttributeSet, and adding a PrinterURI attribute, but that doesn't return any printers. I suspect the lookup service is looking for a printer that can change its destination URI, rather than looking for the printer with that URI.

As a last resort, I thought about just enumerating through all of the PrintServices returned by lookupPrintServices, but the URI is not in any of the attributes. The printer name is there, but I need the URI.

For background, my webapp needs to print a barcode to a specific printer, based on the current user. Each user is associated with a printer URI, which points to a printer on a CUPS server. The printer URI is the only information I have, and I can't constrain the printer name to match the URI or a substring of the URI.

Edit: To clarify a bit, I don't need to render the data, I just need to copy a blob to a given printer. The part I can't figure out is how to identify a printer by its IPP URI.

like image 905
Jason Day Avatar asked Oct 31 '09 19:10

Jason Day


People also ask

How do I connect to a printer using IPP?

Select [Connect to a printer on the Internet or on a home or office network]. Enter [URL]. Enter "http://<the IP address or name of the printer>/ipp." When you want to perform IPPS printing using SSL encrypted communication, enter "https://<the IP address or name of the printer>/ipp."

What is IPP print settings?

Setting flow IPP printing uses the Internet Printing Protocol (IPP) and prints information via the network. IPP that is extended HTTP is used to forward printing data, enabling you to print data on a printer on a distance location via the Internet.

What is the URI of a printer?

This single-valued attribute specifies the Uniform Resource Identifier (URI) of the remote Internet Printing Protocol (IPP) server that is running either in a printer or host system. This attribute is required if you create a printer definition and protocol-type=ipp.

What does IPP mean in printing?

IPP stands for Internet Printing Protocol... It allows programs to send one or more print jobs to the printer or print server and perform administration such as querying the status of a printer, obtaining the status of print jobs, or cancelling individual print jobs.


3 Answers

I finally found a way to do this, by using jipsi:

URI printerURI = new URI("ipp://SERVER:631/printers/PRINTER_NAME");
IppPrintService svc = new IppPrintService(printerURI);
InputStream stream = new BufferedInputStream(new FileInputStream("image.epl"));
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
Doc myDoc = new SimpleDoc(stream, flavor, null);
DocPrintJob job = svc.createPrintJob();
job.print(myDoc, null);

I have to admit I'm disappointed at having to use a 3rd-party library to do something so seemingly simple as printing to a specific printer.

UPDATE

DR points out in the comments that jipsi has a new home, and a new name.

Cups4J is a nice alternative, but as the name implies it may not work correctly if the destination is not a CUPS server. I have had good results using Cups4J to print directly to a Zebra thermal printer.

like image 67
Jason Day Avatar answered Sep 28 '22 23:09

Jason Day


I do not think you can get a printer the way you would like to (I think the Java Print mechanism predates IPP).

You may, however, if I recall correctly be able to render your print job locally and then ship the bytes of the output stream to the target CUPS server "by hand". Would this be "good enough" for you?

like image 22
Thorbjørn Ravn Andersen Avatar answered Sep 28 '22 23:09

Thorbjørn Ravn Andersen


To only submit a printable document format like PDF via IPP to a printer (or to CUPS) this code provides a minimalistic implementation without dependencies. ipp-printjob-java has basic support for decoding the ipp response.

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class IppPrintJob {

  public static void main(String args[]) throws Exception {
    URI printerURI = URI.create("http://colorjet:631/ipp/printer");
    File file = new File("A4-blank.pdf");
    short status = new IppPrintJob()
      .printDocument(printerURI, new FileInputStream(file));
    System.out.println(String.format("ipp status: %04X", status));
  }

  short printDocument(
    URI uri, InputStream documentInputStream
  ) throws IOException {
    HttpURLConnection httpURLConnection =
      (HttpURLConnection) uri.toURL().openConnection();
    httpURLConnection.setDoOutput(true);
    httpURLConnection.setRequestProperty("Content-Type", "application/ipp");
    OutputStream outputStream = httpURLConnection.getOutputStream();
    DataOutputStream dataOutputStream =
      new DataOutputStream(httpURLConnection.getOutputStream());
    dataOutputStream.writeShort(0x0101); // ipp version
    dataOutputStream.writeShort(0x0002); // print job operation
    dataOutputStream.writeInt(0x002A); // request id
    dataOutputStream.writeByte(0x01); // operation group tag
    writeAttribute(dataOutputStream, 0x47, "attributes-charset", "utf-8");
    writeAttribute(dataOutputStream, 0x48, "attributes-natural-language", "en");
    writeAttribute(dataOutputStream, 0x45, "printer-uri", uri.toString());
    dataOutputStream.writeByte(0x03); // end tag
    documentInputStream.transferTo(outputStream);
    dataOutputStream.close();
    outputStream.close();
    if (httpURLConnection.getResponseCode() == 200) {
      DataInputStream dataInputStream =
        new DataInputStream(httpURLConnection.getInputStream());
      System.out.println(String.format("ipp version %d.%s",
        dataInputStream.readByte(), dataInputStream.readByte()
      ));
      return dataInputStream.readShort();
    } else {
      throw new IOException(String.format("post to %s failed with http status %d",
        uri, httpURLConnection.getResponseCode()
      ));
    }
  }

  void writeAttribute(
    DataOutputStream dataOutputStream, int tag, String name, String value
  ) throws IOException
  {
    Charset charset = StandardCharsets.UTF_8;
    dataOutputStream.writeByte(tag);
    dataOutputStream.writeShort(name.length());
    dataOutputStream.write(name.getBytes(charset));
    dataOutputStream.writeShort(value.length());
    dataOutputStream.write(value.getBytes(charset));
  }

}
like image 1
IPP Nerd Avatar answered Sep 28 '22 22:09

IPP Nerd