JavaFX17-学习手册-十三-

221 阅读17分钟

JavaFX17 学习手册(十三)

原文:Learn JavaFX 17

协议:CC BY-NC-SA 4.0

二十七、了解打印 API

在本章中,您将学习:

  • 什么是打印 API

  • 如何获取可用打印机列表

  • 如何获取默认打印机

  • 如何打印节点

  • 如何向用户显示页面设置和打印对话框

  • 如何自定义打印机作业的设置

  • 如何设置打印页面布局

  • 如何打印显示在WebView中的网页

本章的例子在com.jdojo.print包中。为了让它们工作,您必须在module-info.java文件中添加相应的一行:

...
opens com.jdojo.print to javafx.graphics, javafx.base;
...

什么是打印 API?

JavaFX 通过javafx.print包中的 Print API 支持打印节点。API 由以下类和一些枚举(未列出)组成:

  • Printer

  • PrinterAttributes

  • PrintResolution

  • PrinterJob

  • JobSettings

  • Paper

  • PaperSource

  • PageLayout

  • PageRange

上面列出的类的实例代表打印过程的不同部分。例如,Printer代表可以用于打印作业的打印机;一个PrinterJob代表一个可以发送到Printer进行打印的打印作业;而Paper代表打印机上可用的纸张尺寸。

Print API 提供了对打印节点的支持,这些节点可能会也可能不会附加到场景图。打印 web 页面的内容,而不是包含 web 页面的WebView节点,这是一个常见的需求。javafx.scene.web.WebEngine类包含一个打印网页内容的print(PrinterJob job)方法,而不是WebView节点。

如果在打印过程中修改了节点,打印的节点可能看起来不正确。注意,节点的打印可以跨越多个脉冲事件,导致正在打印的内容的同时改变。为确保正确打印,请确保正在打印的节点在打印过程中未被修改。

节点可以打印在任何线程上,包括 JavaFX 应用程序线程。建议在后台线程上提交大型、耗时的打印作业,以保持 UI 的响应性。

Print API 中的类是最终的,因为它们表示现有的打印设备属性。它们中的大多数不提供任何公共构造器,因为你不能组成一个打印设备。相反,您可以使用各种类中的工厂方法来获取它们的引用。

Note

Print API 只为打印节点和网页提供基本的打印支持。您将无法使用它在 JavaFX 应用程序中打印报告。

列出可用的打印机

静态方法返回机器上已安装打印机的可见列表。请注意,方法返回的打印机列表可能会随着新打印机的安装或旧打印机的移除而改变。使用PrintergetName()方法获取打印机的名称。以下代码片段列出了运行该代码的机器上安装的所有打印机。您可能会得到不同的输出:

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-print1\IS-CANON1
\\pro-print1\IS-HP4000
\\pro-print1\IS-HP4015
\\pro-print1\IS-HP4050
\\pro-print1\IS-HP4650
\\pro-print1\IS-HP4650(Color)

获取默认打印机

Printer.getDefaultPrinter()方法返回默认的Printer。如果没有安装打印机,该方法可能返回null。机器上的默认打印机可以更改。因此,该方法可能在调用之间返回不同的打印机,并且返回的打印机可能在一段时间后无效。以下代码片段显示了如何获取默认打印机:

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.");
}

打印节点

打印节点很简单:创建一个PrinterJob并调用它的printPage()方法,传递要打印的节点。使用具有所有默认设置的默认打印机打印节点只需要三行代码:

PrinterJob printerJob = PrinterJob.createPrinterJob();
printerJob.printPage(node); // node is the node to be printed
printerJob.endJob();

在现实世界的应用程序中,您需要处理错误。您可以重写代码来处理错误,如下所示:

// 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.");
}

您可以使用PrinterJob类的createPrinterJob()静态方法来创建打印机作业:

  • public static PrinterJob createPrinterJob()

  • public static PrinterJob createPrinterJob(Printer printer)

不带-args 的方法为默认打印机创建一个打印机作业。您可以使用其他版本的方法为指定的打印机创建打印机作业。

您可以通过调用setPrinter()方法来更改PrinterJob的打印机。如果新打印机不支持当前的打印机作业设置,新打印机会自动重置这些设置:

// Set a new printer for the printer job
printerJob.setPrinter(myNewPrinter);

为作业设置null打印机将使用默认打印机。

