© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
K. Sharan, P. SpäthLearn JavaFX 17https://doi.org/10.1007/978-1-4842-7848-2_27

27. Understanding the Print API

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • What the Print API is

  • How to obtain the list of available printers

  • How to get the default printer

  • How to print nodes

  • How to show the page setup and print dialog to users

  • How to customize the setting for the printer jobs

  • How to set up the page layout for printing

  • How to print web pages displayed in a WebView

The examples of this chapter lie in the com.jdojo.print package. In order for them to work, you must add a corresponding line to the module-info.java file:
...
opens com.jdojo.print to javafx.graphics, javafx.base;
...

What Is the Print API?

JavaFX includes support for printing nodes through the Print API in the javafx.print package. The API consists of the following classes and a number of enums (not listed):
  • Printer

  • PrinterAttributes

  • PrintResolution

  • PrinterJob

  • JobSettings

  • Paper

  • PaperSource

  • PageLayout

  • PageRange

Instances of the above-listed classes represent different parts of the printing process. For example, a Printer represents a printer that can be used for printing jobs; a PrinterJob represents a print job that can be sent to a Printer for printing; and a Paper represents the paper sizes available on printers.

The Print API provides support for printing nodes that may or may not be attached to a scene graph. It is a common requirement to print the content of a web page, not the WebView node that contains the web page. The javafx.scene.web.WebEngine class contains a print(PrinterJob job) method that prints the contents of the web page, not the WebView node.

If a node is modified during the printing process, the printed node may not appear correct. Note that the printing of a node may span multiple pulse events resulting in concurrent change in the content being printed. To ensure correct printing, please make sure that the node being printed is not modified during the print process.

Nodes can be printed on any thread including the JavaFX Application Thread. It is recommended that large, time-consuming print jobs be submitted on a background thread to keep the UI responsive.

Classes in the Print API are final as they represent existing printing device properties. Most of them do not provide any public constructor as you cannot make up a printing device. Rather, you obtain their references using factory methods in various classes.

Note

The Print API provides the basic printing support only to print nodes and web pages. You will not be able to use it to print reports in JavaFX applications.

Listing Available Printers

The Printer.getAllPrinters() static method returns an observable list of installed printers on the machine. Note that the list of printers returned by the method may change over time as new printers are installed or old printers are removed. Use the getName() method of the Printer to get the name of the printer. The following snippet of code lists all installed printers on the machine running the code. You may get a different output:
import javafx.collections.ObservableSet;
import javafx.print.Printer;
...
ObservableSet<Printer> allPrinters = Printer.getAllPrinters();
for(Printer p : allPrinters) {
        System.out.println(p.getName());
}
ImageRight Printer
Microsoft XPS Document Writer
PDF995
Sybase DataWindow PS
\pro-print1IS-CANON1
\pro-print1IS-HP4000
\pro-print1IS-HP4015
\pro-print1IS-HP4050
\pro-print1IS-HP4650
\pro-print1IS-HP4650(Color)

Getting the Default Printer

The Printer.getDefaultPrinter() method returns the default Printer. The method may return null if no printer is installed. The default printer may be changed on a machine. Therefore, the method may return different printers from call to call, and the printer returned may not be valid after some time. The following snippet of code shows how to get the default printer:
Printer defaultprinter = Printer.getDefaultPrinter();
if (defaultprinter != null) {
        String name = defaultprinter.getName();
        System.out.println("Default printer name: " + name);
} else {
        System.out.println("No printers installed.");
}

Printing Nodes

Printing a node is easy: create a PrinterJob and call its printPage() method passing the node to be printed. Printing a node using the default printer with all default settings takes only three lines of code:
PrinterJob printerJob = PrinterJob.createPrinterJob();
printerJob.printPage(node); // node is the node to be printed
printerJob.endJob();
In a real-world application, you want to handle the errors. You can rewrite the code to handle errors as follows:
// Create a printer job for the default printer
PrinterJob printerJob = PrinterJob.createPrinterJob();
if (printerJob!= null) {
        // Print the node
        boolean printed = printerJob.printPage(node);
        if (printed) {
                // End the printer job
                printerJob.endJob();
        } else {
                System.out.println("Printing failed.");
        }
} else {
        System.out.println("Could not create a printer job.");
}
You can use the createPrinterJob() static method of the PrinterJob class to create a printer job:
  • public static PrinterJob createPrinterJob()

  • public static PrinterJob createPrinterJob(Printer printer)

