在 Java 中打印到特定打印机 (IPP URI)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1655297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:43:26  来源:igfitidea点击:

Print to specific printer (IPP URI) in Java

javaprintingipp-protocol

提问by Jason Day

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:

Java 中有什么方法可以打印到特定的 IPP 打印机吗?我发现的所有示例代码和教程都侧重于如何使用以下内容打印特定类型的文档:

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 PrintServiceLookupdoesn't seem to support this. I've tried using a PrintServiceAttributeSet, instead of PrintRequestAttributeSet, and adding a PrinterURIattribute, 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.

此代码段只是打印到发现能够打印文档的第一台打印机。就我而言,我想通过其 URI 查找打印机,但PrintServiceLookup似乎不支持这一点。我试过使用PrintServiceAttributeSet, 而不是PrintRequestAttributeSet, 并添加一个PrinterURI属性,但这不会返回任何打印机。我怀疑查找服务正在寻找可以更改其目标 URI 的打印机,而不是寻找具有该 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.

作为最后的手段,我想只枚举PrintService返回的所有s lookupPrintServices,但 URI 不在任何属性中。打印机名称在那里,但我需要 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.

对于背景,我的 webapp 需要根据当前用户将条形码打印到特定打印机。每个用户都与一个打印机 URI 相关联,该 URI 指向 CUPS 服务器上的打印机。打印机 URI 是我拥有的唯一信息,我无法限制打印机名称以匹配 URI 或 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.

编辑:澄清一下,我不需要渲染数据,我只需要将一个 blob 复制到给定的打印机。我无法弄清楚的部分是如何通过其 IPP URI 识别打印机。

采纳答案by Jason Day

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

我终于找到了一种方法来做到这一点,通过使用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.

我不得不承认我对不得不使用 3rd-party library 来做一些看起来像打印到特定打印机这样简单的事情感到失望。

UPDATE

更新

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

DR 在评论中指出 jipsi 有一个新家,一个新名字。

Cups4Jis 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.

Cups4J是一个不错的选择,但顾名思义,如果目标不是 CUPS 服务器,它可能无法正常工作。我使用 Cups4J 直接打印到 Zebra 热敏打印机的效果很好。

回答by Thorbj?rn Ravn Andersen

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

我认为您无法按照自己的意愿获得打印机(我认为 Java 打印机制早于 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?

但是,如果我没记错的话,您可能能够在本地呈现您的打印作业,然后“手动”将输出流的字节发送到目标 CUPS 服务器。这对你来说“足够好”了吗?

回答by IPP Nerd

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-javabasic support for decoding the ipp response.

为了仅通过 IPP 将可打印的文档格式(如 PDF)提交给打印机(或 CUPS),此代码提供了一个没有依赖性的简约实现。ipp-printjob-java基本支持解码 ipp 响应。

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));
  }

}