使用以下printPage()方法之一打印节点:

  • boolean printPage(Node node)

  • boolean printPage(PageLayout pageLayout, Node node)

该方法的第一个版本只接受要打印的节点作为参数。它使用作业的默认页面布局进行打印。

第二个版本允许您指定打印节点的页面布局。指定的PageLayout将覆盖作业的PageLayout,并且仅用于打印指定的节点。对于后续打印,将使用作业的默认PageLayout。您可以使用Printer类创建一个PageLayout。我将在后面讨论这类例子。

如果打印成功,printPage()方法返回 true。否则,它返回 false。完成打印后,调用endJob()方法。如果作业可以成功地假脱机到打印机队列,则该方法返回 true。否则,它将返回 false,这可能表示作业无法假脱机或已经完成。作业成功完成后,就不能再重复使用了。

Tip

您可以任意多次调用PrinterJob上的printPage()方法。调用endJob()方法告诉作业不再执行打印。该方法将作业状态转换到DONE,并且该作业不应再被重用。

您可以使用PrinterJobcancelJob()方法取消打印作业。打印可能不会立即取消,例如,当页面正在打印时。取消会尽快发生。如果出现以下情况,则该方法没有任何效果

  • 已经请求取消该作业。

  • 这项工作已经完成。

  • 作业有错误。

PrinterJob具有只读状态,由PrinterJob.JobStatus枚举的常量之一定义:

  • NOT_STARTED

  • PRINTING

  • CANCELED

  • DONE

  • ERROR

NOT_STARTED状态表示一个新任务。在这种状态下,可以配置作业,并且可以开始打印。PRINTING状态表示作业已请求打印至少一页,并且尚未终止打印。在这种状态下,无法配置作业。

其他三种状态,CANCELEDDONEERROR,表示作业的终止状态。一旦作业处于其中一种状态,就不应该重复使用。当状态变为CANCELEDERROR时,不需要调用endJob()方法。当打印成功并且调用了endJob()方法时,进入DONE状态。PrinterJob类包含一个只读的jobStatus属性,指示打印作业的当前状态。

清单 27-1 中的程序展示了如何打印节点。它显示一个TextArea,您可以在其中输入文本。提供了两个Button:一个打印TextArea节点,另一个打印整个场景。当开始打印时,打印作业状态显示在Label中。print()方法中的代码与前面讨论的相同。该方法包括在Label中显示作业状态的逻辑。程序显示如图 27-1 所示的窗口。运行程序;在TextArea中输入文本;然后点击两个按钮中的一个进行打印。

img/336502_2_En_27_Fig1_HTML.png

图 27-1

允许用户在文本区域和场景中打印文本的窗口

// 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-1Printing Nodes

显示页面设置和打印对话框

打印 API 允许用户与打印过程进行交互。用户可以在打印开始前交互更改打印机设置。API 允许您显示页面设置和打印设置对话框,用于设置作业的页面属性和打印机设置。

您可以让用户通过显示页面设置对话框来配置页面布局。使用PrinterJobshowPageSetupDialog(Window owner)方法显示页面设置对话框。用户可以设置页面大小、来源、方向和边距。该对话框可以允许用户访问其他打印属性,例如打印机列表。一旦用户确认了对话框上的设置,PrinterJob就会有新的设置。如果用户确认对话框上的设置,该方法返回true。如果用户取消对话框,它将返回false。如果对话框无法显示,例如当作业不在NOT_STARTED状态时,它也返回 false。

该方法的所有者参数是将成为对话框所有者的窗口。可以是null。如果指定,当对话框显示时,窗口的输入将被阻止:

PrinterJob job = PrinterJob.createPrinterJob();

// Show the page setup dialog
boolean proceed = job.showPageSetupDialog(null);
if (proceed) {
        // Start printing here or you can print later
}

您可以使用showPrintDialog(Window owner)方法显示一个打印对话框,用户可以在其中修改打印机和PrinterJob的设置。该方法的返回值和参数与showPageSetupDialog()方法的含义相似:

PrinterJob job = PrinterJob.createPrinterJob();

// Show the print setup dialog
boolean proceed = job.showPrintDialog(null);
if (proceed) {
        // Start printing here or you can print later
}