The method with no-args creates a printer job for the default printer. You can use the other version of the method to create a printer job for the specified printer.

You can change the printer for a PrinterJob by calling its setPrinter() method . If the current printer job settings are not supported by the new printer, the settings are reset automatically for the new printer:
// Set a new printer for the printer job
printerJob.setPrinter(myNewPrinter);

Setting a null printer for the job will use the default printer.

Use one of the following printPage() methods to print a node:
  • boolean printPage(Node node)

  • boolean printPage(PageLayout pageLayout, Node node)

The first version of the method takes only the node to be printed as the parameter. It uses the default page layout for the job for printing.

The second version lets you specify a page layout for printing the node. The specified PageLayout will override the PageLayout for the job, and it will be used only for printing the specified node. For subsequent printing, the default PageLayout for the job will be used. You can create a PageLayout using the Printer class. I will discuss an example of this kind later.

The printPage() method returns true if the printing was successful. Otherwise, it returns false. When you are done printing, call the endJob() method . The method returns true if the job can be successfully spooled to the printer queue. Otherwise, it returns false, which may indicate that the job could not be spooled or it was already completed. After successful completion of the job, the job can no longer be reused.

Tip

You can call the printPage() method on a PrinterJob as many times as you want. Calling the endJob() method tells the job that no more printing will be performed. The method transitions the job status to DONE, and the job should no longer be reused.

You can cancel a print job using the cancelJob() method of the PrinterJob. The printing may not be cancelled immediately, for example, when a page is in the middle of printing. The cancellation occurs as soon as possible. The method does not have any effect if
  • The job has already been requested to be cancelled.

  • The job is already completed.

  • The job has an error.

A PrinterJob has a read-only status, which is defined by one of the constants of the PrinterJob.JobStatus enum:
  • NOT_STARTED

  • PRINTING

  • CANCELED

  • DONE

  • ERROR

The NOT_STARTED status indicates a new job. In this status, the job can be configured, and printing can be initiated. The PRINTING status indicates that the job has requested to print at least one page, and it has not terminated printing. In this status, the job cannot be configured.

The other three statuses, CANCELED, DONE, and ERROR, indicate the termination state of the job. Once the job is in one of these statuses, it should not be reused. There is no need to call the endJob() method when the status goes to CANCELED or ERROR. The DONE status is entered when the printing was successful and the endJob() method was called. The PrinterJob class contains a read-only jobStatus property that indicates the current status of the print job.

The program in Listing 27-1 shows how to print nodes. It displays a TextArea where you can enter text. Two Buttons are provided: one prints the TextArea node and the other the entire scene. When printing is initiated, the print job status is displayed in a Label. The code in the print() method is the same as previously discussed. The method includes the logic to display the job status in the Label. The program displays the window shown in Figure 27-1. Run the program; enter text in the TextArea; and click one of the two buttons to print.
// PrintingNodes.java
package com.jdojo.print;
import javafx.application.Application;
import javafx.print.PrinterJob;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PrintingNodes  extends Application {
        private Label jobStatus = new Label();
        public static void main(String[] args) {
                Application.launch(args);
        }
        @Override
        public void start(Stage stage) {
                VBox root = new VBox(5);
                Label textLbl = new Label("Text:");
                TextArea text = new TextArea();
                text.setPrefRowCount(10);
                text.setPrefColumnCount(20);
                text.setWrapText(true);
                // Button to print the TextArea node
                Button printTextBtn = new Button("Print Text");
                printTextBtn.setOnAction(e -> print(text));
                // Button to print the entire scene
                Button printSceneBtn = new Button("Print Scene");
                printSceneBtn.setOnAction(e -> print(root));
                HBox jobStatusBox = new HBox(5,
                         new Label("Print Job Status:"), jobStatus);
                HBox buttonBox = new HBox(5,
                         printTextBtn, printSceneBtn);
                root.getChildren().addAll(
                         textLbl, text, jobStatusBox, buttonBox);
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.setTitle("Printing Nodes");
                stage.show();
        }
        private void print(Node node) {
                jobStatus.textProperty().unbind();
                jobStatus.setText("Creating a printer job...");
                // Create a printer job for the default printer
                PrinterJob job = PrinterJob.createPrinterJob();
                if (job != null) {
                    // Show the printer job status
                    jobStatus.textProperty().bind(
                               job.jobStatusProperty().asString());
                    // Print the node
                    boolean printed = job.printPage(node);
                    if (printed) {
                        // End the printer job
                        job.endJob();
                    } else {
                        jobStatus.textProperty().unbind();
                        jobStatus.setText("Printing failed.");
                    }
                } else {
                    jobStatus.setText(
                               "Could not create a printer job.");
                }
        }
}
Listing 27-1

