I have a javafx TabPane that I want to be able to print. The TabPane has two tabs, and I want each tab to be printed on its own page. I created a button on one of the tabs with the onAction function to run a method called onPrint. The button will print a table within one tab and another table in the other.
The printer.fxml file
<ScrollPane fx:id="assessment"/>
<Pane fx:id="printPane">
<TabPane prefHeight="900.0" prefWidth="937.0" tabClosingPolicy="UNAVAILABLE">
<Tab text="Issues">
<AnchorPane fx:id="issuesTab" minHeight="0.0" minWidth="0.0" prefHeight="519.0" prefWidth="585.0">
<Text text="here is some text" />
<Button fx:id="print" layoutX="82.0" layoutY="60.0" mnemonicParsing="false" onAction="#onPrint" text="print" />
</AnchorPane>
</Tab>
<Tab text="Function">
<AnchorPane fx:id="FunctionTab" prefHeight="400.0" prefWidth="600.0">
<Text text="here is some text"/>
</AnchorPane>
</Tab>
</Pane>
</Scrollpane>
I've tried several different ways to get this to work properly. I'll walk through them below.
The printerController file is as follows:
public ScrollPane assessment;
public Pane printerPane;
public AnchorPane issuesTab;
public AnchorPane functionTab;
public void onPrint(ActionEvent event) {
printPane.getChildren().addAll(issuesTab, functionTab);
Printer printer = Printer.getDefaultPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.PORTRAIT, Printer.MarginType.HARDWARE_MINIMUM);
PrinterJob job = PrinterJob.createPrinterJob();
job.getJobSettings().setPageRanges(new PageRange(1, 2));
if (job.showPrintDialog(printPane.getScene().getWindow())){
boolean success = job.printPage(pageLayout, printPane);
if (success) {
job.endJob();
}
}
}
Currently this works but it adds both tables ontop of each other. I've tried to use the setPageRanges function to set a page range but I cannot get it to work propertly.
I then thought maybe I need to create a brand new pane for each tab and then add those to a group and it'll print separately.
private Node toPrint;
private Node printAllPages(){
if(toPrint== null){
Group group = new Group();
group.getChildren().addAll(
new Pane(issuesTab),
new Pane(functionTab)
);
notToPrint = group;
}
return notToPrint;
}
public void onPrint(ActionEvent event) {
PrinterJob job = PrinterJob.createPrinterJob();
if(job.showPrintDialog(notToPrint.getScene().getWindow())){
boolean success = job.printPage(printAllPages());
if(success){
job.endJob();
}
}
}
However this results in the following error - Caused by: java.lang.NullPointerException: Cannot invoke "javafx.scene.Node.getScene()" because "this.notToPrint" is null. I also don't believe this is the right approach.
Are there any Printer, PageLayout or PrinterJob settings that I need to apply so that each AnchorPane can be printed on separate pages?
Edits made:
Per @SedJ601 advice on how to fix this issue. I went through the link he provided and decided to try this -
PrintController.java
public TabPane allTabs;
Printer printer = Printer.getDefaultPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT);
PrinterJob job = PrinterJob.createPrinterJob();
if(job.showPrintDialog(allTabs.getScene().getWindow())){
double pagePrintableHeight = pageLayout.getPrintableHeight();
double pagePrintableWidth = pageLayout.getPrintableWidth();
double scaleX = pagePrintableWidth / allTabs.getBoundsInParent().getWidth();
double localScale = scaleX;
double numberOfPages = Math.ceil((allTabs.getPrefWidth()) * localScale) / pagePrintableHeight;
System.out.println(numberOfPages);
for(int i = 0; i < numberOfPages; i++){ job.printPage(pageLayout, allTabs.get(i)); } job.endJob();
}else{
System.out.println("This did not work");
}
Print.fxml
<ScrollPane fx:id="assessment">
<TabPane fx:id="allTabs" prefHeight="900.0" prefWidth="937.0" tabClosingPolicy="UNAVAILABLE">
<Tab text="Issues">
<content>
<AnchorPane fx:id="issuesTab" minHeight="0.0" minWidth="0.0" prefHeight="519.0" prefWidth="585.0">
<Text text="some text" />
</AnchorPane>
</content>
</Tab>
<Tab text="Function">
<content>
<AnchorPane fx:id="functionTab" prefHeight="400.0" prefWidth="600.0">
<Text text="some text" />
</AnchorPane>
</content>
</Tab>
</TabPane>
However this is printing only the first tab. I tried setting the numberOfPages value equal to 10.0 and still only the first tab prints. How do I edit either the for loop to loop through both tabs or change the numberOfPages to accurately find all pages?
If you are going to only have two Tabs
then I think the following code should work.
public void onPrint(ActionEvent event) {
printPane.getChildren().addAll(issuesTab, functionTab);
Printer printer = Printer.getDefaultPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.PORTRAIT, Printer.MarginType.HARDWARE_MINIMUM);
PrinterJob job = PrinterJob.createPrinterJob();
job.getJobSettings().setPageRanges(new PageRange(1, 2));
if (job.showPrintDialog(printPane.getScene().getWindow())){
//if the number of tabs is variable use a loop and print each tab.
boolean success = job.printPage(pageLayout, issuesTab);
boolean successTwo = job.printPage(pageLayout, functionTab);
if (success && successTwo) {
job.endJob();
}
else{
System.out.println("Printing Error!");
}
}
}
I typed the above code in here without testing it. It may have errors.
Here is a MCVE.
//Original code from https://jenkov.com/tutorials/javafx/tabpane.html
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.print.PageLayout;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.Printer;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TabPane;
import javafx.scene.control.Tab;
import javafx.scene.control.Label;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
VBox.setVgrow(tabPane, Priority.ALWAYS);
StackPane stackPane1 = new StackPane(new Label("This is tab 1"));
StackPane stackPane2 = new StackPane(new Label("This is tab 2"));
StackPane stackPane3 = new StackPane(new Label("This is tab 3"));
Tab tab1 = new Tab("Tab 1", stackPane1);
Tab tab2 = new Tab("Tab 2" , stackPane2);
Tab tab3 = new Tab("Tab 3" , stackPane3);
tabPane.getTabs().add(tab1);
tabPane.getTabs().add(tab2);
tabPane.getTabs().add(tab3);
VBox root = new VBox(tabPane);
Scene scene = new Scene(root, 500, 500);
Button button = new Button("Print Nodes");
button.setOnAction(actionEvent -> {
printNodes(tabPane.getTabs(), scene);
});
root.getChildren().add(button);
primaryStage.setScene(scene);
primaryStage.setTitle("JavaFX App");
primaryStage.show();
}
public void printNodes(ObservableList<Tab> nodesFromSameScene, Scene scene)
{
boolean control = true;
Printer printer = Printer.getDefaultPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.PORTRAIT, Printer.MarginType.HARDWARE_MINIMUM);
PrinterJob job = PrinterJob.createPrinterJob();
if (job.showPrintDialog(scene.getWindow()))
{
for(int i = 0; i < nodesFromSameScene.size(); i++)
{
if(!job.printPage(pageLayout, nodesFromSameScene.get(i).getContent()))
{
control = false;
break;
}
}
if(control)
{
job.endJob();
}
else
{
System.out.println("Printing Error!");
}
}
}
}
Output - Saved as PDF instead of printing!