清单 27-2 中的程序显示了与清单 27-1 中的程序类似的窗口。这次,点击打印按钮显示页面设置和打印设置对话框(如图 27-2 )。一旦用户确认了对话框上的设置,就会打印出TextArea中的文本。注意,即使您在显示对话框之前为默认打印机创建了一个PrinterJob,您也可以使用对话框更改打印机,文本将使用更改后的打印机打印。

img/336502_2_En_27_Fig2_HTML.png

图 27-2

允许用户使用打印对话框来自定义打印机设置的窗口

// PrintDialogs.java
// ...find in the book's download area

Listing 27-2Showing the Page Setup and Print Dialogs to the User

自定义打印机作业设置

打印 API 包含两个与打印机和打印机作业设置相关的类:

  • PrinterAttributes

  • JobSettings

打印机具有指示打印机打印能力的属性。打印机属性的示例包括默认纸张尺寸、支持的纸张尺寸、最大份数和默认排序规则。一个PrinterAttributes对象封装了打印机的属性。打印 API 不允许您更改打印机属性,因为您不能更改打印机的功能。你只能使用它的能力。您不能直接创建一个PrinterAttributes对象。你需要使用getPrinterAttributes()方法从一个Printer对象中获取它。以下代码片段打印机器中默认打印机的一些属性。您可能会得到不同的输出:

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

一个PrinterAttributes是一个不可变的对象。它包含打印机的默认属性和支持属性。你从一个Printer对象中获得PrinterAttributes

JobSettings包含用于特定打印机打印作业的打印机属性。您可以使用PrinterJob对象的getJobSettings()方法获得打印作业的JobSettings。一个JobSettings是一个可变对象。它包含可以为打印作业设置的每个打印机属性的属性。默认情况下,其属性被初始化为打印机的默认属性。您可以更改将用于当前打印作业的属性。如果您更改打印机不支持的JobSettings的属性,该属性将恢复为打印机的默认值。下面的代码片段将printSides属性设置为DUPLEX。在这种情况下,打印机仅支持ONE_SIDED打印。因此,printSides属性被设置为ONE_SIDED,这是默认设置,并且只有打印机支持printSides值。您可能会得到不同的输出:

// 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

对于打印作业,您可以使用JobSettingspageRanges属性指定页面范围。pageRanges属性是一个PageRange的数组。一个PageRange具有定义范围的startPageendPage属性。以下代码片段将作业的页面范围设置为 1–5 和 20–25:

PrinterJob job = PrinterJob.createPrinterJob();
JobSettings jobSettings = job.getJobSettings();
jobSettings.setPageRanges(new PageRange(1, 5), new PageRange(20, 25));

大多数打印机属性由枚举常量表示。例如,排序规则属性由Collation.COLLATEDCollation.UNCOLLATED常量表示。一些属性,比如要打印的份数,被指定为一个int。请参考JobSettings类中您可以为打印作业设置的属性列表。

设置页面布局

PageLayout类的一个实例表示打印作业的页面设置。默认情况下,它被设置为打印机默认值。您已经看到了使用页面设置对话框设置页面布局。一个PageLayout封装了三样东西:

  • 纸张尺寸

  • 页面方向

  • 页边距

PageLayout用于配置页面的可打印区域,该区域必须位于硬件的可打印区域内。如果页面呈现在硬件的可打印区域之外,内容将被剪切。

您不能直接创建一个PageLayout对象。您需要使用PrintercreatePageLayout()方法之一来获得PageLayout:

  • PageLayout createPageLayout(Paper paper, PageOrientation orient, double lMargin, double rMargin, double tMargin, double bMargin)

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

边距可以指定为数字或以下Printer.MarginType枚举常量之一:

  • DEFAULT

  • EQUAL

  • EQUAL_OPPOSITES

  • HARDWARE_MINIMUM

DEFAULT边距类型要求所有边的缺省值为 0.75 英寸。

EQUAL边距类型在所有四边使用四个硬件边距中最大的一个,因此所有四边的边距都相等。

EQUAL_OPPOSITES边距类型为左右两边使用较大的左右硬件边距,为上下两边使用较大的上下硬件边距。

HARDWARE_MINIMUM要求在所有边上设置硬件允许的最小余量。

下面的代码片段为 A4 大小的纸张创建了一个PageLayoutLANDSCAPE页面方向,并在所有边上创建了相等的边距。PageLayout被设置为打印作业:

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);

