Skip to content

Commit 386f6d7

Browse files
committed
8223717: javafx printing: Support Specifying Print to File in the API
Reviewed-by: kcr, psadhukhan
1 parent 6fe427f commit 386f6d7

File tree

3 files changed

+286
-2
lines changed

3 files changed

+286
-2
lines changed

modules/javafx.graphics/src/main/java/com/sun/prism/j2d/print/J2DPrinterJob.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import javax.print.attribute.Size2DSyntax;
5252
import javax.print.attribute.standard.Chromaticity;
5353
import javax.print.attribute.standard.Copies;
54+
import javax.print.attribute.standard.Destination;
5455
import javax.print.attribute.standard.DialogTypeSelection;
5556
import javax.print.attribute.standard.Media;
5657
import javax.print.attribute.standard.MediaPrintableArea;
@@ -68,6 +69,8 @@
6869
import java.awt.print.Pageable;
6970
import java.awt.print.Printable;
7071
import java.awt.print.PrinterException;
72+
import java.io.File;
73+
import java.net.URI;
7174
import java.util.ArrayList;
7275
import java.util.Set;
7376
import com.sun.glass.ui.Application;
@@ -332,11 +335,22 @@ private boolean showPageDialogFromNestedLoop(Window owner) {
332335
* equivalent FX public API JobSettings.
333336
*/
334337
private void updateJobName() {
335-
String name = pJob2D.getJobName();
338+
String name = pJob2D.getJobName();
336339
if (!name.equals(settings.getJobName())) {
337340
settings.setJobName(name);
338341
}
339342
}
343+
344+
private void updateOutputFile() {
345+
Destination dest =
346+
(Destination)printReqAttrSet.get(Destination.class);
347+
if (dest != null) {
348+
settings.setOutputFile(dest.getURI().getPath());
349+
} else {
350+
settings.setOutputFile("");
351+
}
352+
}
353+
340354
private void updateCopies() {
341355
int nCopies = pJob2D.getCopies();
342356
if (settings.getCopies() != nCopies) {
@@ -393,7 +407,7 @@ private void updateSides() {
393407
* collation, then its been set by the user at some point,
394408
* even if the current value is the printer default.
395409
* If there is no value for collation in the attribute set,
396-
* it means that we are u sing the printer default.
410+
* it means that we are using the printer default.
397411
*/
398412
private void updateCollation() {
399413
SheetCollate collate =
@@ -578,6 +592,7 @@ private void updatePrinter() {
578592
private void updateSettingsFromDialog() {
579593
updatePrinter();
580594
updateJobName();
595+
updateOutputFile();
581596
updateCopies();
582597
updatePageRanges();
583598
updateSides();
@@ -591,6 +606,7 @@ private void updateSettingsFromDialog() {
591606

592607
private void syncSettingsToAttributes() {
593608
syncJobName();
609+
syncOutputFile();
594610
syncCopies();
595611
syncPageRanges();
596612
syncSides();
@@ -606,6 +622,17 @@ private void syncJobName() {
606622
pJob2D.setJobName(settings.getJobName());
607623
}
608624

625+
private void syncOutputFile() {
626+
printReqAttrSet.remove(Destination.class);
627+
String file = settings.getOutputFile();
628+
if (file != null && !file.isEmpty()) {
629+
// check SE, check access ?
630+
URI uri = (new File(file)).toURI();
631+
Destination d = new Destination(uri);
632+
printReqAttrSet.add(d);
633+
}
634+
}
635+
609636
private void syncCopies() {
610637
pJob2D.setCopies(settings.getCopies());
611638
printReqAttrSet.add(new Copies(settings.getCopies()));
@@ -797,6 +824,10 @@ private void checkPermissions() {
797824
SecurityManager security = System.getSecurityManager();
798825
if (security != null) {
799826
security.checkPrintJobAccess();
827+
String file = settings.getOutputFile();
828+
if (file != null && !file.isEmpty()) {
829+
security.checkWrite(file);
830+
}
800831
}
801832
}
802833

modules/javafx.graphics/src/main/java/javafx/print/JobSettings.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,98 @@ public void setJobName(String name) {
462462
}
463463
/////////////////////// END JOBNAME /////////////////////
464464

465+
/////////////////////// START OUTPUTFILE /////////////////////
466+
467+
private SimpleStringProperty outputFile;
468+
469+
/**
470+
* A {@code StringProperty} representing the
471+
* name of a filesystem file, to which the platform printer
472+
* driver should spool the rendered print data.
473+
* <p>
474+
* Applications can use this to programmatically request print-to-file
475+
* behavior where the native print system is capable of spooling the
476+
* output to a filesystem file, rather than the printer device.
477+
* <p>
478+
* This is often useful where the printer driver generates a format
479+
* such as Postscript or PDF, and the application intends to distribute
480+
* the result instead of printing it, or for some other reason the
481+
* application does not want physical media (paper) emitted by the printer.
482+
* <p>
483+
* The default value is an empty string, which is interpreted as unset,
484+
* equivalent to null, which means output is sent to the printer.
485+
* So in order to reset to print to the printer, clear the value of
486+
* this property by setting it to null or an empty string.
487+
* <p>
488+
* Additionally if the application displays a printer dialog which allows
489+
* the user to specify a file destination, including altering an application
490+
* specified file destination, the value of this property will reflect that
491+
* user-specified choice, including clearing it to reset to print to
492+
* the printer, if the user does so.
493+
* <p>
494+
* If the print system does not support print-to-file, then this
495+
* setting will be ignored.
496+
* <p>
497+
* If the specified name specifies a non-existent path, or does not specify
498+
* a user writable file, when printing the results are platform-dependent.
499+
* Possible behaviours might include replacement with a default output file location,
500+
* printing to the printer instead, or a platform printing error.
501+
* If a {@code SecurityManager} is installed and it denies access to the
502+
* specified file a {@code SecurityException} may be thrown.
503+
*
504+
* @defaultValue an empty string
505+
*
506+
* @return the name of a printer spool file
507+
* @since 17
508+
*/
509+
public final StringProperty outputFileProperty() {
510+
if (outputFile == null) {
511+
outputFile =
512+
new SimpleStringProperty(JobSettings.this, "outputFile", "") {
513+
514+
@Override
515+
public void set(String value) {
516+
if (!isJobNew()) {
517+
return;
518+
}
519+
if (value == null) {
520+
value = "";
521+
}
522+
super.set(value);
523+
}
524+
525+
@Override
526+
public void bind(ObservableValue<? extends String>
527+
rawObservable) {
528+
throw new
529+
RuntimeException("OutputFile property cannot be bound");
530+
}
531+
532+
@Override
533+
public void bindBidirectional(Property<String> other) {
534+
throw new
535+
RuntimeException("OutputFile property cannot be bound");
536+
}
537+
538+
@Override
539+
public String toString() {
540+
return get();
541+
}
542+
};
543+
}
544+
return outputFile;
545+
}
546+
547+
public String getOutputFile() {
548+
return outputFileProperty().get();
549+
}
550+
551+
552+
public void setOutputFile(String filePath) {
553+
outputFileProperty().set(filePath);
554+
}
555+
/////////////////////// END OUTPUTFILE /////////////////////
556+
465557
//////////////////////// START COPIES ////////////////////////
466558

467559
private IntegerProperty copies;
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
import java.io.File;
27+
28+
import javafx.application.Platform;
29+
import javafx.collections.FXCollections;
30+
import javafx.collections.ObservableList;
31+
import javafx.print.*;
32+
33+
import javafx.application.Application;
34+
import javafx.geometry.Rectangle2D;
35+
import javafx.scene.Group;
36+
import javafx.scene.Node;
37+
import javafx.scene.Scene;
38+
import javafx.scene.control.Button;
39+
import javafx.scene.control.Label;
40+
import javafx.scene.control.TextArea;
41+
import javafx.scene.layout.*;
42+
import javafx.scene.paint.Color;
43+
import javafx.scene.text.Text;
44+
import javafx.stage.Screen;
45+
import javafx.stage.Stage;
46+
47+
public class PrintToFileTest extends Application {
48+
49+
private final int WIDTH = 400;
50+
private final int HEIGHT = 400;
51+
52+
private volatile boolean passed = false;
53+
private volatile boolean failed = false;
54+
private Scene scene;
55+
private VBox root;
56+
57+
public static void main(String[] args) {
58+
launch(args);
59+
}
60+
61+
public void start(Stage stage) {
62+
stage.setWidth(WIDTH);
63+
stage.setHeight(HEIGHT);
64+
stage.setTitle("Printing to file test");
65+
Rectangle2D bds = Screen.getPrimary().getVisualBounds();
66+
stage.setX((bds.getWidth() - WIDTH) / 2);
67+
stage.setY((bds.getHeight() - HEIGHT) / 2);
68+
stage.setScene(createScene());
69+
stage.show();
70+
}
71+
72+
static final String instructions =
73+
"This tests that programmatically specifying print to file works.\n" +
74+
"Select the Print button to run the test\n";
75+
76+
static final String noprinter =
77+
"There are no printers installed. This test cannot be run\n";
78+
79+
private TextArea createInfo(String msg) {
80+
TextArea t = new TextArea(msg);
81+
t.setWrapText(true);
82+
t.setEditable(false);
83+
return t;
84+
}
85+
86+
private Scene createScene() {
87+
88+
root = new VBox();
89+
scene = new Scene(root);
90+
91+
String msg = instructions;
92+
if (Printer.getDefaultPrinter() == null) {
93+
msg = noprinter;
94+
}
95+
TextArea info = createInfo(msg);
96+
root.getChildren().add(info);
97+
98+
Button print = new Button("Print");
99+
print.setLayoutX(80);
100+
print.setLayoutY(200);
101+
print.setOnAction(e -> runTest());
102+
root.getChildren().add(print);
103+
104+
return scene;
105+
}
106+
107+
public void runTest() {
108+
new Thread(() -> {
109+
passed = false;
110+
failed = false;
111+
System.out.println("START OF PRINT JOB");
112+
PrinterJob job = PrinterJob.createPrinterJob();
113+
JobSettings settings = job.getJobSettings();
114+
String fileName = "printtofiletest.prn";
115+
settings.outputFileProperty().set(fileName);
116+
String destFileName = settings.outputFileProperty().get();
117+
System.out.println("dest="+ destFileName);
118+
File f = new File(destFileName);
119+
f.delete();
120+
Platform.runLater(() -> {
121+
Text t = new Text("file="+settings.getOutputFile());
122+
root.getChildren().add(t);
123+
});
124+
Text printNode = new Text("\n\nTEST\nabc\ndef");
125+
job.printPage(printNode);
126+
job.endJob();
127+
try {
128+
// wait for printer spooler to create the file.
129+
Thread.sleep(3000);
130+
} catch (InterruptedException e) {
131+
}
132+
if (f.exists()) {
133+
System.out.println("created file " + f);
134+
passed = true;
135+
} else {
136+
failed = true;
137+
}
138+
System.out.println("END OF PRINT JOB");
139+
}).start();
140+
new Thread(() -> {
141+
while (!passed && !failed) {
142+
try {
143+
Thread.sleep(500);
144+
} catch (InterruptedException e) {
145+
}
146+
}
147+
Platform.runLater(() -> displayMessage());
148+
149+
}).start();
150+
}
151+
152+
private void displayMessage() {
153+
Text t = new Text();
154+
if (passed) {
155+
t.setText("TEST PASSED!");
156+
} else {
157+
t.setText("TEST FAILED!");
158+
}
159+
root.getChildren().add(t);
160+
}
161+
}

0 commit comments

Comments
 (0)