Skip to content

Commit d900e31

Browse files
committed
8211055: Provide print to a file (PDF) feature even when printer was not connected
Reviewed-by: clanger Backport-of: 9b0d241
1 parent 7751352 commit d900e31

File tree

3 files changed

+232
-2
lines changed

3 files changed

+232
-2
lines changed

src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,18 +254,22 @@ private void completePrintLoop() {
254254
}
255255
}
256256

257+
boolean isPrintToFile = false;
258+
private void setPrintToFile(boolean printToFile) {
259+
isPrintToFile = printToFile;
260+
}
261+
257262
@Override
258263
public void print(PrintRequestAttributeSet attributes) throws PrinterException {
259264
// NOTE: Some of this code is copied from RasterPrinterJob.
260265

261-
262266
// this code uses javax.print APIs
263267
// this will make it print directly to the printer
264268
// this will not work if the user clicks on the "Preview" button
265269
// However if the printer is a StreamPrintService, its the right path.
266270
PrintService psvc = getPrintService();
267271

268-
if (psvc == null) {
272+
if (psvc == null && !isPrintToFile) {
269273
throw new PrinterException("No print service found.");
270274
}
271275

src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,14 @@ static void nsPrintInfoToJavaPrinterJob(JNIEnv* env, NSPrintInfo* src, jobject d
358358
DECLARE_METHOD(jm_setCopiesAttribute, sjc_CPrinterJob, "setCopiesAttribute", "(I)V");
359359
DECLARE_METHOD(jm_setCollated, sjc_CPrinterJob, "setCollated", "(Z)V");
360360
DECLARE_METHOD(jm_setPageRangeAttribute, sjc_CPrinterJob, "setPageRangeAttribute", "(IIZ)V");
361+
DECLARE_METHOD(jm_setPrintToFile, sjc_CPrinterJob, "setPrintToFile", "(Z)V");
362+
363+
NSPrintJobDispositionValue jobDisposition = [src jobDisposition];
364+
if (jobDisposition == NSPrintSaveJob) {
365+
(*env)->CallVoidMethod(env, dstPrinterJob, jm_setPrintToFile, true);
366+
} else {
367+
(*env)->CallVoidMethod(env, dstPrinterJob, jm_setPrintToFile, false);
368+
}
361369

362370
// get the selected printer's name, and set the appropriate PrintService on the Java side
363371
NSString *name = [[src printer] name];
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Copyright (c) 2018, 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
@bug 8211055
26+
@summary Verifies "print to file" works even when there is no printer
27+
@requires (os.family == "mac")
28+
@run main/manual TestSaveFileWithoutPrinter
29+
*/
30+
31+
import java.awt.BorderLayout;
32+
import java.awt.FlowLayout;
33+
import java.awt.Graphics;
34+
import java.awt.event.WindowAdapter;
35+
import java.awt.event.WindowEvent;
36+
import java.awt.print.Printable;
37+
import java.awt.print.PageFormat;
38+
import java.awt.print.PrinterException;
39+
import java.awt.print.PrinterJob;
40+
import java.util.concurrent.CountDownLatch;
41+
import java.util.concurrent.TimeUnit;
42+
import javax.swing.JButton;
43+
import javax.swing.JDialog;
44+
import javax.swing.JLabel;
45+
import javax.swing.JPanel;
46+
import javax.swing.JTextArea;
47+
import javax.swing.SwingUtilities;
48+
import javax.swing.Timer;
49+
import javax.swing.WindowConstants;
50+
51+
public class TestSaveFileWithoutPrinter implements Printable {
52+
private static final CountDownLatch testEndedSignal = new CountDownLatch(1);
53+
private static final int testTimeout = 300000;
54+
private static volatile String testFailureMsg;
55+
private static volatile boolean testPassed;
56+
private static volatile boolean testFinished;
57+
private static PrinterJob job;
58+
59+
public static void main(String[] args) {
60+
job = PrinterJob.getPrinterJob();
61+
if (job.getPrintService() != null) {
62+
System.out.println("This test requires no printers to be installed. Exiting.");
63+
return;
64+
}
65+
SwingUtilities.invokeLater(() -> createAndShowTestDialog());
66+
67+
try {
68+
if (!testEndedSignal.await(testTimeout, TimeUnit.MILLISECONDS)) {
69+
throw new RuntimeException(String.format(
70+
"Test timeout '%d ms' elapsed.", testTimeout));
71+
}
72+
if (!testPassed) {
73+
String failureMsg = testFailureMsg;
74+
if ((failureMsg != null) && (!failureMsg.trim().isEmpty())) {
75+
throw new RuntimeException(failureMsg);
76+
} else {
77+
throw new RuntimeException("Test failed.");
78+
}
79+
}
80+
} catch (InterruptedException ie) {
81+
throw new RuntimeException(ie);
82+
} finally {
83+
testFinished = true;
84+
}
85+
}
86+
87+
private static void doTest() {
88+
job.setPrintable(new TestSaveFileWithoutPrinter());
89+
if (job.printDialog()) {
90+
try {
91+
job.print();
92+
} catch (PrinterException pe) {
93+
throw new RuntimeException(pe);
94+
}
95+
}
96+
}
97+
98+
private static void pass() {
99+
testPassed = true;
100+
testEndedSignal.countDown();
101+
}
102+
103+
private static void fail(String failureMsg) {
104+
testFailureMsg = failureMsg;
105+
testPassed = false;
106+
testEndedSignal.countDown();
107+
}
108+
109+
private static String convertMillisToTimeStr(int millis) {
110+
if (millis < 0) {
111+
return "00:00:00";
112+
}
113+
int hours = millis / 3600000;
114+
int minutes = (millis - hours * 3600000) / 60000;
115+
int seconds = (millis - hours * 3600000 - minutes * 60000) / 1000;
116+
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
117+
}
118+
119+
private static void createAndShowTestDialog() {
120+
String description =
121+
" To run this test it is required to delete any installed printers.\r\n" +
122+
"\r\n" +
123+
" 1. Verify that saving file via \"Save as PDF\" results in saving file\r\n" +
124+
" even if there is no installed printer.\r\n" +
125+
" 2. Click on \"Start Test\" button.\r\n" +
126+
" 3. In the shown print dialog select \"Save as PDF\" in PDF drop-down list\r\n" +
127+
" 4. Another dialog opens prompting for filename, enter any filename and press \"Save\".\r\n" +
128+
"\r\n" +
129+
" If the file is saved without any PrinterException, click on \"PASS\"\r\n" +
130+
" button, otherwise click on \"FAIL\" button.";
131+
132+
final JDialog dialog = new JDialog();
133+
dialog.setTitle("SaveFileWithoutPrinter");
134+
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
135+
dialog.addWindowListener(new WindowAdapter() {
136+
@Override
137+
public void windowClosing(WindowEvent e) {
138+
dialog.dispose();
139+
fail("Main dialog was closed.");
140+
}
141+
});
142+
143+
final JLabel testTimeoutLabel = new JLabel(String.format(
144+
"Test timeout: %s", convertMillisToTimeStr(testTimeout)));
145+
final long startTime = System.currentTimeMillis();
146+
final Timer timer = new Timer(0, null);
147+
timer.setDelay(1000);
148+
timer.addActionListener((e) -> {
149+
int leftTime = testTimeout - (int) (System.currentTimeMillis() - startTime);
150+
if ((leftTime < 0) || testFinished) {
151+
timer.stop();
152+
dialog.dispose();
153+
}
154+
testTimeoutLabel.setText(String.format(
155+
"Test timeout: %s", convertMillisToTimeStr(leftTime)));
156+
});
157+
timer.start();
158+
159+
JTextArea textArea = new JTextArea(description);
160+
textArea.setEditable(false);
161+
162+
final JButton testButton = new JButton("Start Test");
163+
final JButton passButton = new JButton("PASS");
164+
final JButton failButton = new JButton("FAIL");
165+
testButton.addActionListener((e) -> {
166+
testButton.setEnabled(false);
167+
new Thread(() -> {
168+
try {
169+
doTest();
170+
171+
SwingUtilities.invokeLater(() -> {
172+
passButton.setEnabled(true);
173+
failButton.setEnabled(true);
174+
});
175+
} catch (Throwable t) {
176+
t.printStackTrace();
177+
dialog.dispose();
178+
fail("Exception occurred in a thread executing the test.");
179+
}
180+
}).start();
181+
});
182+
passButton.setEnabled(false);
183+
passButton.addActionListener((e) -> {
184+
dialog.dispose();
185+
pass();
186+
});
187+
failButton.setEnabled(false);
188+
failButton.addActionListener((e) -> {
189+
dialog.dispose();
190+
fail("PrinterException thrown.");
191+
});
192+
193+
JPanel mainPanel = new JPanel(new BorderLayout());
194+
JPanel labelPanel = new JPanel(new FlowLayout());
195+
labelPanel.add(testTimeoutLabel);
196+
mainPanel.add(labelPanel, BorderLayout.NORTH);
197+
mainPanel.add(textArea, BorderLayout.CENTER);
198+
JPanel buttonPanel = new JPanel(new FlowLayout());
199+
buttonPanel.add(testButton);
200+
buttonPanel.add(passButton);
201+
buttonPanel.add(failButton);
202+
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
203+
dialog.add(mainPanel);
204+
205+
dialog.pack();
206+
dialog.setVisible(true);
207+
}
208+
209+
@Override
210+
public int print(Graphics g, PageFormat pf, int pageIndex)
211+
throws PrinterException {
212+
if (pageIndex == 0) {
213+
return Printable.PAGE_EXISTS;
214+
} else {
215+
return Printable.NO_SUCH_PAGE;
216+
}
217+
}
218+
}

0 commit comments

Comments
 (0)