有时,您想知道页面上可打印区域的大小。你可以使用PageLayoutgetPrintableWidth()getPrintableHeight()方法得到它。如果您想在打印前调整节点大小,使其适合可打印区域,这很有用。下面的代码片段打印一个适合可打印区域的Ellipse:

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();
}

打印网页

打印网页内容有一种特殊的方法。使用WebEngine类的print(PrinterJob job)方法打印引擎加载的网页。该方法不修改指定的job。在print()方法调用后,作业可用于更多的打印:

WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
...
PrinterJob job = PrinterJob.createPrinterJob();
webEngine.print(job);

清单 27-3 中的程序显示了如何打印网页。这个项目中没有你没有涉及过的新内容。该程序显示一个带有 URL 字段的窗口,一个 Go 按钮,一个 Print 按钮和一个WebView。当网页成功加载时,Print按钮被激活。您可以输入一个网页 URL,然后单击 Go 按钮导航至该页面。点击打印按钮打印网页。

// PrintingWebPage.java
// ...find in the book's download area.

Listing 27-3Printing a Web Page

摘要

JavaFX 通过javafx.print包中的 Print API 支持打印节点。API 由几个类和一些枚举组成。Print API 提供了对打印节点的支持,这些节点可能会也可能不会附加到场景图。打印 web 页面的内容,而不是包含 web 页面的WebView节点,这是一个常见的需求。javafx.scene.web.WebEngine类包含一个打印网页内容的print(PrinterJob job)方法,而不是WebView节点。

如果在打印过程中修改了节点,打印的节点可能看起来不正确。注意,节点的打印可以跨越多个脉冲事件,导致正在打印的内容的同时改变。为了确保正确打印,请确保正在打印的节点在打印过程中没有被修改。

节点可以打印在任何线程上,包括 JavaFX 应用程序线程。建议在后台线程上提交大型、耗时的打印作业,以保持 UI 的响应性。

Print API 中的类是最终的,因为它们表示现有的打印设备属性。它们中的大多数不提供任何公共构造器,因为你不能组成一个打印设备。相反,您可以使用各种类中的工厂方法来获取它们的引用。

Printer类的一个实例代表一台打印机。静态方法返回机器上已安装打印机的可见列表。请注意,方法返回的打印机列表可能会随着新打印机的安装或旧打印机的移除而改变。使用PrintergetName()方法获取打印机的名称。

Printer.getDefaultPrinter()方法返回默认的Printer。如果没有安装打印机,该方法可能返回null。机器上的默认打印机可以更改。因此,该方法可能在调用之间返回不同的打印机,并且返回的打印机可能在一段时间后无效。

您可以通过调用PrinterJob.createPrinterJob()方法来创建打印机作业。它返回一个PrinterJob类的对象。一旦获得了一个PrinterJob对象,就调用它的printPage()方法来打印一个节点。要打印的节点作为参数传递给方法。

打印 API 允许用户与打印过程进行交互。用户可以在打印开始前交互更改打印机设置。API 允许您显示页面设置和打印设置对话框,用于设置作业的页面属性和打印机设置。您可以让用户通过显示页面设置对话框来配置页面布局。使用PrinterJobshowPageSetupDialog(Window owner)方法显示页面设置对话框。用户可以设置页面大小、来源、方向和页边距。该对话框可以允许用户访问其他打印属性,例如打印机列表。

打印 API 允许您自定义打印机作业设置。API 包含两个与打印机和打印机作业设置相关的类:PrinterAttributesJobSettings类。打印机具有指示打印机打印能力的属性,如默认纸张尺寸、支持的纸张尺寸、最大份数和默认排序规则。一个PrinterAttributes对象封装了打印机的属性。打印 API 不允许您更改打印机属性,因为您不能更改打印机的功能。您不能直接创建一个PrinterAttributes对象。你需要使用getPrinterAttributes()方法从一个Printer对象中获取它。

PageLayout类的一个实例表示打印作业的页面设置。默认情况下,它被设置为打印机默认值。PageLayout用于配置页面的可打印区域,该区域必须位于硬件的可打印区域内。如果页面呈现在硬件的可打印区域之外,内容将被剪切。你不能直接创建一个PageLayout对象。你需要使用PrintercreatePageLayout()方法之一来获得PageLayout

打印网页内容有一种特殊的方法。使用WebEngine类的print(PrinterJob job)方法打印引擎加载的网页。该方法不修改指定的job。在print()方法调用后,该作业可用于更多的打印。