Printing Nodes

Figure 27-1

A window letting the user print text in a TextArea and the scene

Showing the Page Setup and Print Dialogs

The Print API allows users to interact with the printing process. Users can change the printer settings interactively before the printing is initiated. The API lets you show Page Setup and Print Setup dialogs for setting the page properties and printer settings for the job.

You can let the user configure the page layout by showing a Page Setup dialog. Use the showPageSetupDialog(Window owner) method of the PrinterJob to show a Page Setup dialog. The user can set the page size, source, orientation, and margin. The dialog may allow the user to access other printing properties such as the list of printers. Once the user confirms the settings on the dialog, the PrinterJob has the new settings. The method returns true if the user confirms the settings on the dialog. It returns false if the user cancels the dialog. It also returns false if the dialog cannot be displayed, such as when the job is not in the NOT_STARTED state.

The owner parameter to the method is the window that will be the owner of the dialog box. It can be null. If specified, the inputs to the window will be blocked, while the dialog is displayed:
PrinterJob job = PrinterJob.createPrinterJob();
// Show the page setup dialog
boolean proceed = job.showPageSetupDialog(null);
if (proceed) {
        // Start printing here or you can print later
}
You can use the showPrintDialog(Window owner) method to show a Print dialog where the user can modify the printer and settings for the PrinterJob. The return value and parameter of this method have meanings similar to that of the showPageSetupDialog() method :
PrinterJob job = PrinterJob.createPrinterJob();
// Show the print setup dialog
boolean proceed = job.showPrintDialog(null);
if (proceed) {
        // Start printing here or you can print later
}
The program in Listing 27-2 shows a similar window as shown by the program in Listing 27-1. This time, clicking the print buttons displays a Page Setup and Print Setup dialogs (as shown in Figure 27-2). Once the user confirms the settings on the dialogs, the text in the TextArea is printed. Notice that even though you create a PrinterJob for the default printer before showing the dialogs, you can change the printer using the dialogs, and the text will print using the changed printer.
// PrintDialogs.java
// ...find in the book's download area
Listing 27-2

Showing the Page Setup and Print Dialogs to the User

Figure 27-2

A window letting users use print dialogs to customize the printer settings

Customizing PrinterJob Settings

The Print API contains two classes that are related to printer and printer job settings:
  • PrinterAttributes

  • JobSettings

A printer has attributes, which indicate the printing capabilities of the printer. Examples of printer attributes are default paper size, supported paper sizes, maximum number of copies, and default collation. A PrinterAttributes object encapsulates the attributes of a printer. The Print API does not let you change the printer attributes as you cannot change the capabilities of a printer. You can only use its capabilities. You cannot create a PrinterAttributes object directly. You need to get it from a Printer object using the getPrinterAttributes() method . The following snippet of code prints some attributes of the default printer in the machine. You may get a different output:
import javafx.print.Collation;
import javafx.print.PageOrientation;
import javafx.print.PrintSides;
import javafx.print.Printer;
import javafx.print.PrinterAttributes;
...
Printer printer = Printer.getDefaultPrinter();
PrinterAttributes attribs = printer.getPrinterAttributes();
// Read some printer attributes
int maxCopies = attribs.getMaxCopies();
PrintSides printSides = attribs.getDefaultPrintSides();
Set<PageOrientation> orientations = attribs.getSupportedPageOrientations();
Set<Collation> collations = attribs.getSupportedCollations();
// Print the printer attributes
System.out.println("Max. Copies: " + maxCopies);
System.out.println("Print Sides: " + printSides);
System.out.println("Supported Orientation: " + orientations);
System.out.println("Supported Collations: " + collations);
Max. Copies: 999
Print Sides: ONE_SIDED
Supported Orientation: [PORTRAIT, LANDSCAPE, REVERSE_LANDSCAPE]
Supported Collations: [UNCOLLATED, COLLATED]
Tip

