JavaFX PrintAPI wrong PaperSource

I'm using the JavaFx Print-Dialog to customize the print job. All properties will be stored in the PrinterJob#JobSettings variable, but when I receive the paper source from the jobSetting the paper source is always the default.

How can I get the paper source that I set?

Here is a short example:

public class PrinterPaperSourceTest extends Application {
    public static void main(String[] args) {
        launch( args );
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Printer");
        Button btn = new Button();
        btn.setText("Show Printer Settings ");
        btn.setOnAction( new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                PrinterJob job = PrinterJob.createPrinterJob(Printer.getDefaultPrinter());
                job.showPageSetupDialog(null);
                Alert alert = new Alert(AlertType.INFORMATION);
                PaperSource paperSource = job.getJobSettings().getPaperSource();
                alert.setContentText("PaperSource: " + paperSource.getName());
                alert.show();
            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }
}

I don't have an answer but I will try to explain why it happens and why it will not be easy to fix. This behavior seems to be influenced by Internet Printing Protocol (IPP) specifications and is caused by the way IPP is implemented by Java Print Service API (to which JavaFX print jobs delegate). Below is a fragment from Oracle's tech note that explains limitations of manually setting the paper source (https://docs.oracle.com/javase/8/docs/technotes/guides/jps/spec/attributes.fm5.html):

Media is the IPP attribute that identifies the medium on which to print. The Media attribute is an important attribute to understand, but is relatively complex.

The Java Print Service API defines three subclasses of the abstract class Media to reflect the overloaded Media attribute in the IPP specification: MediaSizeName, MediaName and MediaTray. All the Media subclasses have the Media category, for which each subclass defines different standard attribute values. […]

The value of the Media attribute is always a String, but because the attribute is overloaded, its value determines the type of media to which the attribute refers. For example, the IPP pre-defined set of attribute values include the values "a4" and "top-tray". If Media is set to the value "a4" then the Media attribute refers to the size of paper, but if Media is set to "top-tray" then the Media attribute refers to the paper source. […]

In most cases, applications will use either MediaSizeName or MediaTray. The MediaSizeName class enumerates the media by size. The MediaTray class enumerates the paper trays on a printer, which usually include a main tray and a manual feed tray. The IPP 1.1 specification does not provide for specifying both the media size and the media tray at the same time, which means, for example, that an application cannot request size A4 paper from the manual tray. A future revision of the IPP specification might provide for a way to request more than one type of media at a time, in which case the JPS API will most likely be enhanced to implement this change.

So, MediaTray (or paper source) is not an independent parameter and cannot be set if the Media attribute is already defined by one of the other two ways ( MediaSizeName or MediaName ). This is exactly what happens with page setup dialogs.

J2DPrinterJob class (from com.sun.prism.j2d.print package) contains the dialog code and updates print job settings (I found this by debugging your application). Below is the method from this class that updates the paper source setting from the dialog.

private void updatePaperSource() {
    Media m = (Media)printReqAttrSet.get(Media.class);
    if (m instanceof MediaTray) {
        PaperSource s = j2dPrinter.getPaperSource((MediaTray)m);
        if (s != null) {
            settings.setPaperSource(s);
        }
    }
}

I tested different scenarios and the result was the same: by the time updatePaperSource() starts execution the Media attribute is already defined as MediaSizeName type. So the statements in the if branches are never executed and that's why the paper source is not updated.

I suspect that paper type or paper size have priority over paper source and because the page setup dialog always defines paper type (there is no 'Automatic' option), it overloads the selection of paper source to avoid attribute conflict. This essentially makes this option useless.

It may be a bug in JDK or an intentional design decision. In any case, I don't see an easy way to solve this problem staying within JavaFX considering that it comes from private methods in Java's internal API.


Printing API appeared in fx8.0. And it can print nodes. You can create printer job with javafx.print.PrinterJob class. But it prints only region that fits to a printed page, and not the one that you on a screen. So you need to make your node fit page(scale, translate, etc) by hands.

You can use this code. Hope it will help you.

/**
 * Prints the current page displayed within the internal browser, not necessarily the {@link #presentationProperty()}.
 * If no printers are installed on the system, an awareness is displayed.
 */
public final void print() {
    final PrinterJob job = PrinterJob.createPrinterJob();

    if (job != null) {
        if (job.showPrintDialog(null)) {
            if(this.getPresentation().getArchive() != null) {
                final String extension = ".".concat(this.getPresentation().getArchiveExtension());
                final int indexOfExtension = this.getPresentation().getArchive().getName().indexOf(extension);
                final String jobName = this.getPresentation().getArchive().getName().substring(0, indexOfExtension);
                job.getJobSettings().setJobName(jobName);
            }

            job.getJobSettings().setPrintQuality(PrintQuality.HIGH);
            job.getJobSettings().setPageLayout(job.getPrinter().createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, 0, 0, 0, 0));

            this.internalBrowser.getEngine().print(job);
            job.endJob();
        } else {
            job.cancelJob();
        }
    } else {
        DialogHelper.showError("No printer", "There is no printer installed on your system.");
    }
}

Resource Link:

