Printing via cups using javax.print - java

I have a file that works just fine if I use the command lp filename.
The file is an ESC/P file for a receipt impact printer. That has linux native CUPS drivers and all that works.
Im trying to use the javax.print API so that I can have a finer grained control over the printing and hopefully keep it cross-platform compatible, though Linux is the target platform.
I've tried every DocFlavor combination known to man and every type of data type (InputStream, byte[], Reader etc.)
It either just ignores the print() command all together or just flips out a blank sheet of paper. Running lp filename prints it perfect, so how do I get javax.print to just do the functional equivalent of lp filename?
I'm not set on using javax.print I can use other "things" and might start investigating cups4J but it seems it would limit me to Linux/*nix only, which is OK for now but would rather have a cross platform solution.
I could just issue the lp system command on the file but, I need finer grained control. These aren't receipts we're printing, they are tickets and the tickets range from $5.00 to to thousands of dollars. Currently if we detect a printing issue, we void the transaction and if anything printed, its invalid, we don't take reprints lightly and most of the time charge to print a new copy if the customer looses his copy. Oh the reason for doing this is we're changing the POS system from Windows to Linux and the printers from direct access over serial ports to CUPS managed over USB. Here's my code that doesn't work. Any help is appreciated.
try {
// Find the default service
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
// Create the print job
DocPrintJob job = service.createPrintJob();
InputStream in = new FileInputStream("/home/bart/real.escp");
Doc docNew = new SimpleDoc(in,flavor,null);
// Monitor print job events; for the implementation of PrintJobWatcher,
// see Determining When a Print Job Has Finished
PrintJobWatcher pjDone = new PrintJobWatcher(job);
// Print it
job.print(docNew, null);
// Wait for the print job to be done
pjDone.waitForDone();
// It is now safe to close the input stream
in.close();
} catch (PrintException e) {
} catch (IOException e) {
}

I am fine with cups4j.
First get your printer.
try {
CupsClient client = new CupsClient("addressOfTheCupsServer", 631);
List<CupsPrinter> printers = client.getPrinters();
if (printers.size() == 0) {
throw new RuntimeException("Cant list Printer");
}
for (CupsPrinter cupsPrinter : printers) {
if (cupsPrinter.getName().equals("NameOfPrinter")) {
selectedPrinter = cupsPrinter;
}
}
}catch (Exception ignored){
ignored.printStackTrace();
}
}
Then create a printjob and send it to the printer
PrintJob printJob = new PrintJob.Builder(inputStream).jobName("Jobname").build();
PrintRequestResult result = selectedPrinter.print(printJob);

Related

Java printing PDF with options (staple, duplex, etc)

