1 /*
2  * Copyright (c) 2000, 2005, 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 package sun.print;
27 
28 import java.net.URL;
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.Reader;
32 import java.util.Vector;
33 
34 import javax.print.CancelablePrintJob;
35 import javax.print.Doc;
36 import javax.print.DocFlavor;
37 import javax.print.DocPrintJob;
38 import javax.print.PrintService;
39 import javax.print.PrintException;
40 import javax.print.event.PrintJobEvent;
41 import javax.print.event.PrintJobListener;
42 import javax.print.event.PrintJobAttributeListener;
43 
44 import javax.print.attribute.Attribute;
45 import javax.print.attribute.AttributeSet;
46 import javax.print.attribute.AttributeSetUtilities;
47 import javax.print.attribute.DocAttributeSet;
48 import javax.print.attribute.HashPrintJobAttributeSet;
49 import javax.print.attribute.HashPrintRequestAttributeSet;
50 import javax.print.attribute.PrintJobAttribute;
51 import javax.print.attribute.PrintJobAttributeSet;
52 import javax.print.attribute.PrintRequestAttribute;
53 import javax.print.attribute.PrintRequestAttributeSet;
54 import javax.print.attribute.standard.Copies;
55 import javax.print.attribute.standard.DocumentName;
56 import javax.print.attribute.standard.Fidelity;
57 import javax.print.attribute.standard.JobName;
58 import javax.print.attribute.standard.JobOriginatingUserName;
59 import javax.print.attribute.standard.Media;
60 import javax.print.attribute.standard.MediaSize;
61 import javax.print.attribute.standard.MediaSizeName;
62 import javax.print.attribute.standard.OrientationRequested;
63 import javax.print.attribute.standard.RequestingUserName;
64 
65 import java.awt.print.*;
66 
67 public class PSStreamPrintJob implements CancelablePrintJob {
68 
69     private transient Vector<PrintJobListener> jobListeners;
70     private transient Vector<PrintJobAttributeListener> attrListeners;
71     private transient Vector<PrintJobAttributeSet> listenedAttributeSets;
72 
73     private PSStreamPrintService service;
74     private boolean fidelity;
75     private boolean printing = false;
76     private boolean printReturned = false;
77     private PrintRequestAttributeSet reqAttrSet = null;
78     private PrintJobAttributeSet jobAttrSet = null;
79     private PrinterJob job;
80     private Doc doc;
81     /* these variables used globally to store reference to the print
82      * data retrieved as a stream. On completion these are always closed
83      * if non-null.
84      */
85     private InputStream instream = null;
86     private Reader reader = null;
87 
88     /* default values overridden by those extracted from the attributes */
89     private String jobName = "Java Printing";
90     private int copies = 1;
91     private MediaSize     mediaSize = MediaSize.NA.LETTER;
92     private OrientationRequested orient = OrientationRequested.PORTRAIT;
93 
PSStreamPrintJob(PSStreamPrintService service)94     PSStreamPrintJob(PSStreamPrintService service) {
95         this.service = service;
96     }
97 
getPrintService()98     public PrintService getPrintService() {
99         return service;
100     }
101 
getAttributes()102     public PrintJobAttributeSet getAttributes() {
103         synchronized (this) {
104             if (jobAttrSet == null) {
105                 /* just return an empty set until the job is submitted */
106                 PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet();
107                 return AttributeSetUtilities.unmodifiableView(jobSet);
108             } else {
109                 return jobAttrSet;
110             }
111         }
112     }
113 
addPrintJobListener(PrintJobListener listener)114     public void addPrintJobListener(PrintJobListener listener) {
115         synchronized (this) {
116             if (listener == null) {
117                 return;
118             }
119             if (jobListeners == null) {
120                 jobListeners = new Vector<>();
121             }
122             jobListeners.add(listener);
123         }
124     }
125 
removePrintJobListener(PrintJobListener listener)126     public void removePrintJobListener(PrintJobListener listener) {
127         synchronized (this) {
128             if (listener == null || jobListeners == null ) {
129                 return;
130             }
131             jobListeners.remove(listener);
132             if (jobListeners.isEmpty()) {
133                 jobListeners = null;
134             }
135         }
136     }
137 
138     /* Closes any stream already retrieved for the data.
139      * We want to avoid unnecessarily asking the Doc to create a stream only
140      * to get a reference in order to close it because the job failed.
141      * If the representation class is itself a "stream", this
142      * closes that stream too.
143      */
closeDataStreams()144     private void closeDataStreams() {
145 
146         if (doc == null) {
147             return;
148         }
149 
150         Object data = null;
151 
152         try {
153             data = doc.getPrintData();
154         } catch (IOException e) {
155             return;
156         }
157 
158         if (instream != null) {
159             try {
160                 instream.close();
161             } catch (IOException e) {
162             } finally {
163                 instream = null;
164             }
165         }
166         else if (reader != null) {
167             try {
168                 reader.close();
169             } catch (IOException e) {
170             } finally {
171                 reader = null;
172             }
173         }
174         else if (data instanceof InputStream) {
175             try {
176                 ((InputStream)data).close();
177             } catch (IOException e) {
178             }
179         }
180         else if (data instanceof Reader) {
181             try {
182                 ((Reader)data).close();
183             } catch (IOException e) {
184             }
185         }
186     }
187 
notifyEvent(int reason)188     private void notifyEvent(int reason) {
189         synchronized (this) {
190             if (jobListeners != null) {
191                 PrintJobListener listener;
192                 PrintJobEvent event = new PrintJobEvent(this, reason);
193                 for (int i = 0; i < jobListeners.size(); i++) {
194                     listener = jobListeners.elementAt(i);
195                     switch (reason) {
196 
197                         case PrintJobEvent.JOB_CANCELED :
198                             listener.printJobCanceled(event);
199                             break;
200 
201                         case PrintJobEvent.JOB_FAILED :
202                             listener.printJobFailed(event);
203                             break;
204 
205                         case PrintJobEvent.DATA_TRANSFER_COMPLETE :
206                             listener.printDataTransferCompleted(event);
207                             break;
208 
209                         case PrintJobEvent.NO_MORE_EVENTS :
210                             listener.printJobNoMoreEvents(event);
211                             break;
212 
213                         case PrintJobEvent.JOB_COMPLETE :
214                             listener.printJobCompleted(event);
215                             break;
216 
217                         default:
218                             break;
219                     }
220                 }
221             }
222        }
223     }
224 
addPrintJobAttributeListener( PrintJobAttributeListener listener, PrintJobAttributeSet attributes)225     public void addPrintJobAttributeListener(
226                                   PrintJobAttributeListener listener,
227                                   PrintJobAttributeSet attributes) {
228         synchronized (this) {
229             if (listener == null) {
230                 return;
231             }
232             if (attrListeners == null) {
233                 attrListeners = new Vector<>();
234                 listenedAttributeSets = new Vector<>();
235             }
236             attrListeners.add(listener);
237             if (attributes == null) {
238                 attributes = new HashPrintJobAttributeSet();
239             }
240             listenedAttributeSets.add(attributes);
241         }
242     }
243 
removePrintJobAttributeListener( PrintJobAttributeListener listener)244     public void removePrintJobAttributeListener(
245                                         PrintJobAttributeListener listener) {
246         synchronized (this) {
247             if (listener == null || attrListeners == null ) {
248                 return;
249             }
250             int index = attrListeners.indexOf(listener);
251             if (index == -1) {
252                 return;
253             } else {
254                 attrListeners.remove(index);
255                 listenedAttributeSets.remove(index);
256                 if (attrListeners.isEmpty()) {
257                     attrListeners = null;
258                     listenedAttributeSets = null;
259                 }
260             }
261         }
262     }
263 
print(Doc doc, PrintRequestAttributeSet attributes)264     public void print(Doc doc, PrintRequestAttributeSet attributes)
265         throws PrintException {
266 
267         synchronized (this) {
268             if (printing) {
269                 throw new PrintException("already printing");
270             } else {
271                 printing = true;
272             }
273         }
274 
275         this.doc = doc;
276         /* check if the parameters are valid before doing much processing */
277         DocFlavor flavor = doc.getDocFlavor();
278         Object data;
279 
280         try {
281             data = doc.getPrintData();
282         } catch (IOException e) {
283             notifyEvent(PrintJobEvent.JOB_FAILED);
284             throw new PrintException("can't get print data: " + e.toString());
285         }
286 
287         if (flavor == null || (!service.isDocFlavorSupported(flavor))) {
288             notifyEvent(PrintJobEvent.JOB_FAILED);
289             throw new PrintJobFlavorException("invalid flavor", flavor);
290         }
291 
292         initializeAttributeSets(doc, attributes);
293 
294         getAttributeValues(flavor);
295 
296         String repClassName = flavor.getRepresentationClassName();
297         if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
298             flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
299             flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
300             flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
301             flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
302             flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) {
303             try {
304                 instream = doc.getStreamForBytes();
305                 printableJob(new ImagePrinter(instream), reqAttrSet);
306                 return;
307             } catch (ClassCastException cce) {
308                 notifyEvent(PrintJobEvent.JOB_FAILED);
309                 throw new PrintException(cce);
310             } catch (IOException ioe) {
311                 notifyEvent(PrintJobEvent.JOB_FAILED);
312                 throw new PrintException(ioe);
313             }
314         } else if (flavor.equals(DocFlavor.URL.GIF) ||
315                    flavor.equals(DocFlavor.URL.JPEG) ||
316                    flavor.equals(DocFlavor.URL.PNG)) {
317             try {
318                 printableJob(new ImagePrinter((URL)data), reqAttrSet);
319                 return;
320             } catch (ClassCastException cce) {
321                 notifyEvent(PrintJobEvent.JOB_FAILED);
322                 throw new PrintException(cce);
323             }
324         } else if (repClassName.equals("java.awt.print.Pageable")) {
325             try {
326                 pageableJob((Pageable)doc.getPrintData(), reqAttrSet);
327                 return;
328             } catch (ClassCastException cce) {
329                 notifyEvent(PrintJobEvent.JOB_FAILED);
330                 throw new PrintException(cce);
331             } catch (IOException ioe) {
332                 notifyEvent(PrintJobEvent.JOB_FAILED);
333                 throw new PrintException(ioe);
334             }
335         } else if (repClassName.equals("java.awt.print.Printable")) {
336             try {
337                 printableJob((Printable)doc.getPrintData(), reqAttrSet);
338                 return;
339             } catch (ClassCastException cce) {
340                 notifyEvent(PrintJobEvent.JOB_FAILED);
341                 throw new PrintException(cce);
342             } catch (IOException ioe) {
343                 notifyEvent(PrintJobEvent.JOB_FAILED);
344                 throw new PrintException(ioe);
345             }
346         } else {
347             notifyEvent(PrintJobEvent.JOB_FAILED);
348             throw new PrintException("unrecognized class: "+repClassName);
349         }
350     }
351 
printableJob(Printable printable, PrintRequestAttributeSet attributes)352     public void printableJob(Printable printable,
353                              PrintRequestAttributeSet attributes)
354         throws PrintException {
355         try {
356             synchronized(this) {
357                 if (job != null) { // shouldn't happen
358                     throw new PrintException("already printing");
359                 } else {
360                     job = new PSPrinterJob();
361                 }
362             }
363             job.setPrintService(getPrintService());
364             PageFormat pf = new PageFormat();
365             if (mediaSize != null) {
366                 Paper p = new Paper();
367                 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,
368                           mediaSize.getY(MediaSize.INCH)*72.0);
369                 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0,
370                                    p.getHeight()-144.0);
371                 pf.setPaper(p);
372             }
373             if (orient == OrientationRequested.REVERSE_LANDSCAPE) {
374                 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE);
375             } else if (orient == OrientationRequested.LANDSCAPE) {
376                 pf.setOrientation(PageFormat.LANDSCAPE);
377             }
378             job.setPrintable(printable, pf);
379             job.print(attributes);
380             notifyEvent(PrintJobEvent.JOB_COMPLETE);
381             return;
382         } catch (PrinterException pe) {
383             notifyEvent(PrintJobEvent.JOB_FAILED);
384             throw new PrintException(pe);
385         } finally {
386             printReturned = true;
387         }
388     }
389 
pageableJob(Pageable pageable, PrintRequestAttributeSet attributes)390     public void pageableJob(Pageable pageable,
391                             PrintRequestAttributeSet attributes)
392         throws PrintException {
393         try {
394             synchronized(this) {
395                 if (job != null) { // shouldn't happen
396                     throw new PrintException("already printing");
397                 } else {
398                     job = new PSPrinterJob();
399                 }
400             }
401             job.setPrintService(getPrintService());
402             job.setPageable(pageable);
403             job.print(attributes);
404             notifyEvent(PrintJobEvent.JOB_COMPLETE);
405             return;
406         } catch (PrinterException pe) {
407             notifyEvent(PrintJobEvent.JOB_FAILED);
408             throw new PrintException(pe);
409         } finally {
410             printReturned = true;
411         }
412     }
413 
414     /* There's some inefficiency here as the job set is created even though
415      * it may never be requested.
416      */
417     private synchronized void
initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet)418         initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) {
419 
420         reqAttrSet = new HashPrintRequestAttributeSet();
421         jobAttrSet = new HashPrintJobAttributeSet();
422 
423         Attribute[] attrs;
424         if (reqSet != null) {
425             reqAttrSet.addAll(reqSet);
426             attrs = reqSet.toArray();
427             for (int i=0; i<attrs.length; i++) {
428                 if (attrs[i] instanceof PrintJobAttribute) {
429                     jobAttrSet.add(attrs[i]);
430                 }
431             }
432         }
433 
434         DocAttributeSet docSet = doc.getAttributes();
435         if (docSet != null) {
436             attrs = docSet.toArray();
437             for (int i=0; i<attrs.length; i++) {
438                 if (attrs[i] instanceof PrintRequestAttribute) {
439                     reqAttrSet.add(attrs[i]);
440                 }
441                 if (attrs[i] instanceof PrintJobAttribute) {
442                     jobAttrSet.add(attrs[i]);
443                 }
444             }
445         }
446 
447         /* add the user name to the job */
448         String userName = "";
449         try {
450           userName = System.getProperty("user.name");
451         } catch (SecurityException se) {
452         }
453 
454         if (userName == null || userName.equals("")) {
455             RequestingUserName ruName =
456                 (RequestingUserName)reqSet.get(RequestingUserName.class);
457             if (ruName != null) {
458                 jobAttrSet.add(
459                     new JobOriginatingUserName(ruName.getValue(),
460                                                ruName.getLocale()));
461             } else {
462                 jobAttrSet.add(new JobOriginatingUserName("", null));
463             }
464         } else {
465             jobAttrSet.add(new JobOriginatingUserName(userName, null));
466         }
467 
468         /* if no job name supplied use doc name (if supplied), if none and
469          * its a URL use that, else finally anything .. */
470         if (jobAttrSet.get(JobName.class) == null) {
471             JobName jobName;
472             if (docSet != null && docSet.get(DocumentName.class) != null) {
473                 DocumentName docName =
474                     (DocumentName)docSet.get(DocumentName.class);
475                 jobName = new JobName(docName.getValue(), docName.getLocale());
476                 jobAttrSet.add(jobName);
477             } else {
478                 String str = "JPS Job:" + doc;
479                 try {
480                     Object printData = doc.getPrintData();
481                     if (printData instanceof URL) {
482                         str = ((URL)(doc.getPrintData())).toString();
483                     }
484                 } catch (IOException e) {
485                 }
486                 jobName = new JobName(str, null);
487                 jobAttrSet.add(jobName);
488             }
489         }
490 
491         jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet);
492     }
493 
getAttributeValues(DocFlavor flavor)494     private void getAttributeValues(DocFlavor flavor) throws PrintException {
495 
496         Attribute attr;
497         Class<? extends Attribute> category;
498 
499         if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) {
500             fidelity = true;
501         } else {
502             fidelity = false;
503         }
504 
505         Attribute []attrs = reqAttrSet.toArray();
506         for (int i=0; i<attrs.length; i++) {
507             attr = attrs[i];
508             category = attr.getCategory();
509             if (fidelity == true) {
510                 if (!service.isAttributeCategorySupported(category)) {
511                     notifyEvent(PrintJobEvent.JOB_FAILED);
512                     throw new PrintJobAttributeException(
513                         "unsupported category: " + category, category, null);
514                 } else if
515                     (!service.isAttributeValueSupported(attr, flavor, null)) {
516                     notifyEvent(PrintJobEvent.JOB_FAILED);
517                     throw new PrintJobAttributeException(
518                         "unsupported attribute: " + attr, null, attr);
519                 }
520             }
521             if (category == JobName.class) {
522                 jobName = ((JobName)attr).getValue();
523             } else if (category == Copies.class) {
524                 copies = ((Copies)attr).getValue();
525             } else if (category == Media.class) {
526                 if (attr instanceof MediaSizeName &&
527                     service.isAttributeValueSupported(attr, null, null)) {
528                     mediaSize =
529                         MediaSize.getMediaSizeForName((MediaSizeName)attr);
530                 }
531             } else if (category == OrientationRequested.class) {
532                 orient = (OrientationRequested)attr;
533             }
534         }
535     }
536 
537     /* Cancel PrinterJob jobs that haven't yet completed. */
cancel()538     public void cancel() throws PrintException {
539         synchronized (this) {
540             if (!printing) {
541                 throw new PrintException("Job is not yet submitted.");
542             } else if (job != null && !printReturned) {
543                 job.cancel();
544                 notifyEvent(PrintJobEvent.JOB_CANCELED);
545                 return;
546             } else {
547                 throw new PrintException("Job could not be cancelled.");
548             }
549         }
550     }
551 
552 }
553