i require to send a pdf document to print on the server side of a web app, the printer fully supports pdf printing etc, it is networked as well to the server. The pdf is also stored on the server.
what i am trying to is on a button click, print out the pdf file, currently i have the code below :
//Server side printing
public class PrintDocument {
public void printText(String text) throws PrintException, IOException {
//Looks for all printers
//PrintService[] printServices = PrinterJob.lookupPrintServices();
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
InputStream is = new ByteArrayInputStream(text.getBytes("UTF8"));
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
pras.add(new Copies(1));
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
Doc doc = new SimpleDoc(is, flavor, null);
DocPrintJob job = service.createPrintJob();
PrintJobWatcher pjw = new PrintJobWatcher(job);
job.print(doc, pras);
pjw.waitForDone();
is.close();
}
}
class PrintJobWatcher {
boolean done = false;
PrintJobWatcher(DocPrintJob job) {
job.addPrintJobListener(new PrintJobAdapter() {
public void printJobCanceled(PrintJobEvent pje) {
allDone();
}
public void printJobCompleted(PrintJobEvent pje) {
allDone();
}
public void printJobFailed(PrintJobEvent pje) {
allDone();
}
public void printJobNoMoreEvents(PrintJobEvent pje) {
allDone();
}
void allDone() {
synchronized (PrintJobWatcher.this) {
done = true;
System.out.println("Printing has successfully completed, please collect your prints)");
PrintJobWatcher.this.notify();
}
}
});
}
public synchronized void waitForDone() {
try {
while (!done) {
wait();
}
} catch (InterruptedException e) {
}
}
}
But i have a few questions / issues, how do i put the pdf into the input stream for this to be printed out, can i select options such as duplex printing, and how can i call this from inside a JSF web app
Thanks
According to this article it should be possible to start a print job with a PJL block (Wikipedia link includes pointers to the PJL reference documentation), followed by the PDF data.
Thank to PJL you should be able to control all features the printer has to offer including duplex, etc - the blog article even mentions stapling of a combined printout of 2 pdfs.
Be sure to read the comments on the article as well, there is a comment from the guy who's listed as inventor on the patent as well with extra information on the PJL commands.
Take a look at this blog. We had to print documents with duplex print option.
Its not possible to duplex print directly in java. However the work around is to use ghostscript and convert PDF to PS (Post script file). To that you can add either PJL Commands or Post script commands.
More info at
http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html
Also read similar question
Printing with Attributes(Tray Control, Duplex, etc...) using javax.print library
After reading through this Q&A I spent awhile working with the javax.print library only to discover that it is not very consistent with printer option support. I.e. even if a printer has an option like stapling, the javax.printer library showed it as "stapling not supported".
So I then tried out PJL commands using a plain java socket and it worked great, in my tests it also printed faster than the javax.print library, it has a much smaller code footprint and best part is no libraries are needed at all:
private static void print(File document, String printerIpAddress)
{
try (Socket socket = new Socket(printerIpAddress, 9100))
{
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
String title = document.getName();
byte[] bytes = Files.readAllBytes(document.toPath());
out.write(27);
out.write("%-12345X#PJL\n".getBytes());
out.write(("#PJL SET JOBNAME=" + title + "\n").getBytes());
out.write("#PJL SET DUPLEX=ON\n".getBytes());
out.write("#PJL SET STAPLEOPTION=ONE\n".getBytes());
out.write("#PJL ENTER LANGUAGE=PDF\n".getBytes());
out.write(bytes);
out.write(27);
out.write("%-12345X".getBytes());
out.flush();
out.close();
}
catch (Exception e)
{
System.out.println(e);
}
}
See this for more info on attempts with javax.print.
Related
I want to print my file directly from webpage. For that I am using following reference and trying to implement same with ZUL and Composer.
http://tonny-bruckers.blogspot.in/2012/11/printing-files-directly-from-web-page.html
ZUL File :-
<zk>
<applet code = "PrintApplet.class" codebase = "applet/" id="printApplet" width="400px" style="border: 1px" />
<button id="btnClickMe" label="Click Me" sclass="light-btn"/>
</zk>
PrintApplet.class is present inside "WebContent/applet".
public class AppletComposer extends GenericForwardComposer<Window> {
private Applet printApplet;
public void doOverrideAfterComposer(Window comp) throws Exception {
}
public void onClick$btnClickMe(Event event) throws Exception {
String Originalstr = "ByteArrayInputStream Example!";
byte[] Originalbytes = Originalstr.getBytes();
ByteArrayInputStream bis=new ByteArrayInputStream(Originalbytes);
printApplet.invoke("print", bis);
}
}
PrintApplet Class :-
public class PrintApplet extends Applet {
public void init()
{
}
public void print(ByteArrayInputStream bis) throws PrintException
{
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
if (service != null) {
DocFlavor psFormat = DocFlavor.INPUT_STREAM.PDF;
PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
DocPrintJob job = service.createPrintJob();
Doc pdfDoc = new SimpleDoc(bis,psFormat, null);
job.print(pdfDoc, attributes);
}
}
}
I am able to invoke PrintApplet with this approach but getting Null as service. PrintApplet is working fine with AppletViewer and with normal Java Application but unable to get default printer service while using the above approach.
First I want to mention that APPLET IS ALWAYS RUN ON CLIENT SIDE and APPLET only communicates to the server which from it is downloaded.
That’s why we have to specify codebase directory, so that we can download the applet on client browser by then JAVA Platform Environment plugin from browser takes on control, which in turn run on client JRE environment.
So we have to be very carefully that JDK environment is installed properly.
To trace applet log we can use of java applet console tool ‘jconsole’.
Steps for APPLET to run properly on client browser :
At browser (firefox,chrome,opera) check whether JAVA Platform plugin is there or not, because to run applet from the browser we require that plugin installed and enabled.
If you are working on linux machine: than it is bit complex,
You can find how to enable plugin for LINUX-BROWSER from here:
http://www.oracle.com/technetwork/java/javase/manual-plugin-install-linux-136395.html
Enable console log for applet, when it executes on client JRE, we can look into it for tracing.
Path : JDK_DIR/bin/jcontrol
[JControl Window][1]
For only development purpose: you can lower the security
We have to clear the cache of applet, every time we build new applet, to reflect the latest change we need to clear the cache first, otherwise it'll load cached applet class.
To clear we can use 'javaws -viewer'
Path : JAVA_HOME/bin/javaws -viewer
[Clear Applet Cache][2]
As per your code, your server side code (zul and composer) are perfect but the problem is at applet code.
You are looking for a default printer in print() method, which are one time configuration codes. It has to be in init().
PrintApplet.java
public class PrintApplet extends Applet {
private static final long serialVersionUID = 1L;
private PrintService service;
public void init()
{
JOptionPane.showMessageDialog(null, "Inside INIT()");
if(null==service){
service = PrintServiceLookup.lookupDefaultPrintService();
System.out.println(service.getName());
} else {
System.out.println(service.getName());
}
}
public void print(String str) throws PrintException
{
JOptionPane.showMessageDialog(null, "Inside print()");
JOptionPane.showMessageDialog(null, "String is:::"+str);
cPrint cP = new cPrint(str, service);
System.out.println((Integer) AccessController.doPrivileged(cP));
}
}
And you need another implementation of AccessController to give access to the default printer locate and print.
cPrint.java
class cPrint implements PrivilegedAction<Object> {
String str;
PrintService service;
public cPrint(String str, PrintService argPrintService) {
this.str = str;
this.service = argPrintService;
};
public Object run() {
// privileged code goes here
InputStream is = null;
try
{
JOptionPane.showMessageDialog(null, "String is:::"+str);
byte[] Originalbytes = str.getBytes();
JOptionPane.showMessageDialog(null, "Original bytes:::"+Originalbytes);
is=new ByteArrayInputStream(Originalbytes);
FileWriter fstream = new FileWriter("/home/test/out.pdf");
BufferedWriter out = new BufferedWriter(fstream);
for (Byte b: Originalbytes) {
out.write(b);
}
out.close();
DocPrintJob printJob = service.createPrintJob();
Doc doc;
DocAttributeSet docAttrSet = new HashDocAttributeSet();
PrintRequestAttributeSet printReqAttr = new HashPrintRequestAttributeSet();
doc = new SimpleDoc(is, DocFlavor.INPUT_STREAM.AUTOSENSE, docAttrSet);
PrintJobWatcher pjDone = new PrintJobWatcher(printJob);
printJob.print(doc, printReqAttr);
pjDone.waitForDone();
// It is now safe to close the input stream
is.close();
}
catch (Exception e) {
e.printStackTrace();
System.out.println(e);
return 1;
}
return 0;
}
static class PrintJobWatcher {
// true iff it is safe to close the print job's input stream
boolean done = false;
PrintJobWatcher(DocPrintJob job) {
// Add a listener to the print job
job.addPrintJobListener(new PrintJobAdapter() {
public void printJobCanceled(PrintJobEvent pje) {
allDone();
}
public void printJobCompleted(PrintJobEvent pje) {
allDone();
}
public void printJobFailed(PrintJobEvent pje) {
allDone();
}
public void printJobNoMoreEvents(PrintJobEvent pje) {
allDone();
}
void allDone() {
synchronized (PrintJobWatcher.this) {
done = true;
PrintJobWatcher.this.notify();
}
}
});
}
public synchronized void waitForDone() {
try {
while (!done) {
wait();
}
} catch (InterruptedException e) {
}
}
}
}
cPrint(str,PrintService)
Where str can be file name if you want file to be print, or byte array string.
Here in my example, I expected byte array, so I create pdf file from byte array given by the applet from the composer and then it'll sent to the default printer to the given PrintService.
So Actual flow for applet in zk to get access for default printer and to print is by this [graph][3].
I have been trying to use a thermal-printer "Bixolon SRP-F310" and print some text using JAVA's PrintService. Printer is detected and there is no exception while calling the print function. I can see in the web interface of Cups that the print event is called. However the printer doesn't print and the error message "No pages found!" can be seen in the web-interface of Cups. Any help will be appreciated. I have included the screenshot of the Cups web-interface and the error logs.
import javax.print.*;
import java.util.Arrays;
import java.util.List;
public class Printer {
static Printer INSTANCE;
public static void main(String[] args) {
INSTANCE = new Printer();
List<PrintService> services = INSTANCE.getServicesByName("BIXOLON_SRP-F310");
if(services == null) {
throw new RuntimeException("No printer services available");
}
INSTANCE.printServices(services);
try {
INSTANCE.print(services.get(0), "Hello");
} catch (Exception e) {
e.printStackTrace();
}
}
public List<PrintService> getServicesByName(String serviceName) {
//Find printer service by name
AttributeSet aset = new HashAttributeSet();
aset.add(new PrinterName(serviceName, null));
return Arrays.asList(PrintServiceLookup.lookupPrintServices(null, aset));
}
public void print(PrintService service, String printData) throws Exception {
if(service == null) {
throw new Exception("Service is not valid");
}
if(printData == null) {
throw new Exception("Nothing to print");
}
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
pras.add(new Copies(1));
pras.add(new PrinterResolution(180,180,PrinterResolution.DPI));
DocPrintJob job = service.createPrintJob();
DocAttributeSet das = new HashDocAttributeSet();
das.add(new PrinterResolution(180,180,PrinterResolution.DPI));
byte[] desc = printData.getBytes();
Doc doc = new SimpleDoc(desc, DocFlavor.BYTE_ARRAY.AUTOSENSE, das);
try {
job.print(doc, pras);
} catch (Exception e) {
e.printStackTrace();
}
}
public void printServices(List<PrintService> services) {
System.out.println("Printer Services found:");
for (PrintService service : services) {
System.out.println("\t" + service);
}
}
}
Web Interface of Cups:
Error Logs:
http://pastebin.com/kYiKGsSn
Do the following steps and Hopefully your problem will be solved.
Check the IP of your printer if it's same which you are hitting through CUPs then fine otherwise you have to reset the IP.
Reset IP: Press the feed button of your thermal printer for 2-3 min a long print receipt will print with detail info about the printer.
Now just attache your printer with LAN cable to your PC and open the printer settings. Here you can reset the printer IP according to the manual of that particulate printer.
After setting the IP now try again from the server to hit on that thermal printer with new IP.
If your CUPS is installed properly then it will work and otherwise you have to check the CUPS.
Check all these things and let me know is that works or any error message.
I'm facing the very same problem as you did. You may try to setup your page size and format. Try to do that.
You can as well do a simple troubleshoot such as using another printer. If things goes OK, it is safe to assume that there's nothing wrong with your code, but the printer driver you are currently using might be causing the problem.
I tried to use PDFBox on regular .pdf files and it worked fine.
However when I encountered a corrupted .pdf , the code would "freeze" .. not throwing errors or something .. simply the load or parse function take forever to execute
Here is the corrupted file (i have zipped it so that everybody could download it), it is probably not a native pdf file but it was saved as a .pdf extension and it is only 4 Kb.
I am not an expert at all, but I think that this is a bug with PDFBox. According to documentation, both load() and parse() methods are supposed to throw exceptions if they fail. However in case with my file, the code would take forever to execute and not throw exception.
I tried using only load, one can try parse() .. the result is the same
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.PDFTextStripper;
public class TestTest {
public static void main(String[] args) throws FileNotFoundException, IOException {
System.out.println(pdfToText("C:\\..............MYFILE.pdf"));
System.out.println("done ! ! !");
}
private static String pdfToText(String fileName) throws IOException {
PDDocument document = null;
document = PDDocument.load(new File(fileName)); // THIS TAKES FOREVER
PDFTextStripper stripper = new PDFTextStripper();
document.close();
return stripper.getText(document);
}
}
How to force this code throw an exception or stop executing if the .pdf file is corrupted?
Thanks
Try this solution:
private static String pdfToText(String fileName) {
PDDocument document = null;
try {
document = PDDocument.load(fileName);
PDFTextStripper stripper = new PDFTextStripper();
return stripper.getText(document);
} catch (IOException e) {
System.err.println("Unable to open PDF Parser. " + e.getMessage());
return null;
} finally {
if (document != null) {
try {
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
For implementing simple timeouts for 3rd party libs I often use an implementation like Apache Commons ThreadMonitor:
long timeoutInMillis = 1000;
try {
Thread monitor = ThreadMonitor.start(timeoutInMillis);
// do some work here
ThreadMonitor.stop(monitor);
} catch (InterruptedException e) {
// timed amount was reached
}
Example code is from Apache's ThreadMonitor Javadoc.
I only use this when the 3rd party API does not provide some timeout mechanism, of course.
However I was forced to tweak this a bit some weeks ago, because this solution does not work well with (3rd party) code that is using Exception masking.
In particular we run into problems with c3p0 which masks all Exceptions (and in particular InterruptedExceptions). Our solution was to tweak the implementation to also check the exception's cause chain for InterruptedExceptions.
This example is based on an example from the book Restlet in Action.
If I try
public class StreamResource extends ServerResource
{
#Get
public Representation getStream() throws ResourceException, IOException
{
Representation representation = new WriterRepresentation(MediaType.TEXT_PLAIN)
{
#Override
public void write(Writer writer) throws IOException
{
String json = "{\"foo\" : \"bar\"}";
while (true)
{
writer.write(json);
}
}
};
return representation;
}
}
it works and it continuously sends the json string to the client.
If I introduce a delay in the while loop like this
String json = "{\"foo\" : \"bar\"}\r\n";
while (true)
{
writer.write(json);
try
{
Thread.sleep(250);
}
catch (InterruptedException e)
{}
}
I was hoping that the client would get data 4 times in a second BUT nothing seems to get to the client.
Can anyone explain why the introduction of Thread.sleep() does that? What is a good way to introduce delay in streaming data to the client?
You should try with the Jetty connector instead of the internal Restlet connector. This connector isn't ready for production even though we are working on fixing it.
You can also try the Simple extension which has less dependent JARs than the Jetty extension.
You can try to flush the buffer, like this:
String json = "{\"foo\" : \"bar\"}\r\n";
while (true)
{
writer.write(json);
writer.flush(); // flush the buffer.
try
{
Thread.sleep(250);
}
catch (InterruptedException e)
{}
}
Without writer.flush(), the writer waits to fill the internal buffer before writing the socket. Thread.sleep(250) reduces the output produced at each second, so that far more time is required to fill the buffer.
I need to know the Printer Status. I need to control the Printer Status using Java Program.
Example
Check the Printer status, weather will it accept the Job or not,
Out of Paper
Printer queue
Toner
and etc..
I know there is a way to check the basic information, such as name, color supported or not. But I can't find any example to check paper, toner, job queue. I like to know if it is possible to using Java API. I found big API for printer function, but they didn't give a simple example how to use it.
Have a look at this PrinterStateReason. And also javax.print.
Getting the complete status of a printer is not possible. Printers have a native driver which is able to request services but because there are so many possible printer functionalities, Java only supports a subset of it.
You can actually offer the user to modify the status by calling
PrinterJob pj = PrinterJob.getPrinterJob();
pj.printDialog()
which shows the native printer dialog.
Despite the information in the javax.print API that it is possible to check the printer state, I was not able to do so for my printer !. (Canon).
Code to check:
import javax.print.*;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.PrintServiceAttributeSet;
import javax.print.attribute.standard.PrinterStateReason;
import javax.print.attribute.standard.PrinterStateReasons;
import javax.print.attribute.standard.Severity;
import javax.print.event.*;
import java.awt.*;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.*;
import java.util.Set;
/**
* PrintTest
*/
public class PrintTest implements PrintServiceAttributeListener,PrintJobListener,Doc, Printable, PrintJobAttributeListener {
private static final transient String TEXT = "12345";
public static void main(String[] args) {
PrintTest test = new PrintTest();
test.checkPrinters();
}
public void checkPrinters() {
Thread newThread = new Thread(new Runnable() {
public void run() {
PrintService ps = PrinterJob.getPrinterJob().getPrintService();
DocFlavor[] myFlavors = ps.getSupportedDocFlavors();
ps.addPrintServiceAttributeListener(PrintTest.this);
DocPrintJob docJob = ps.createPrintJob();
docJob.addPrintJobAttributeListener(PrintTest.this, null);
docJob.addPrintJobListener(PrintTest.this);
try {
docJob.print(PrintTest.this,null);
}
catch (PrintException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
} });
newThread.start();
/**
PrintServiceAttributeSet attSet = ps.getAttributes();
PrinterStateReasons psr = ps.getAttribute(PrinterStateReasons.class);
if (psr != null) {
Set<PrinterStateReason> errors = psr.printerStateReasonSet(Severity.REPORT);
for (PrinterStateReason reason : errors)
System.out.printf(" Reason : %s",reason.getName());
System.out.println();
} */
}
public void attributeUpdate(PrintServiceAttributeEvent psae) {
System.out.println(psae.getAttributes());
}
public void printDataTransferCompleted(PrintJobEvent pje) {
System.out.println("Transfer completed");
}
public void printJobCompleted(PrintJobEvent pje) {
System.out.println("Completed");
}
public void printJobFailed(PrintJobEvent pje) {
System.out.println("Failed");
PrinterStateReasons psr = pje.getPrintJob().getPrintService().getAttribute(PrinterStateReasons.class);
if (psr != null) {
Set<PrinterStateReason> errors = psr.printerStateReasonSet(Severity.REPORT);
for (PrinterStateReason reason : errors)
System.out.printf(" Reason : %s",reason.getName());
System.out.println();
}
}
public void printJobCanceled(PrintJobEvent pje) {
System.out.println("Canceled");
}
public void printJobNoMoreEvents(PrintJobEvent pje) {
System.out.println("No more events");
}
public void printJobRequiresAttention(PrintJobEvent pje) {
System.out.println("Job requires attention");
PrinterStateReasons psr = pje.getPrintJob().getPrintService().getAttribute(PrinterStateReasons.class);
if (psr != null) {
Set<PrinterStateReason> errors = psr.printerStateReasonSet(Severity.REPORT);
for (PrinterStateReason reason : errors)
System.out.printf(" Reason : %s",reason.getName());
System.out.println();
}
}
public DocFlavor getDocFlavor() {
return DocFlavor.SERVICE_FORMATTED.PRINTABLE; //To change body of implemented methods use File | Settings | File Templates.
}
public Object getPrintData() throws IOException {
return this;
}
public DocAttributeSet getAttributes() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public Reader getReaderForText() throws IOException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public InputStream getStreamForBytes() throws IOException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
return pageIndex == 0 ? PAGE_EXISTS : NO_SUCH_PAGE; //To change body of implemented methods use File | Settings | File Templates.
}
public void attributeUpdate(PrintJobAttributeEvent pjae) {
System.out.println("Look out");
}
}
I have tried to get a PrinterReasonsState by willfully opening the case or removing the paper, but I was unsuccessfull. Perhaps someone else can show how it is possible, but so far it seems that the API offers much more functionality which is in reality not available.
Or in short:
It does not work, at least not for my printer.
I was told one could check the printer status this way:
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
AttributeSet attributes = printService.getAttributes();
String printerState = attributes.get(PrinterState.class).toString();
String printerStateReason = attributes.get(PrinterStateReason.class).toString();
System.out.println("printerState = " + printerState); // May be IDLE, PROCESSING, STOPPED or UNKNOWN
System.out.println("printerStateReason = " + printerStateReason); // If your printer state returns STOPPED, for example, you can identify the reason
if (printerState.equals(PrinterState.STOPPED.toString()) {
if (printerStateReason.equals(PrinterStateReason.TONER_LOW.toString()) {
System.out.println("Toner level is low.");
}
}
Sadly it seems that my printer doesn't have support for printerState so I can't test it.
UPDATE:
Instead of querying WMI "win32_printer" object I would recommend using Powershell directly like this, its much cleaner API :
Get-Printer | where PrinterStatus -like 'Normal' | fl
To see all the printers and statuses:
Get-Printer | fl Name, PrinterStatus
To see all the attributes:
Get-Printer | fl
You can still use ProcessBuilder in Java as described below.
Before update:
Solution for Windows only.
In Windows you can query WMI "win32_printer" class, so you check that the state on OS layer: Win32_Printer class
In Java you can use ProcessBuilder like this to start PowerShell and execute the PS script like this:
String printerName = "POS_PRINTER";
ProcessBuilder builder = new ProcessBuilder("powershell.exe", "get-wmiobject -class win32_printer | Select-Object Name, PrinterState, PrinterStatus | where {$_.Name -eq '"+printerName+"'}");
String fullStatus = null;
Process reg;
builder.redirectErrorStream(true);
try {
reg = builder.start();
fullStatus = getStringFromInputStream(reg.getInputStream());
reg.destroy();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.print(fullStatus);
After converting the InputStream to String you should get something like that:
Name PrinterState PrinterStatus
---- ------------ -------------
POS_PRINTER 0 3
State and Status should change for various situations (printer turned off, out of paper, cover opened,...).
This should work, but depends on the printer and drivers. I used this with EPSON TM printers with ESDPRT port and I could get information like: no paper, cover open, printer offline/turned off, printer paused.
More comprehensive answer here:
- my StackOverflow answer on a similar question.