Reputation: 2102
Here is the problem :
I currently have to print a a4, landscape page containing "parts" of my current javaFx scene.
The parts I have to print are parts of a BorderPane, I need to print the Left, Center and Bottom nodes so the printed page should look like this :
The most important thing to print is the Center part. It must be as wide/tall as possible. Basically, the Left part can take like max 10% of the page width, and the bottom could take like 10-15% of the page height. So, the center item should stretch to fit 90% of (printable) page width and 85% of (printable) page height.
I actually tried few solutions without success... I did create a print button that acts like this :
PrinterJob job = PrinterJob.createPrinterJob();
BorderPane bpToPrint = new BorderPane();
bpToPrint.setLeft(sourceBorderPane.getLeft());
bpToPrint.setCenter(sourceBorderPane.getCenter());
bpToPrint.setBottom(sourceBorderPane.getBottom());
if(job != null && job.showPrintDialog(root.getScene().getWindow())){
Printer printer = job.getPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.HARDWARE_MINIMUM);
double scaleX = pageLayout.getPrintableWidth() / bpToPrint.getWidth();
double scaleY = pageLayout.getPrintableHeight() / bpToPrint.getHeight();
Scale scale = new Scale(scaleX, scaleY);
bpToPrint.getTransforms().add(scale);
boolean success = job.printPage(pageLayout, bpToPrint);
if(success){
job.endJob();
}
}
// Since I did "move" the nodes to the "bpToPrint" borderpane
// I have to put them back to the "source"
sourceBorderPane.setBottom(bpToPrint.getBottom());
sourceBorderPane.setLeft(bpToPrint.getLeft());
sourceBorderPane.setCenter(bpToPrint.getCenter());
When I try this piece of code, the printer receives a job, "prints" a page... A blank page! Nothing printed on it...
If I remove the "scale" part, I have something that prints on the page, but it does NOT respect the page size at all...
I also did try to "snapshot" the nodes, so I can check their attributes (width, height), but did not find any way to "concat them" and print the result...
// WritableImage myLeft = bPane.getLeft().snapshot(null,null);
// System.out.println("left item width : "+myLeft.getWidth()+" height : "+myLeft.getHeight());
// WritableImage myCenter = bPane.getCenter().snapshot(null,null);
// System.out.println("center item width : "+myCenter.getWidth()+" height : "+myCenter.getHeight());
// WritableImage myBottom = bPane.getBottom().snapshot(null,null);
// System.out.println("Bottom item width : "+myBottom.getWidth()+" height : "+myBottom.getHeight());
I get this as output (just for information) :
left item width : 112.0 height : 892.0
center item width : 1746.0 height : 892.0
bottom item width : 1858.0 height : 95.0
Thanks for help/reading, any help really appreciated.
Update 01 :
Here is the "updated code" :
PrinterJob job = PrinterJob.createPrinterJob();
Node myLeft = root.getLeft();
Node myCenter = root.getCenter();
Node myBottom = root.getBottom();
//I will use a HBox to put the left & center, and a VBox to put hbox + bottom
VBox vToPrint = new VBox();
HBox hToPrint = new HBox();
hToPrint.getChildren().addAll(myLeft, myCenter);
vToPrint.getChildren().addAll(hToPrint,myBottom);
//Next lines come from the applyCss example
Group tempRoot = new Group();
Scene tempScene = new Scene(tempRoot);
tempRoot.getChildren().add(vToPrint);
tempRoot.applyCss();
tempRoot.layout();
if(job != null && job.showPrintDialog(root.getScene().getWindow())){
Printer printer = job.getPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.HARDWARE_MINIMUM);
double width = vToPrint.getWidth();
System.out.println("WIDTH : "+width);
//Output : 852.0
double height = vToPrint.getHeight();
System.out.println("HEIGHT : "+height);
//Output : 803.0
PrintResolution resolution = job.getJobSettings().getPrintResolution();
System.out.println("FEEDRESOLUTION : "+resolution.getFeedResolution());
//Output : 600
System.out.println("CROSSFEEDRESOLUTION : "+resolution.getCrossFeedResolution());
//Output : 600
width /= resolution.getFeedResolution();
System.out.println("NEW WIDTH : "+width);
//Output : 1.42
height /= resolution.getCrossFeedResolution();
System.out.println("NEW HEIGHT : "+height);
//Output : 1.3383333333333334
double scaleX = pageLayout.getPrintableWidth() / 72 / width;
System.out.println("SCALE X : "+scaleX);
//Output : 8.01056338028169
double scaleY = pageLayout.getPrintableHeight() / 72 / height;
System.out.println("SCALE Y : "+scaleY);
//Output : 5.935252677755962
Scale scale = new Scale(scaleX, scaleY);
vToPrint.getTransforms().add(scale);
boolean success = job.printPage(pageLayout, vToPrint);
if(success){
job.endJob();
}
}
root.setLeft(myLeft);
root.setCenter(myCenter);
root.setBottom(myBottom);
The print result shows just the top left corner of my vToPrint box, stretched over a whole a4 page...^^
And the output of width / height here are not the same as the snapshot width/height I got earlier...
left item width : 112.0 height : 892.0
center item width : 1746.0 height : 892.0
bottom item width : 1858.0 height : 95.0
This is really confusing...
Update 02 :
New try with snapshots & imageViews :
PrinterJob job = PrinterJob.createPrinterJob();
WritableImage myLeftImg = root.getLeft().snapshot(null, null);
WritableImage myCenterImg = root.getCenter().snapshot(null, null);
WritableImage myBottomImg = root.getBottom().snapshot(null, null);
ImageView myLeftImgView = new ImageView();
ImageView myCenterImgView = new ImageView();
ImageView myBottomImgView = new ImageView();
myLeftImgView.setImage(myLeftImg);
myCenterImgView.setImage(myCenterImg);
myBottomImgView.setImage(myBottomImg);
VBox vToPrint = new VBox();
HBox hToPrint = new HBox();
hToPrint.getChildren().addAll(myLeftImgView, myCenterImgView);
vToPrint.getChildren().addAll(hToPrint, myBottomImgView);
Group myRoot = new Group();
Scene myScene = new Scene(myRoot);
myRoot.getChildren().add(vToPrint);
myRoot.applyCss();
myRoot.layout();
if(job != null && job.showPrintDialog(root.getScene().getWindow())){
Printer printer = job.getPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.HARDWARE_MINIMUM);
boolean success = job.printPage(pageLayout, vToPrint);
if(success){
job.endJob();
}
}
I did remove the scale part of this piece of code and it actually gives me the following output on printing :
Update 03 :
Now I come to a result, but print resolution seems to fail. Instead of /72 the scale (as in Update 01) :
double scaleX = pageLayout.getPrintableWidth() / 72 / width;
I did divide it with 600 (like the resolution.getFeedResolution)... But it's probably wrong since I already divided it with 600 earlier... :
width /= resolution.getFeedResolution();
// ...
double scaleX = pageLayout.getPrintableWidth() / width / 600;
The result prints the "vToPrint" vbox on a a4 landscape page, BUT resolution is really, really, really, ... Bad! The whole thing is pixelized. Texts are readable but there is something wrong now with the print resolution...
I also tried to make a snapshot of the nodes to print, so I build my vToPrint VBox, then snapshot it, and save the snapshot into a .png file. This is good, the resolution is fine. So the problem is in the print part now...
Upvotes: 3
Views: 8410
Reputation: 11
Points are a measure of distance (1/72 inch), while pixels are still absolute values. The API can get you the amount of pixels (dots) per inch for your screen. Dividing the 72 points per inch by the retrieved amount of pixels per inch, delivers you the desired factor:
double scaleX = 72.0/(Screen.getPrimary().getDpi());
double scaleY = scaleX;
This is sufficient if you add a Scale-instance to 'getTransforms()' (as done in the question). If you would apply the scale as:
bp.setScaleX(scaleX);
one more correction would be required, because the method scales from the center:
bp.setTranslateX(bp.getTranslateX() - bp.getBoundsInParent().getMinX());
(Same holds for Y.)
Upvotes: 1
Reputation: 2102
Here is the solution,
After many researches it appears that the problem comes from the "temp" container I use to print. Even if applyCss(), layout(), or autosize() are called, it does NOT really render the container.
The actual solution I found is to use the "real" containers and just hide the items I don't want to print before starting the printJob.
Here is the current code I use :
PrinterJob job = PrinterJob.createPrinterJob();
if(job != null && job.showPrintDialog(root.getScene().getWindow())){
Printer printer = job.getPrinter();
PageLayout pageLayout = printer.createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, Printer.MarginType.HARDWARE_MINIMUM);
root.getTop().setVisible(false);
root.getTop().setManaged(false);
double width = root.getWidth();
double height = root.getHeight();
PrintResolution resolution = job.getJobSettings().getPrintResolution();
width /= resolution.getFeedResolution();
height /= resolution.getCrossFeedResolution();
double scaleX = pageLayout.getPrintableWidth()/width/600;
double scaleY = pageLayout.getPrintableHeight()/height/600;
Scale scale = new Scale(scaleX, scaleY);
root.getTransforms().add(scale);
boolean success = job.printPage(pageLayout, root);
if(success){
job.endJob();
}
root.getTransforms().remove(scale);
}
root.getTop().setManaged(true);
root.getTop().setVisible(true);
Upvotes: 3
Reputation: 44335
getPrintableWidth() and getPrintableHeight() return their sizes in 1⁄72 inch. getWidth() and getHeight() return sizes in pixels.
To divide equivalent units, you can convert pixels to inches using the printResolution property of JobSettings:
double width = bpToPrint.getWidth();
double height = bpToPrint.getHeight();
// Convert to inches
PrintResolution resolution = job.getJobSettings().getPrintResolution();
width /= resolution.getFeedResolution();
height /= resolution.getCrossFeedResolution();
double scaleX = pageLayout.getPrintableWidth() / 72 / width;
double scaleY = pageLayout.getPrintableHeight() / 72 / height;
Be aware that a Node’s getWidth() and getHeight() methods may return zero if it is not in a visible Stage, unless its applyCss()
and layout()
methods are called first, as described in the documentation of Node.applyCss():
As a more complete example, the following code uses
applyCss()
andlayout()
to find the width and height of the Button before the Stage has been shown. If either the call toapplyCss()
or the call tolayout()
is commented out, the calls togetWidth()
andgetHeight()
will return zero (until some time after the Stage is shown).
Upvotes: 2