A PrinterAttributes is an immutable object. It contains the default and supported attributes of a printer. You obtain PrinterAttributes from a Printer object.

A JobSettings contains the printer attributes to be used for a print job for a specific printer. You can obtain the JobSettings of a print job using the getJobSettings() method of the PrinterJob object. A JobSettings is a mutable object. It contains a property for each printer attribute that can be set for a print job. By default, its properties are initialized to the default properties of the printer. You can change the property that will be used for the current print job. If you change the property of a JobSettings that is not supported by the printer, the property reverts to the default value for the printer. The following snippet of code sets the printSides property to DUPLEX. In this case, the printer supports only ONE_SIDED printing. Therefore, the printSides property is set to ONE_SIDED, which is the default, and only supported printSides value by the printer. You may get a different output:
// Create a printer job for the default printer
PrinterJob job = PrinterJob.createPrinterJob();
// Get the JobSettings for the print job
JobSettings jobSettings = job.getJobSettings();
System.out.println(jobSettings.getPrintSides());
// Set the printSides to DUPLEX
jobSettings.setPrintSides(PrintSides.DUPLEX);
System.out.println(jobSettings.getPrintSides());
ONE_SIDED
ONE_SIDED
For a print job, you can specify the page ranges using the pageRanges property of the JobSettings. The pageRanges property is an array of PageRange. A PageRange has startPage and endPage properties that define the range. The following snippet of code sets the page ranges for a job to 1–5 and 20–25:
PrinterJob job = PrinterJob.createPrinterJob();
JobSettings jobSettings = job.getJobSettings();
jobSettings.setPageRanges(new PageRange(1, 5), new PageRange(20, 25));

Most of the printer attributes are represented by enum constants. For example, the collation attribute is represented by Collation.COLLATED and Collation.UNCOLLATED constants. Some attributes, such as the number of copies to be printed, are specified as an int. Please refer to the list of properties in the JobSettings class that you can set for a print job.

Setting Page Layout

An instance of the PageLayout class represents the page setup for a print job. By default, it is set to the printer default value. You have already seen setting up the page layout using the Page Setup dialog. A PageLayout encapsulates three things:
  • The paper size

  • The page orientation

  • The page margins

A PageLayout is used to configure the printable area of the page, which must lie within the printable area of the hardware. If a page is rendered outside the printable area of the hardware, the content is clipped.

You cannot create a PageLayout object directly. You need to use one of the createPageLayout() methods of the Printer to get a PageLayout:
  • PageLayout createPageLayout(Paper paper, PageOrientation orient, double lMargin, double rMargin, double tMargin, double bMargin)

  • PageLayout createPageLayout(Paper paper, PageOrientation orient, Printer.MarginType mType)

The margins can be specified as numbers or as one of the following constants of the Printer.MarginType enum:
  • DEFAULT

  • EQUAL

  • EQUAL_OPPOSITES

  • HARDWARE_MINIMUM

The DEFAULT margin type requests default 0.75 inch on all sides.

The EQUAL margin type uses the largest of the four hardware margins on all four sides, so the margins are equal on all four sides.

The EQUAL_OPPOSITES margin type uses the larger of left and right hardware margins for the left and right sides, and the larger of the top and bottom hardware margins for the top and bottom sides.

The HARDWARE_MINIMUM requests that the minimum hardware allowed margins should be set on all sides.

The following snippet of code creates a PageLayout for A4 size paper, LANDSCAPE page orientation, and equal margins on all sides. The PageLayout is set to a print job:
import javafx.print.JobSettings;
import javafx.print.PageLayout;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.Printer;
import javafx.print.PrinterJob;
...
PrinterJob job = PrinterJob.createPrinterJob();
Printer printer = job.getPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4,
                                                 PageOrientation.LANDSCAPE,
                                                 Printer.MarginType.EQUAL);
JobSettings jobSettings = job.getJobSettings();
jobSettings.setPageLayout(pageLayout);
Sometimes, you want to know the size of the printable area on the page. You can get it using the getPrintableWidth() and getPrintableHeight() methods of the PageLayout. This is useful if you want to resize a node before printing, so it fits the printable area. The following snippet of code prints an Ellipse that fits the printable area:
PrinterJob job = PrinterJob.createPrinterJob();
JobSettings jobSettings = job.getJobSettings();
PageLayout pageLayout = jobSettings.getPageLayout();
double pgW = pageLayout.getPrintableWidth();
double pgH = pageLayout.getPrintableHeight();
// Make the Ellipse fit the printable are of the page
Ellipse node = new Ellipse(pgW/2, pgH/2, pgW /2, pgH/2);
node.setFill(null);
node.setStroke(Color.BLACK);
node.setStrokeWidth(1);
boolean printed = job.printPage(node);
if (printed) {
        // End the printer job
        job.endJob();
}

