Skip to content
Permalink
Browse files
8223717: javafx printing: Support Specifying Print to File in the API
Reviewed-by: kcr, psadhukhan
  • Loading branch information
prrace committed Jul 7, 2021
1 parent 6fe427f commit 386f6d7a56d9cfb334c210fccd29e1c5da58b591
Showing 3 changed files with 286 additions and 2 deletions.
@@ -51,6 +51,7 @@
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.standard.Chromaticity;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.Destination;
import javax.print.attribute.standard.DialogTypeSelection;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaPrintableArea;
@@ -68,6 +69,8 @@
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Set;
import com.sun.glass.ui.Application;
@@ -332,11 +335,22 @@ private boolean showPageDialogFromNestedLoop(Window owner) {
* equivalent FX public API JobSettings.
*/
private void updateJobName() {
String name = pJob2D.getJobName();
String name = pJob2D.getJobName();
if (!name.equals(settings.getJobName())) {
settings.setJobName(name);
}
}

private void updateOutputFile() {
Destination dest =
(Destination)printReqAttrSet.get(Destination.class);
if (dest != null) {
settings.setOutputFile(dest.getURI().getPath());
} else {
settings.setOutputFile("");
}
}

private void updateCopies() {
int nCopies = pJob2D.getCopies();
if (settings.getCopies() != nCopies) {
@@ -393,7 +407,7 @@ private void updateSides() {
* collation, then its been set by the user at some point,
* even if the current value is the printer default.
* If there is no value for collation in the attribute set,
* it means that we are u sing the printer default.
* it means that we are using the printer default.
*/
private void updateCollation() {
SheetCollate collate =
@@ -578,6 +592,7 @@ private void updatePrinter() {
private void updateSettingsFromDialog() {
updatePrinter();
updateJobName();
updateOutputFile();
updateCopies();
updatePageRanges();
updateSides();
@@ -591,6 +606,7 @@ private void updateSettingsFromDialog() {

private void syncSettingsToAttributes() {
syncJobName();
syncOutputFile();
syncCopies();
syncPageRanges();
syncSides();
@@ -606,6 +622,17 @@ private void syncJobName() {
pJob2D.setJobName(settings.getJobName());
}

private void syncOutputFile() {
printReqAttrSet.remove(Destination.class);
String file = settings.getOutputFile();
if (file != null && !file.isEmpty()) {
// check SE, check access ?
URI uri = (new File(file)).toURI();
Destination d = new Destination(uri);
printReqAttrSet.add(d);
}
}

private void syncCopies() {
pJob2D.setCopies(settings.getCopies());
printReqAttrSet.add(new Copies(settings.getCopies()));
@@ -797,6 +824,10 @@ private void checkPermissions() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPrintJobAccess();
String file = settings.getOutputFile();
if (file != null && !file.isEmpty()) {
security.checkWrite(file);
}
}
}

@@ -462,6 +462,98 @@ public void setJobName(String name) {
}
/////////////////////// END JOBNAME /////////////////////

/////////////////////// START OUTPUTFILE /////////////////////

private SimpleStringProperty outputFile;

/**
* A {@code StringProperty} representing the
* name of a filesystem file, to which the platform printer
* driver should spool the rendered print data.
* <p>
* Applications can use this to programmatically request print-to-file
* behavior where the native print system is capable of spooling the
* output to a filesystem file, rather than the printer device.
* <p>
* This is often useful where the printer driver generates a format
* such as Postscript or PDF, and the application intends to distribute
* the result instead of printing it, or for some other reason the
* application does not want physical media (paper) emitted by the printer.
* <p>
* The default value is an empty string, which is interpreted as unset,
* equivalent to null, which means output is sent to the printer.
* So in order to reset to print to the printer, clear the value of
* this property by setting it to null or an empty string.
* <p>
* Additionally if the application displays a printer dialog which allows
* the user to specify a file destination, including altering an application
* specified file destination, the value of this property will reflect that
* user-specified choice, including clearing it to reset to print to
* the printer, if the user does so.
* <p>
* If the print system does not support print-to-file, then this
* setting will be ignored.
* <p>
* If the specified name specifies a non-existent path, or does not specify
* a user writable file, when printing the results are platform-dependent.
* Possible behaviours might include replacement with a default output file location,
* printing to the printer instead, or a platform printing error.
* If a {@code SecurityManager} is installed and it denies access to the
* specified file a {@code SecurityException} may be thrown.
*
* @defaultValue an empty string
*
* @return the name of a printer spool file
* @since 17
*/
public final StringProperty outputFileProperty() {
if (outputFile == null) {
outputFile =
new SimpleStringProperty(JobSettings.this, "outputFile", "") {

@Override
public void set(String value) {
if (!isJobNew()) {
return;
}
if (value == null) {
value = "";
}
super.set(value);
}

@Override
public void bind(ObservableValue<? extends String>
rawObservable) {
throw new
RuntimeException("OutputFile property cannot be bound");
}

@Override
public void bindBidirectional(Property<String> other) {
throw new
RuntimeException("OutputFile property cannot be bound");
}

@Override
public String toString() {
return get();
}
};
}
return outputFile;
}

public String getOutputFile() {
return outputFileProperty().get();
}


public void setOutputFile(String filePath) {
outputFileProperty().set(filePath);
}
/////////////////////// END OUTPUTFILE /////////////////////

//////////////////////// START COPIES ////////////////////////

private IntegerProperty copies;
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import java.io.File;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.print.*;

import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
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.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class PrintToFileTest extends Application {

private final int WIDTH = 400;
private final int HEIGHT = 400;

private volatile boolean passed = false;
private volatile boolean failed = false;
private Scene scene;
private VBox root;

public static void main(String[] args) {
launch(args);
}

public void start(Stage stage) {
stage.setWidth(WIDTH);
stage.setHeight(HEIGHT);
stage.setTitle("Printing to file test");
Rectangle2D bds = Screen.getPrimary().getVisualBounds();
stage.setX((bds.getWidth() - WIDTH) / 2);
stage.setY((bds.getHeight() - HEIGHT) / 2);
stage.setScene(createScene());
stage.show();
}

static final String instructions =
"This tests that programmatically specifying print to file works.\n" +
"Select the Print button to run the test\n";

static final String noprinter =
"There are no printers installed. This test cannot be run\n";

private TextArea createInfo(String msg) {
TextArea t = new TextArea(msg);
t.setWrapText(true);
t.setEditable(false);
return t;
}

private Scene createScene() {

root = new VBox();
scene = new Scene(root);

String msg = instructions;
if (Printer.getDefaultPrinter() == null) {
msg = noprinter;
}
TextArea info = createInfo(msg);
root.getChildren().add(info);

Button print = new Button("Print");
print.setLayoutX(80);
print.setLayoutY(200);
print.setOnAction(e -> runTest());
root.getChildren().add(print);

return scene;
}

public void runTest() {
new Thread(() -> {
passed = false;
failed = false;
System.out.println("START OF PRINT JOB");
PrinterJob job = PrinterJob.createPrinterJob();
JobSettings settings = job.getJobSettings();
String fileName = "printtofiletest.prn";
settings.outputFileProperty().set(fileName);
String destFileName = settings.outputFileProperty().get();
System.out.println("dest="+ destFileName);
File f = new File(destFileName);
f.delete();
Platform.runLater(() -> {
Text t = new Text("file="+settings.getOutputFile());
root.getChildren().add(t);
});
Text printNode = new Text("\n\nTEST\nabc\ndef");
job.printPage(printNode);
job.endJob();
try {
// wait for printer spooler to create the file.
Thread.sleep(3000);
} catch (InterruptedException e) {
}
if (f.exists()) {
System.out.println("created file " + f);
passed = true;
} else {
failed = true;
}
System.out.println("END OF PRINT JOB");
}).start();
new Thread(() -> {
while (!passed && !failed) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
Platform.runLater(() -> displayMessage());

}).start();
}

private void displayMessage() {
Text t = new Text();
if (passed) {
t.setText("TEST PASSED!");
} else {
t.setText("TEST FAILED!");
}
root.getChildren().add(t);
}
}

1 comment on commit 386f6d7

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on 386f6d7 Jul 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.