使用JavaFX打印节点

时间:2017-06-21 14:19:40

标签: java javafx printing

问题在于:

我目前必须打印一个包含当前javaFx场景“部分”的a4横向页面。

我必须打印的部件是BorderPane的一部分,我需要打印Left,Center和Bottom节点,这样打印的页面应如下所示:

Second A4 Template Image

打印最重要的是中心部分。它必须尽可能宽/高。基本上,左边部分可以占据页面宽度的最大10%,底部可以占页面高度的10-15%。因此,中心项目应拉伸至适合(可打印)页面宽度的90%和(可打印)页面高度的85%。

我实际上尝试了几个没有成功的解决方案......我确实创建了一个像这样的打印按钮:

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

当我尝试这段代码时,打印机会收到一份工作,“打印”一页......一个空白页!没有印在上面......

如果删除“比例”部分,我会在页面上打印一些内容,但它根本不尊重页面大小......

我也尝试“快照”节点,所以我可以检查它们的属性(宽度,高度),但没有找到任何方法来“连接它们”并打印结果......

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

我将此作为输出(仅供参考):

left item width : 112.0 height : 892.0
center item width : 1746.0 height : 892.0
bottom item width : 1858.0 height : 95.0

感谢您的帮助/阅读,任何帮助都非常感激。

更新01:

这是“更新的代码”:

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

打印结果显示我的vToPrint框的左上角,整个a4页面已拉伸... ^^

此处宽度/高度的输出与我之前获得的快照宽度/高度不同...

left item width : 112.0 height : 892.0
center item width : 1746.0 height : 892.0
bottom item width : 1858.0 height : 95.0

这真令人困惑......

更新02:

使用快照的新尝试& 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();
    }
}

我确实删除了这段代码的缩放部分,它实际上在打印时给出了以下输出:

enter image description here

更新03:

现在我得出一个结果,但打印分辨率似乎失败了。而不是/ 72比例(如更新01中所示):

double scaleX = pageLayout.getPrintableWidth() / 72 / width;

我把它除以600(就像resolution.getFeedResolution一样)......但是它可能是错的,因为我之前已经将它除以600 ...:

width /= resolution.getFeedResolution();
// ... 
double scaleX = pageLayout.getPrintableWidth() / width / 600;

结果在a4横向页面上打印“vToPrint”vbox,但分辨率真的非常非常......糟糕!整个事情是像素化的。文本是可读的,但现在打印分辨率有问题......

我还尝试制作要打印的节点的快照,因此我构建了我的vToPrint VBox,然后对其进行快照,并将快照保存到.png文件中。这很好,分辨率很好。所以问题出现在印刷部分......

3 个答案:

答案 0 :(得分:2)

getPrintableWidth()getPrintableHeight() 1 / 72 英寸中返回其大小。 getWidth()和getHeight()以像素为单位返回大小。

要划分等效单位,您可以使用JobSettings的printResolution属性将像素转换为英寸:

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;

请注意,如果Node的getWidth()和getHeight()方法不在可见阶段,则它可能返回零,除非首先调用applyCss()layout()方法,如{ {3}}:

  

作为一个更完整的示例,以下代码使用applyCss()layout()在显示舞台之前查找Button的宽度和高度。如果对applyCss()的调用或对layout()的调用已被注释掉,则对getWidth()getHeight()的调用将返回零(直到显示舞台后的某个时间) 。

答案 1 :(得分:1)

这是解决方案,

经过多次研究后,问题似乎来自我用来打印的“临时”容器。即使调用applyCss(),layout()或autosize(),它也不会真正呈现容器。

我找到的实际解决方案是使用“真正的”容器,并在启动printJob之前隐藏我不想打印的项目。

以下是我使用的当前代码:

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

答案 2 :(得分:0)

点是距离(1/72英寸)的度量,而像素仍然是绝对值。 该API可以为您提供屏幕每英寸的像素(点)数量。将每英寸72点除以每英寸检索到的像素数量,可为您提供所需的因子:

double scaleX = 72.0/(Screen.getPrimary().getDpi());
double scaleY = scaleX;

如果将Scale实例添加到“ getTransforms()”(在问题中已完成),这就足够了。如果将比例尺应用为:

bp.setScaleX(scaleX);

还需要进行一次更正,因为该方法从中心缩放:

bp.setTranslateX(bp.getTranslateX() - bp.getBoundsInParent().getMinX());

(Y也是如此)