Search code examples
javaexceptionprintingdocumentraw-data

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.


Solution

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