Using the JavaFX printing API

You can use the API in the package javafx.print to print parts of the JavaFX scene graph. The main entry point for this capability is PrinterJob. This class provides a printPage() method, which takes a Node as input and generates a high-resolution printout of the content of that node. The rest of the package provides more supporting classes such as Printer and PageLayout.

This tutorial explains how to use this API to print a map displayed in a TLspFXView.

Printing a TLspFXView

You print a TLspFXView by first asking the view to generate an image with a resolution suitable for printing. Then, you pass this image on to the JavaFX printing API.

To this effect, the view provides a print() method, which returns a JavaFX Image. The size of the images produced by this method is limited by available memory only. This means that the images can be much larger than the on-screen dimensions of the view.

The print() method takes one parameter, which is a TLspViewPrintSettings object. See Printing a view for more information about this class.

The main difference between printing a TLspFXView and any of the other view implementations lies in the interpretation of the printBounds property. In a JavaFX view, the print bounds are used to determine the dimensions of the output image only. This means that the X and Y position of the bounds are disregarded.

Once you have an image, you can present it in a Node, an ImageView for example, which you can then print.

You can change the print layout and add page numbers or other decorations, for example, or partition the print across more pages. To make such layout changes, place the image in an appropriate layout pane along with any other content, and then print that pane.

The example below illustrates how to perform a simple full-page print of a TLspFXView. For a more elaborate example, including a preview and multi-page printing, see samples.lightspeed.javafx.common.printing.PrintPreview.

Program: Printing a JavaFX view
TLspFXView view = getView();

// Default to a PDF printer if we have one
Printer printer = Printer
    .getAllPrinters()
    .stream()
    .filter(p -> p.getName().contains("PDF"))
    .findFirst()
    .orElse(Printer.getDefaultPrinter());

// Create a printer job and get the default page layout
PrinterJob job = PrinterJob.createPrinterJob(printer);
PageLayout pageLayout = job.getJobSettings().getPageLayout();
// JavaFX provides a "cross-feed" resolution as well, but we assume the printer has the same resolution
// along both the X and the Y axis.
double printDPI = job.getJobSettings().getPrintResolution().getFeedResolution();
// One JavaFX coordinate unit will equal one point (1/72 of an inch) on the print. This scale factor will
// therefore convert from points to actual pixels at the printer's resolution.
double pt2px = printDPI / 72.0;
// Compute the extents, in pixels, of the printable area of the page
int width = (int) (pageLayout.getPrintableWidth() * pt2px);
int height = (int) (pageLayout.getPrintableHeight() * pt2px);

// Create print settings that will fill the page
TLspViewPrintSettings settings = TLspViewPrintSettings
    .newBuilder()
    // The size of the image we want to print:
    .printBounds(new Rectangle(0, 0, width, height))
    // Relative scaling of fonts, lines and icons (adjust to taste):
    .featureScale(0.5)
    // We can optionally receive progress updates from the printing process:
    .statusListener(System.out::println)
    .build();

// Now we can generate a high-res image for printing
Image print = view.print(settings);

// PrinterJob expects a Node as input, so we put the image in an ImageView and scale it to fit the page
ImageView node = new ImageView(print);
node.setFitWidth(pageLayout.getPrintableWidth());
node.setFitHeight(pageLayout.getPrintableHeight());

// Finally, we can now print the ImageView
job.printPage(node);
// We can now submit additional pages, or we can end the job here so everything gets sent to the printer
job.endJob();