  • javafx.print.PrinterJob examples
  • Introduction by Example: JavaFX 8 Printing

  • After a lot of searching I have found a way to print to a different tray with javafx and this was the first place I looked so I figured here would be the best spot to post my solution it may vary with different tray names mine was Tray 2 it will also print out all trays that are available

    private void printImage(Node node) {
        PrinterJob job = PrinterJob.createPrinterJob();
        if (job != null) {
            JobSettings js = job.getJobSettings();
            PaperSource papersource = js.getPaperSource();
            System.out.println("PaperSource=" + papersource);
            PrinterAttributes pa = printer.getPrinterAttributes();
            Set<PaperSource> s = pa.getSupportedPaperSources();
            System.out.println("# of papersources=" + s.size());
            if (s != null) {
                for (PaperSource newPaperSource : s) {
                    System.out.println("newpapersource= " + newPaperSource);
    
                    //Here is where you would put the tray name that is appropriate
                    //in the contains section
                    if(newPaperSource.toString().contains("Tray 2"))
                        js.setPaperSource(newPaperSource);
                }
            }
            job.getJobSettings().setJobName("Whatever You want");
            ObjectProperty<PaperSource> sources = job.getJobSettings().paperSourceProperty();
            System.out.println(sources.toString());
            boolean success = job.printPage(node);
            if (success) {
                System.out.println("PRINTING FINISHED");
                job.endJob();
                //Stage mainStage = (Stage) root.getScene().getWindow();
                //mainStage.close();
            }
        }
    }
    

    Here's My output:

    PaperSource=Paper source : Automatic
    # of papersources=6
    newpapersource= Paper source :
    newpapersource= Paper source :  Manual Feed in Tray 1
    newpapersource= Paper source :  Printer auto select
    newpapersource= Paper source :  Tray 1
    newpapersource= Paper source :  Tray 2
    newpapersource= Paper source : Form-Source
    ObjectProperty [bean:  Collation = UNCOLLATED
     Copies = 1
     Sides = ONE_SIDED
     JobName = Whatever
     Page ranges = null
     Print color = COLOR
     Print quality = NORMAL
     Print resolution = Feed res=600dpi. Cross Feed res=600dpi.
     Paper source = Paper source :  Tray 2
     Page layout = Paper=Paper: Letter size=8.5x11.0 INCH Orient=PORTRAIT leftMargin=54.0 rightMargin=54.0 topMargin=54.0 bottomMargin=54.0, name: paperSource, value: Paper source :  Tray 2]
    PRINTING FINISHED
    
    链接地址: http://www.djcxy.com/p/34776.html

    上一篇: System.TypeLoadException:无法加载类型

    下一篇: JavaFX PrintAPI错误的PaperSource