I have a java program that prints PDFs. It uses Apache PDFBox to create a PDDocument object (from a pdf document or from a stream in some cases) and then sends it to the printer using the javax.print API:
private boolean print(File pdf, String printer)
{
boolean success = false;
try (PDDocument document = PDDocument.load(pdf))
{
PrintService[] printServices = PrinterJob.lookupPrintServices();
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
PrinterJob job = PrinterJob.getPrinterJob();
job.setPageable(new PDFPageable(document));
// set printer
if (printer != null)
{
for (PrintService selected : printServices)
{
if (selected.getName().equals(printer))
{
printService = selected;
break;
}
}
}
job.setPrintService(printService);
job.print();
success = true;
}
catch (Exception e)
{
myLog.error("Printer error.", e);
}
return success;
}
Now I need to be able to tell the printer to staple the thing...
I am familiar with the javax.print.attributes API and use this successfully for specifying the tray or setting duplex, e.g.:
// this works fine
if (duplex != null)
{
if (duplex.equalsIgnoreCase("short"))
{
myLog.debug("Setting double-sided: Short");
attr.add(Sides.TWO_SIDED_SHORT_EDGE);
}
else
{
myLog.debug("Setting double-sided: Long");
attr.add(Sides.TWO_SIDED_LONG_EDGE);
}
}
I know there is an attribute for stapling:
attr.add(javax.print.attribute.standard.Finishings.STAPLE);
I have a Xerox Versalink B7035 with a Finisher XL attachment that fully supports stapling (i.e. it works from MS Office document settings) however the printer disregards the STAPLE attribute set from Java. I tried all other variants of staple attributes but soon found that the printer did not support ANY Java finishing attributes.
Or to put it in code, the following prints NO results:
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
Object finishings = myPrinter.getSupportedAttributeValues(Finishings.class, flavor, null);
if (finishings != null && o.getClass().isArray())
{
for (Finishings finishing : (Finishings[]) finishings)
{
System.out.println(finishing.getValue() + " : " + finishing);
}
}
After reading this and trying a few different things I concluded the printer will not accept the STAPLE attribute because the finisher is an attachment or simply because Xerox doesn't like Java or something. So now I am attempting to solve this by prepending PJL commands to the pdf before sending it, as covered here.
*PJL = Print Job Language
E.g:
<ESC>%-12345X#PJL<CR><LF>
#PJL SET STAPLE=LEFTTOP<CR><LF>
#PJL ENTER LANGUAGE = PDF<CR><LF>
[... all bytes of the PDF file, starting with '%PDF-1.' ...]
[... all bytes of the PDF file ............................]
[... all bytes of the PDF file ............................]
[... all bytes of the PDF file, ending with '%%EOF' .......]
<ESC>%-12345X
At first I assumed there would just be some method in the Apache PDFBox library to do just this, but no luck. Then I checked out the API for Ghost4J and saw nothing for prepending. Has anyone else solved this already?
Reverting to Java socket printing makes PJL a thing:
// this works, it also printed faster than javax.print when tested
private static void print(File document, String printerIpAddress, boolean staple)
{
try (Socket socket = new Socket(printerIpAddress, 9100))
{
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
byte[] bytes = Files.readAllBytes(document.toPath());
out.write(27); //esc
out.write("%-12345X#PJL\n".getBytes());
out.write("#PJL SET DUPLEX=ON\n".getBytes());
if (staple)
{
out.write("#PJL SET STAPLEOPTION=ONE\n".getBytes());
}
out.write("#PJL ENTER LANGUAGE=PDF\n".getBytes());
out.write(bytes);
out.write(27); //esc
out.write("%-12345X".getBytes());
out.flush();
out.close();
}
catch (Exception e)
{
System.out.println(e);
}
}
The needed PJL commands came from this Xerox datasheet.
It should be noted that the same PJL commands worked for two different Xerox models and a Lexmark printer, that's all I had handy to test with. Dunno if other models will want something different.
Do not need the Apache PDFBox library anymore. Or any external libraries at all.
This might work for other types of documents, aside from PDFs.

Sending a Barcode to a Zebra Printer from a Java Application

I am creating a java application that retrieves a userId from a database, transforms it into a barcode, then sends it to a printer. I am planning to use a Zebra printer and I was wondering if anyone has experience of printing to a Zebra printer from a Java application; if so, could you share some code making this possible?
Thanks in advance,
Tumaini
There are two ways to work with Zebra printers. The first is to print as on regular printer. Java printing basics are well explained in official tutorial. End of page will treated by printer as end of sticker. The disadvantage of this approach is that all painting must be done by hands. I.e. you can't use internal printer's barcoding ability.
The second is to write ZPL commands directly to printer. Something like this:
PrintService pservice = ... // acquire print service of your printer
DocPrintJob job = pservice.createPrintJob();
String commands = "^XA\n\r^MNM\n\r^FO050,50\n\r^B8N,100,Y,N\n\r^FD1234567\n\r^FS\n\r^PQ3\n\r^XZ";
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
Doc doc = new SimpleDoc(commands.getBytes(), flavor, null);
job.print(doc, null);
The disadvantage is that you need to learn ZPL - Zebra Programming Language. Although it is simple enough, but such things as images and custom fonts could make you headache. Programming manuals are freely available on Zebra site: Part 1 and Part 2.
Not every Zebra printer has ZPL II, but then you can use EPL
EPL2 programming guide for Zebra.pdf (Link in comment section)
Code sample:
private static boolean printLabel(PrintService printService, String label) {
if (printService == null || label == null) {
System.err.println("[Print Label] print service or label is invalid.");
return false;
}
String czas = new SimpleDateFormat("d MMMMM yyyy'r.' HH:mm s's.'").format(new Date());
String command =
"N\n"+
"A50,50,0,2,2,2,N,\""+label+"\"\n"+
"B50,100,0,1,2,2,170,B,\""+label+"\"\n"+
"A50,310,0,3,1,1,N,\""+czas+"\"\n"+
"P1\n"
;
byte[] data;
data = command.getBytes(StandardCharsets.US_ASCII);
Doc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
boolean result = false;
try {
printService.createPrintJob().print(doc, null);
result = true;
} catch (PrintException e) {
e.printStackTrace();
}
return result;
}

