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

16

在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) { 
    }
}

这段代码会打印到第一个能够打印文档的打印机上。在我的情况下,我想要通过URI查找打印机,但是PrintServiceLookup好像不支持这样做。我尝试使用PrintServiceAttributeSet代替PrintRequestAttributeSet,并添加一个PrinterURI属性,但是没有返回任何打印机。我怀疑查找服务正在寻找可以更改目标URI的打印机,而不是寻找具有该URI的打印机。

最后,我想到了枚举由lookupPrintServices返回的所有PrintService,但是URI不在任何属性中。打印机名称在那里,但是我需要URI。

背景是,我的web应用程序需要根据当前用户将条形码打印到特定的打印机上。每个用户都与打印机URI相关联,该URI指向CUPS服务器上的打印机。打印机URI是我所拥有的唯一信息,我无法将打印机名称限制为与URI或其子字符串匹配。

编辑:稍作澄清,我不需要呈现数据,我只需要将一块数据复制到给定的打印机上。我无法弄清楚如何通过IPP URI标识打印机。

4个回答

19

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

我必须承认,我对于不得不使用第三方库来完成似乎很简单的打印到指定打印机的操作感到失望。

更新:

DR在评论中指出jipsi有一个新的主页和新的名称。

Cups4J是一个不错的替代品,但是顾名思义,如果目标不是CUPS服务器,则可能无法正常工作。 我已经成功地使用Cups4J直接打印到Zebra热敏打印机。


这个链接似乎已经失效了,但是原始代码似乎仍然可以在code.google.com上找到,不过名字有所改变(jspi):http://code.google.com/p/jspi/ (只能svn checkout,没有下载)。 - Daniel Rikowski
@DR 感谢提供链接,我已经更新了答案并添加了这些信息。 - Jason Day
你是怎么想出要在URL末尾加上PRINTER_NAME的呢? - rancidfishbreath
@rancidfishbreath 我已经知道打印机的名称了,那是我们在CUPS服务器中配置的。我已经从CUPS获得了完整的URI,我只需要一种打印它的方法。 - Jason Day
6
现在,JSPI已经进行了重构和Maven化,并可在 https://github.com/bhagyas/jspi 上获得。 - bhagyas
显示剩余2条评论

1
这段代码提供了一个最小化的实现,可以通过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));
  }

}

1

我认为您无法以您想要的方式获取打印机(我认为Java打印机制先于IPP)。

但是,如果我记得正确,您可以在本地呈现打印作业,然后手动将输出流的字节发送到目标CUPS服务器。这对您来说是否足够好?


实际上,打印作业已经由外部进程呈现为EPL格式,这是打印机本地支持的格式(类似于Postscript或PCL)。所以,如果我可以将字节发送到CUPS服务器,那就足够好了。不过,如果可能的话,我想避免手动实现IPP客户端。 - Jason Day

0

使用ipp-client,您可以将PDF文件提交到支持此文档格式的打印机(或CUPS):

new IppPrinter(URI.create("ipp://myprinter")).printJob(
    File("mydocument.pdf"), documentFormat("application/pdf")
);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接