For anyone dealing with this problem here in 2020, I found a pretty slick way of printing PDF files while being able to monitor the print job, without using JavaFX - as people don't seem to be too hip on using JavaFX for printing PDF files, and I'm not to keen on object node printing, as it is really diffiult to get the print out to look like a normal document in my opinion ... JavaFX seems to like the idea of - basically - screen shotting a portion of your form, then rendering that snap shot as a graphic, then you have to scale it for the printer and it just ends up looking kinda weird ... whereas taking something like nicely formatted HTML, and printing it through a PDF library is really clean, and really fast. So here is what I found:
First, I used this library, which is free, to render my HTML String into a PDF file. Here is the Maven source for the library:
<dependency>
<groupId>org.icepdf.os</groupId>
<artifactId>icepdf-core</artifactId>
<version>6.2.2</version>
</dependency>
And here is my code for rendering the PDF:
String docPath = "/Users/michael/Documents/JavaFile.pdf";
String html = "<html>Any HTML Code you want, including embedded CSS for a really clean look</html>
OutputStream outputStream = new FileOutputStream(docPath);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(outputStream);
outputStream.close();
And here is my class for printing the PDF ... its a little lengthy, but VERY simple, and it uses native Java API which is nice (i'm using 1.8):
import javafx.application.Platform;
import javafx.scene.control.Label;
import java.io.*;
import javax.print.*;
import javax.print.event.PrintJobAdapter;
import javax.print.event.PrintJobEvent;
public class PrintHandler {
private void delay(int msec) {
try {
Thread.sleep(msec);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public PrintHandler (FileInputStream fis, Label statusLabel) {
this.statusLabel = statusLabel;
this.fis = fis;
}
private FileInputStream fis;
private Label statusLabel;
private String state;
public void startPrintJob () {
try {
Platform.runLater(()->statusLabel.setText("PRINTING"));
delay(5000);
InputStream is = new BufferedInputStream(this.fis);
DocFlavor flavor = DocFlavor.INPUT_STREAM.PDF;
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocPrintJob printJob = service.createPrintJob();
JobMonitor monitor = new JobMonitor();
printJob.addPrintJobListener(monitor);
Doc doc = new SimpleDoc(is, flavor, null);
printJob.print(doc, null);
monitor.waitForJobCompletion();
is.close();
} catch (PrintException | IOException e) {
e.printStackTrace();
}
}
private class JobMonitor extends PrintJobAdapter {
private boolean notify = false;
final int DATA_TRANSFERRED = 10;
final int JOB_COMPLETE = 11;
final int JOB_FAILED = 12;
final int JOB_CANCELED = 13;
final int JOB_NO_MORE_EVENTS = 14;
final int JOB_NEEDS_ATTENTION = 15;
private int status;
@Override
public void printDataTransferCompleted(PrintJobEvent pje) {
status = DATA_TRANSFERRED;
markAction();
}
@Override
public void printJobCompleted(PrintJobEvent pje) {
status = JOB_COMPLETE;
markAction();
}
@Override
public void printJobFailed(PrintJobEvent pje) {
status = JOB_FAILED;
markAction();
}
@Override
public void printJobCanceled(PrintJobEvent pje) {
status = JOB_CANCELED;
markAction();
}
@Override
public void printJobNoMoreEvents(PrintJobEvent pje) {
status = JOB_NO_MORE_EVENTS;
markAction();
}
@Override
public void printJobRequiresAttention(PrintJobEvent pje) {
status = JOB_NEEDS_ATTENTION;
markAction();
}
private void markAction() {
synchronized (JobMonitor.this) {
notify = true;
JobMonitor.this.notify();
}
}
public synchronized void waitForJobCompletion() {
Runnable runner = ()->{
boolean keepRunning = true;
while (keepRunning) {
try {
while (!notify) {
wait();
}
switch(this.status){
case DATA_TRANSFERRED:
state = "DATA_TRANSFERRED";
break;
case JOB_COMPLETE:
state = "JOB_FINISHED";
keepRunning = false;
break;
case JOB_FAILED:
state = "JOB_FAILED";
keepRunning = false;
break;
case JOB_CANCELED:
state = "JOB_CANCELED";
keepRunning = false;
break;
case JOB_NO_MORE_EVENTS:
state = "JOB_COMPLETE";
keepRunning = false;
break;
case JOB_NEEDS_ATTENTION:
state = "JOB_NEEDS_ATTENTION";
break;
}
Platform.runLater(()->statusLabel.setText(state));
delay(5000);
notify = false;
}
catch (InterruptedException e) {}
}
delay(5000);
Platform.runLater(()->statusLabel.setText(""));
};
Thread monitor = new Thread(runner);
monitor.start();
}
}
}
And here is how I invoke the class to print and monitor the job:
FileInputStream fis = new FileInputStream(docPath);
Label jobStatus = new Label(); //Already in my AnchorPane but included here for clarity
new PrintHandler(fis,jobStatus).startPrintJob();