Print job submitted to printer, but didn't print anything. Java

I have problem with Java print service. I need to print a simple text document, to my default printer. I use HP Deskjet as my printer on Windows machine, all driver installed. This is the source code I use:
import java.io.*;
import javax.print.*;
public class PrintTest {
public static void main(String[] args) throws IOException {
File file = new File("print.txt");
InputStream is = new BufferedInputStream(new FileInputStream(file));
//Discover the default print service.
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
//Doc flavor specifies the output format of the file.
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
// Create the print job
DocPrintJob job = service.createPrintJob();
//Create the Doc
Doc doc = new SimpleDoc(is, flavor, null);
//Order to print
try {
job.print(doc, null);
} catch (PrintException e) {
e.printStackTrace();
}
is.close();
System.out.println("Printing done....");
}
}
I can see the print job on printer queue for several milisecond before its gone. But nothing get printed. I have heard it's because Java Print Service in JDK 1.6 is still buggy. But I'm not entirely sure. Any ideas why?
I know it is a very late answer but I had the same problem on Windows with PDFs (not text). It seems that printers may not be able to deal with native PDFs, so the job is accepted but nothing happens (no error too). I solved this by using a third-party lib, Apache PdfBox, and it worked like a charm.
I wrote some code example by answering a similar question https://stackoverflow.com/a/39271053/935039.

Unable to print the document using Java printer Services(JPS)

I have implemented a program, to print the document to the specific printer using IP address, printer name and running fine with out any errors and exception. A printer job is being sent from java, I am able to see this on my local printer print pool, but the page is not printing on printer.
URI myURI=null;
FileInputStream psStream=null;
try {
psStream = new FileInputStream("sample.doc");
}
catch ( FileNotFoundException e ) {
e.printStackTrace();
}
DocFlavor psInFormat = DocFlavor.BYTE_ARRAY.GIF;
Doc myDoc = new SimpleDoc( psStream, psInFormat, null );
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
PrintService[] services = PrintServiceLookup.lookupPrintServices( psInFormat, aset);
if ( services.length > 0 ) {
DocPrintJob job = services[0].createPrintJob();
try {
job.print( myDoc, aset);
}
catch ( PrintException e ){
}
}
Could you please help me out on this?
Thanks,
Srikanth Chilukuri
I found the problem.
psStream = new FileInputStream("sample.doc");
The above statement creating problem. Because It is MS Word Application, So unable to read the file using File Input Stream.
I am using POI jar and reading the doc.
POIFSFileSystem psStream = new POIFSFileSystem(new FileInputStream(filesname));
Doc myDoc = new SimpleDoc( psStream, psInFormat, null );
But Doc API is not supporing got IllegalArgumentException
Exception in thread "Main Thread" java.lang.IllegalArgumentException: data is not of declared type
at javax.print.SimpleDoc.<init>(SimpleDoc.java:82)
at com.src.print.TestPrint2.main(TestPrint2.java:67)
Could you please help me out on this.
Java Print Services allow you to "draw" pages like Swing and then send the result to a printer. It does not have knowledge of Word formats or HTML pages or similar.
Therefore you need a module which knows how to draw the contents of a doc-file to a printer to do this. I do not personally have experience with such a module.
Thanks for your response
I guess you are talking about the AWT print. This is different from Java Print Service. You can have have preformatted text data printed using a variety of options using Java Print Service(JPS)
http://docs.oracle.com/javase/1.5.0/docs/guide/jps/spec/docflavor.fm1.html#998469
The problem comes in when the file is encoded using UTF-8 and you try to print this using JPS
if its a normal ASCII file. it gets printed correctly

