Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send raw data to printer with Java

I am trying to create a simple program that sends a string to a printer to print. This is what my program looks like:

import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;

public class PrinterTest {
  public static void main (String [] args) throws PrintException {
    DocPrintJob job = null;
    PrintService[] printServices = 
    PrintServiceLookup.lookupPrintServices(null, null);
    System.out.println("Number of print services: " + printServices.length);
    for (PrintService printer : printServices) {
        System.out.println("Printer: " + printer.getName());
        if (printer.getName().contains("ZM400")) {
            String hello = "Hello";
            DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN;
            Doc doc = new SimpleDoc(hello, flavor, null);
            job = printer.createPrintJob();
            job.print(doc, null);
        }
    }
  }
}

I export this as a jar file and run it on command line (Windows) using:

java -jar PrinterTest.jar

The program runs, and starts looping through all of the installed printers on the computer. But when it gets to the printer I am looking for, I then get the following error:

Exception in thread "main" sun.print.PrintJobFlavorException: invalid flavor
  at sun.print.Win32PrintJob.print(Unknown Source)
  at PrinterTest.main(PrinterTest.java:21)

Not really sure what I'm doing wrong here, as the printer that I am searching for does in fact show up.

-Using the following link for reference: http://docs.oracle.com/javase/7/docs/technotes/guides/jps/spec/jpsOverview.fm4.html

-Tried changing DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN to DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE, but I get the error IllegalArgumentException: data is not of declared type.

-Tried changing Doc doc = new SimpleDoc(hello, flavor, null) to Doc doc = new SimpleDoc(hello, null, null), but it seems like you need to add a flavor there.

-Tried changing the printer, as the original printer I was trying to call was a labeling printer, but that did not make a difference.

Any idea what I'm doing wrong here? What can I do to fix this code and make it print?

UPDATE

I got this to (somewhat) work. This is what I have so far:

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;

public class PrinterTest {
  public static void main (String [] args) throws PrintException, IOException {
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    System.out.print("Enter the name of the printer: ");
    String printerName = bufferedReader.readLine();
    System.out.print("Enter a short message of what you would like to print here: ");
    String printerMessage = "PRINTER MESSAGE: " + bufferedReader.readLine();
    boolean printerCheck = false;
    DocPrintJob job = null;
    PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
    System.out.println("Number of print services: " + printServices.length);
    for (PrintService printer : printServices) {
        System.out.println("Printer: " + printer.getName());
        if (printer.getName().contains(printerName)) {
            InputStream inputStream = new ByteArrayInputStream(printerMessage.getBytes());
            DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
            Doc doc = new SimpleDoc(inputStream, flavor, null);
            job = printer.createPrintJob();
            job.print(doc, null);
            printerCheck = true;
        }
    }
    if (printerCheck == false) {
        System.out.println("The printer you were searching for could not be found.");
    }
  }
}

What I did was put the string into an input stream, and changed DocFlavor.STRING.TEXT_PLAIN to DocFlavor.INPUT_STREAM.AUTOSENSE.

My only hiccup now is that nothing actually prints unless something else is sent to the printer. Leaving this here for now for reference.

like image 994
aCarella Avatar asked Aug 03 '17 21:08

aCarella


1 Answers

The exception messages are rather helpful and should help you to find a solution.

1) First, this flavor is not supported by your printer :

 DocFlavor.STRING.TEXT_PLAIN;

This exception says it :

Exception in thread "main" sun.print.PrintJobFlavorException: invalid flavor

If you look in the source code of this constant value, you can see that it is declared as :

public static final STRING TEXT_PLAIN =
    new STRING ("text/plain; charset=utf-16");

So, the first thing you should do is checking which flavors are supported by your printer.
To render it, change the actual code :

if (printer.getName().contains("ZM400")) {
    String hello = "Hello";
    DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN;
    Doc doc = new SimpleDoc(hello, flavor, null);
    job = printer.createPrintJob();
    job.print(doc, null);
}

by :

if (printer.getName().contains("ZM400")) {
    Arrays.stream(printer.getSupportedDocFlavors()).forEach(f->System.out.println(f.getMediaType() + ":" + f.getMimeType() + ":" + f.getRepresentationClassName()));
    String hello = "Hello";
    DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN;
    Doc doc = new SimpleDoc(hello, flavor, null);
    job = printer.createPrintJob();
    job.print(doc, null);
}

With my printer, it produces the following output :

image:image/gif:[B image:image/gif:java.io.InputStream

image:image/gif:java.net.URL image:image/jpeg:[B

image:image/jpeg:java.io.InputStream image:image/jpeg:java.net.URL

image:image/png:[B image:image/png:java.io.InputStream

image:image/png:java.net.URL

application:application/x-java-jvm-local-objectref:java.awt.print.Pageable

application:application/x-java-jvm-local-objectref:java.awt.print.Printable

application:application/octet-stream:[B

application:application/octet-stream:java.net.URL

application:application/octet-stream:java.io.InputStream

You may notice the my printer doesn't support the "text/plain; charset=utf-16" flavor either.

2) By changing the flavor to DocFlavor.INPUT_STREAM.AUTOSENSE, you don't have any longer the exception related to missing support for the DocFlavor.
So it means that DocFlavor.INPUT_STREAM.AUTOSENSE is supported by your printer.
But you encounter another problem as the data to print doesn't match to declared type associated to DocFlavor.INPUT_STREAM.AUTOSENSE :

IllegalArgumentException: data is not of declared type.

In DocFlavor, the INPUT_STREAM.AUTOSENSE constant is declared in this way :

    public static final INPUT_STREAM AUTOSENSE =
        new INPUT_STREAM ("application/octet-stream");

This corresponds to the last supported flavor in the previous output :

application:application/octet-stream:java.io.InputStream

And the problem is there :

String hello = "Hello";     
...
Doc doc = new SimpleDoc(hello, flavor, null);

you don't have pass a InputStream but a String.

You can for example provide an InputStream in this way:

String hello = "Hello";     
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
Doc doc = new SimpleDoc(new ByteArrayInputStream(hello.getBytes()),
                         flavor, null);

Or you could also use this flavor :

application:application/octet-stream:[B

as you have not an InputStream but a String as input :

Here is the flavor constant to use :

DocFlavor.BYTE_ARRAY.AUTOSENSE

You could also use it in this way :

String hello = "Hello";     
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
Doc doc = new SimpleDoc(hello.getBytes(), flavor, null);
like image 100
davidxxx Avatar answered Oct 03 '22 09:10

davidxxx