0

I have object of javafx.scene.image.Image class. How can I print it on printer using javafx8? Please, note, that I don't want to print some node, for example ImageView. I need to print image. Although it's very simple question I can't find answer in internet. The only code I found is:

PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
boolean success = job.printPage(node);
if (success) {
job.endJob();
}
}

However it is about printing the node.

Pavel_K
  • 10,748
  • 13
  • 73
  • 186

1 Answers1

2

Problem

javafx.print.PrinterJob only prints Node and it's subclasses. Image isn't a subclass of Node. So you have to wrap it in a Node (ImageView) or print from plain Java.

Difference of JavaFX-PrinterJob and AWT-PrinterJob

The main difference is, that the JavaFX PrinterJob was introduced for usage with Node objects. It has set some usefull things as a JavaFX Property like the Jobstatus or the Printer itself. And it is more thread safe as the older AWT PrinterJob. The AWT PrinterJob can print mostly anything you want like Strings, Images, Arc's, etc., because it takes an AWT Graphics Object to draw things on the page.

Solution

Before you can use the plain Java solution, you have to convert your FX-Image to a BufferedImage with SwingFXUtils.fromFXImage(). But there is a bug with *.jpg Files, as described here: https://stackoverflow.com/a/30995307/4170073

The Minimal, Complete, and Verifiable example down below shows a working solution:

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ImagePrinter extends Application {

  @Override
  public void start(Stage primaryStage) {

    Image image = new Image("http://www.gnu.org/graphics/gnu-head.png");
    BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);

    Button btn = new Button();
    btn.setText("Print Image");
    btn.setOnAction(new EventHandler<ActionEvent>() {

      @Override
      public void handle(ActionEvent event) {
        printImage(bufferedImage);
      }
    });

    StackPane root = new StackPane();
    root.getChildren().add(btn);

    Scene scene = new Scene(root, 300, 250);

    primaryStage.setTitle("Image Printer");
    primaryStage.setScene(scene);
    primaryStage.show();
  }

  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    launch(args);
  }

  private void printImage(BufferedImage image) {
    PrinterJob printJob = PrinterJob.getPrinterJob();
    printJob.setPrintable(new Printable() {
      @Override
      public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
        // Get the upper left corner that it printable
        int x = (int) Math.ceil(pageFormat.getImageableX());
        int y = (int) Math.ceil(pageFormat.getImageableY());
        if (pageIndex != 0) {
          return NO_SUCH_PAGE;
        }
        graphics.drawImage(image, x, y, image.getWidth(), image.getHeight(), null);
        return PAGE_EXISTS;
      }
    });
    try {
      printJob.print();
    } catch (PrinterException e1) {
      e1.printStackTrace();
    }
  }
}
Community
  • 1
  • 1
aw-think
  • 4,723
  • 2
  • 21
  • 42
  • Could you explain the difference - printing from plain java and printing from javafx? – Pavel_K Jun 28 '15 at 16:52
  • Thank you very much. But a little comment javafx printing is also used for printing content (not node) of webview. – Pavel_K Jun 28 '15 at 17:51
  • @JimJim2000 [WebView](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebView.html) extends Parent. Parent extends Node :-) So WebView is a Node. – aw-think Jun 28 '15 at 17:56
  • No, you don't understand me. WebView is a node. But I am speaking about webView.getEngine().print(job) -> this is the RIGHT way to print the CONTENT of webview – Pavel_K Jun 28 '15 at 17:59
  • @JimJim2000 Ah, now I know what you mean. But you have to give it a PrinterJob Object, than the method creates a `final class Printable extends Node` to do the printing. So you see, it only prints Nodes, but it does it with an WCGraphicsContext. This is an old class from sun. – aw-think Jun 28 '15 at 18:02
  • Maybe you are right - I didn't dig so deep. Maybe you can solve this problem with webview printing - I also have it - http://stackoverflow.com/questions/30839474/javafx-webview-print-with-fit-to-size-100 – Pavel_K Jun 28 '15 at 18:08
  • @JimJim2000 I don't exactly understand where the problem is? Scaling? The printed page doesn't look as it should? Too small or not width enough? – aw-think Jun 28 '15 at 18:14
  • Look, when you set units in html code in "pt" you must get certain sizes on paper. For example 475pt=16.8cm on paper. I have html page and div with with 475pt. I print it in IE (scale=100%) and I get 16.8cm on paper. However when I print this html page in webview (getEntine.print()) I get this div 11.4cm on paper (although it must be 16.8). WebView changes the scale. – Pavel_K Jun 28 '15 at 18:22
  • WebView is internaly a WebKit based Browser. Have you tried to print it with a WebKit based Browser? Here is a list: https://en.wikipedia.org/wiki/List_of_web_browsers#WebKit-based – aw-think Jun 28 '15 at 18:28
  • No, I have no one of them. – Pavel_K Jun 28 '15 at 18:33