Is there a good way to persist printer settings in a Swing app?

We are using the new Java printing API which uses PrinterJob.printDialog(attributes) to display the dialog to the user.
Wanting to save the user's settings for the next time, I wanted to do this:
PrintRequestAttributeSet attributes = loadAttributesFromPreferences();
if (printJob.printDialog(attributes)) {
// print, and then...
saveAttributesToPreferences(attributes);
}
However, what I found by doing this is that sometimes (I haven't figured out how, yet) the attributes get some bad data inside, and then when you print, you get a white page of nothing. Then the code saves the poisoned settings into the preferences, and all subsequent print runs get poisoned settings too. Additionally, the entire point of the exercise, making the settings for the new run the same as the user chose for the previous run, is defeated, because the new dialog does not appear to use the old settings.
So I would like to know if there is a proper way to do this. Surely Sun didn't intend that users have to select the printer, page size, orientation and margin settings every time the application starts up.
Edit to show the implementation of the storage methods:
private PrintRequestAttributeSet loadAttributesFromPreferences()
{
PrintRequestAttributeSet attributes = null;
byte[] marshaledAttributes = preferences.getByteArray(PRINT_REQUEST_ATTRIBUTES_KEY, null);
if (marshaledAttributes != null)
{
try
{
#SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
ObjectInput objectInput = new ObjectInputStream(new ByteArrayInputStream(marshaledAttributes));
attributes = (PrintRequestAttributeSet) objectInput.readObject();
}
catch (IOException e)
{
// Can occur due to invalid object data e.g. InvalidClassException, StreamCorruptedException
Logger.getLogger(getClass()).warn("Error trying to read print attributes from preferences", e);
}
catch (ClassNotFoundException e)
{
Logger.getLogger(getClass()).warn("Class not found trying to read print attributes from preferences", e);
}
}
if (attributes == null)
{
attributes = new HashPrintRequestAttributeSet();
}
return attributes;
}
private void saveAttributesToPreferences(PrintRequestAttributeSet attributes)
{
ByteArrayOutputStream storage = new ByteArrayOutputStream();
try
{
ObjectOutput objectOutput = new ObjectOutputStream(storage);
try
{
objectOutput.writeObject(attributes);
}
finally
{
objectOutput.close(); // side-effect of flushing the underlying stream
}
}
catch (IOException e)
{
throw new IllegalStateException("I/O error writing to a stream going to a byte array", e);
}
preferences.putByteArray(PRINT_REQUEST_ATTRIBUTES_KEY, storage.toByteArray());
}
Edit: Okay, it seems like the reason it isn't remembering the printer is that it isn't in the PrintRequestAttributeSet at all. Indeed, the margins and page sizes are remembered, at least until the settings get poisoned at random. But the printer chosen by the user is not here:
[0] = {java.util.HashMap$Entry#9494} class javax.print.attribute.standard.Media -> na-letter
[1] = {java.util.HashMap$Entry#9501} class javax.print.attribute.standard.Copies -> 1
[2] = {java.util.HashMap$Entry#9510} class javax.print.attribute.standard.MediaPrintableArea -> (10.0,10.0)->(195.9,259.4)mm
[3] = {java.util.HashMap$Entry#9519} class javax.print.attribute.standard.OrientationRequested -> portrait
It appears that what you're looking for is the PrintServiceAttributeSet, rather than the PrintRequestAttributeSet.
Take a look at the PrintServiceAttribute interface, and see if the elements you need have been implemented as classes. If not, you can implement your own PrintServiceAttribute class(es).

Categories

Resources