Printing a Web Page

There is a special way to print the contents of a web page. Use the print(PrinterJob job) method of the WebEngine class to print the web page loaded by the engine. The method does not modify the specified job. The job can be used for more printing after the print() method call:
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
...
PrinterJob job = PrinterJob.createPrinterJob();
webEngine.print(job);
The program in Listing 27-3 shows how to print web pages. There is nothing new in the program that you have not already covered. The program displays a window with a URL field, a Go button, a Print button, and a WebView. The Print button is enabled when a web page is successfully loaded. You can enter a web page URL and click the Go button to navigate to the page. Click the Print button to print the web page.
// PrintingWebPage.java
// ...find in the book's download area.
Listing 27-3

Printing a Web Page

Summary

JavaFX includes support for printing nodes through the Print API in the javafx.print package. The API consists of a few classes and a number of enums. The Print API provides support for printing nodes that may or may not be attached to a scene graph. It is a common requirement to print the content of a web page, not the WebView node that contains the web page. The javafx.scene.web.WebEngine class contains a print(PrinterJob job) method that prints the contents of the web page, not the WebView node.

If a node is modified during the printing process, the printed node may not appear correct. Note that the printing of a node may span multiple pulse events resulting in concurrent change in the content being printed. To ensure correct printing, make sure that the node being printed is not modified during the print process.

Nodes can be printed on any thread including the JavaFX Application Thread. It is recommended that large, time-consuming print jobs be submitted on a background thread to keep the UI responsive.

Classes in the Print API are final as they represent existing printing device properties. Most of them do not provide any public constructor as you cannot make up a printing device. Rather, you obtain their references using factory methods in various classes.

An instance of the Printer class represents a printer. The Printer.getAllPrinters() static method returns an observable list of installed printers on the machine. Note that the list of printers returned by the method may change over time as new printers are installed or old printers are removed. Use the getName() method of the Printer to get the name of the printer.

The Printer.getDefaultPrinter() method returns the default Printer. The method may return null if no printer is installed. The default printer may be changed on a machine. Therefore, the method may return different printers from call to call, and the printer returned may not be valid after some time.

You can create a printer job by calling the PrinterJob.createPrinterJob() method. It returns an object of the PrinterJob class. Once you get a PrinterJob object, call its printPage() method to print a node. The node to be printed is passed as an argument to the method.

The Print API allows users to interact with the printing process. Users can change the printer settings interactively before the printing is initiated. The API lets you show Page Setup and Print Setup dialogs for setting the page properties and printer settings for the job. You can let the user configure the page layout by showing a Page Setup dialog. Use the showPageSetupDialog(Window owner) method of the PrinterJob to show a Page Setup dialog. The user can set the page size, source, orientation, and margins. The dialog may allow the user to access other printing properties such as the list of printers.

The Print API lets you customize the printer job settings. The API contains two classes that are related to printer and printer job settings: PrinterAttributes and JobSettings classes. A printer has attributes, which indicate the printing capabilities of the printer such as default paper size, supported paper sizes, maximum number of copies, and default collation. A PrinterAttributes object encapsulates the attributes of a printer. The Print API does not let you change the printer attributes as you cannot change the capabilities of a printer. You cannot create a PrinterAttributes object directly. You need to get it from a Printer object using the getPrinterAttributes() method.

An instance of the PageLayout class represents the page setup for a print job. By default, it is set to the printer default value. A PageLayout is used to configure the printable area of the page, which must lie within the printable area of the hardware. If a page is rendered outside the printable area of the hardware, the content is clipped. You cannot create a PageLayout object directly. You need to use one of the createPageLayout() methods of the Printer to get a PageLayout.

There is a special way to print the contents of a web page. Use the print(PrinterJob job) method of the WebEngine class to print the web page loaded by the engine. The method does not modify the specified job. The job can be used for more printing after the print() method call.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset