Печать на определенный принтер (IPP URI) в Java

Есть ли способ в 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.

В крайнем случае я подумал о том, чтобы просто перечислить все PrintServiceс возвращено lookupPrintServices, но URI отсутствует ни в одном из атрибутов. Имя принтера там, но мне нужен URI.

В качестве фона моему веб-приложению необходимо распечатать штрих-код на определенном принтере в зависимости от текущего пользователя. Каждый пользователь связан с URI принтера, который указывает на принтер на сервере CUPS. URI принтера - единственная информация, которую я имею, и я не могу ограничить имя принтера, чтобы оно совпадало с URI или подстрокой URI.

Изменить: Чтобы уточнить немного, мне не нужно визуализировать данные, мне просто нужно скопировать BLOB-объект на данный принтер. Часть, которую я не могу понять, это как идентифицировать принтер по IP-адресу URI.

4 ответа

Решение

Я наконец нашел способ сделать это, используя 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.

Чтобы отправить только печатный формат документа, такой как PDF, через IPP на принтер (или в 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));
  }

}

Я не думаю, что вы можете получить принтер так, как вы хотели бы (я думаю, что механизм Java Print предшествует IPP).

Однако вы можете, если я правильно помню, иметь возможность визуализировать ваше задание на печать локально, а затем пересылать байты выходного потока на целевой сервер CUPS "вручную". Будет ли это "достаточно хорошо" для вас?

Используя этот ipp-клиент, вы можете отправить файл PDF на принтер (или CUPS), который поддерживает этот формат документа:

      new IppPrinter(URI.create("ipp://myprinter")).printJob(
    File("mydocument.pdf"), documentFormat("application/pdf")
);
Другие вопросы по тегам