1 /*
2  * Copyright (c) 2000, 2019, 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.URI;
29 import java.io.BufferedInputStream;
30 import java.io.BufferedOutputStream;
31 import java.io.File;
32 import java.io.FileOutputStream;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.InputStream;
36 import java.io.IOException;
37 import java.io.FileNotFoundException;
38 import java.io.Reader;
39 import java.net.URL;
40 import java.util.Vector;
41 
42 import javax.print.CancelablePrintJob;
43 import javax.print.Doc;
44 import javax.print.DocFlavor;
45 import javax.print.DocPrintJob;
46 import javax.print.PrintService;
47 import javax.print.PrintException;
48 import javax.print.event.PrintJobEvent;
49 import javax.print.event.PrintJobListener;
50 import javax.print.event.PrintJobAttributeListener;
51 
52 import javax.print.attribute.Attribute;
53 import javax.print.attribute.AttributeSet;
54 import javax.print.attribute.AttributeSetUtilities;
55 import javax.print.attribute.DocAttributeSet;
56 import javax.print.attribute.HashPrintJobAttributeSet;
57 import javax.print.attribute.HashPrintRequestAttributeSet;
58 import javax.print.attribute.PrintJobAttribute;
59 import javax.print.attribute.PrintJobAttributeSet;
60 import javax.print.attribute.PrintRequestAttribute;
61 import javax.print.attribute.PrintRequestAttributeSet;
62 import javax.print.attribute.standard.Copies;
63 import javax.print.attribute.standard.DocumentName;
64 import javax.print.attribute.standard.Fidelity;
65 import javax.print.attribute.standard.JobName;
66 import javax.print.attribute.standard.JobOriginatingUserName;
67 import javax.print.attribute.standard.Media;
68 import javax.print.attribute.standard.MediaSize;
69 import javax.print.attribute.standard.MediaSizeName;
70 import javax.print.attribute.standard.OrientationRequested;
71 import javax.print.attribute.standard.RequestingUserName;
72 import javax.print.attribute.standard.Destination;
73 import javax.print.attribute.standard.PrinterIsAcceptingJobs;
74 import javax.print.attribute.standard.PrinterState;
75 import javax.print.attribute.standard.PrinterStateReason;
76 import javax.print.attribute.standard.PrinterStateReasons;
77 
78 import java.awt.print.*;
79 
80 public class Win32PrintJob implements CancelablePrintJob {
81 
82     private transient Vector<PrintJobListener> jobListeners;
83     private transient Vector<PrintJobAttributeListener> attrListeners;
84     private transient Vector<PrintJobAttributeSet> listenedAttributeSets;
85 
86     private Win32PrintService service;
87     private boolean fidelity;
88     private boolean printing = false;
89     private boolean printReturned = false;
90     private PrintRequestAttributeSet reqAttrSet = null;
91     private PrintJobAttributeSet jobAttrSet = null;
92     private PrinterJob job;
93     private Doc doc;
94     private String mDestination = null;
95 
96     /* these variables used globally to store reference to the print
97      * data retrieved as a stream. On completion these are always closed
98      * if non-null.
99      */
100     private InputStream instream = null;
101     private Reader reader = null;
102 
103     /* default values overridden by those extracted from the attributes */
104     private String jobName = "Java Printing";
105     private int copies = 0;
106     private MediaSizeName mediaName = null;
107     private MediaSize     mediaSize = null;
108     private OrientationRequested orient = null;
109 
110     /* print job handle used by native code */
111     private long hPrintJob;
112 
113     /* buffer length for printing raw data */
114     private static final int PRINTBUFFERLEN = 8192;
115 
Win32PrintJob(Win32PrintService service)116     Win32PrintJob(Win32PrintService service) {
117         this.service = service;
118     }
119 
getPrintService()120     public PrintService getPrintService() {
121         return service;
122     }
123 
getAttributes()124     public PrintJobAttributeSet getAttributes() {
125         synchronized (this) {
126             if (jobAttrSet == null) {
127                 /* just return an empty set until the job is submitted */
128                 PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet();
129                 return AttributeSetUtilities.unmodifiableView(jobSet);
130             } else {
131               return jobAttrSet;
132             }
133         }
134     }
135 
addPrintJobListener(PrintJobListener listener)136     public void addPrintJobListener(PrintJobListener listener) {
137         synchronized (this) {
138             if (listener == null) {
139                 return;
140             }
141             if (jobListeners == null) {
142                 jobListeners = new Vector<>();
143             }
144             jobListeners.add(listener);
145         }
146     }
147 
removePrintJobListener(PrintJobListener listener)148     public void removePrintJobListener(PrintJobListener listener) {
149         synchronized (this) {
150             if (listener == null || jobListeners == null ) {
151                 return;
152             }
153             jobListeners.remove(listener);
154             if (jobListeners.isEmpty()) {
155                 jobListeners = null;
156             }
157         }
158     }
159 
160 
161     /* Closes any stream already retrieved for the data.
162      * We want to avoid unnecessarily asking the Doc to create a stream only
163      * to get a reference in order to close it because the job failed.
164      * If the representation class is itself a "stream", this
165      * closes that stream too.
166      */
closeDataStreams()167     private void closeDataStreams() {
168 
169         if (doc == null) {
170             return;
171         }
172 
173         Object data = null;
174 
175         try {
176             data = doc.getPrintData();
177         } catch (IOException e) {
178             return;
179         }
180 
181         if (instream != null) {
182             try {
183                 instream.close();
184             } catch (IOException e) {
185             } finally {
186                 instream = null;
187             }
188         }
189         else if (reader != null) {
190             try {
191                 reader.close();
192             } catch (IOException e) {
193             } finally {
194                 reader = null;
195             }
196         }
197         else if (data instanceof InputStream) {
198             try {
199                 ((InputStream)data).close();
200             } catch (IOException e) {
201             }
202         }
203         else if (data instanceof Reader) {
204             try {
205                 ((Reader)data).close();
206             } catch (IOException e) {
207             }
208         }
209     }
210 
notifyEvent(int reason)211     private void notifyEvent(int reason) {
212 
213         /* since this method should always get called, here's where
214          * we will perform the clean up of any data stream supplied.
215          */
216         switch (reason) {
217             case PrintJobEvent.DATA_TRANSFER_COMPLETE:
218             case PrintJobEvent.JOB_CANCELED :
219             case PrintJobEvent.JOB_FAILED :
220             case PrintJobEvent.NO_MORE_EVENTS :
221             case PrintJobEvent.JOB_COMPLETE :
222                 closeDataStreams();
223         }
224 
225         synchronized (this) {
226             if (jobListeners != null) {
227                 PrintJobListener listener;
228                 PrintJobEvent event = new PrintJobEvent(this, reason);
229                 for (int i = 0; i < jobListeners.size(); i++) {
230                     listener = jobListeners.elementAt(i);
231                     switch (reason) {
232 
233                         case PrintJobEvent.JOB_COMPLETE :
234                             listener.printJobCompleted(event);
235                             break;
236 
237                         case PrintJobEvent.JOB_CANCELED :
238                             listener.printJobCanceled(event);
239                             break;
240 
241                         case PrintJobEvent.JOB_FAILED :
242                             listener.printJobFailed(event);
243                             break;
244 
245                         case PrintJobEvent.DATA_TRANSFER_COMPLETE :
246                             listener.printDataTransferCompleted(event);
247                             break;
248 
249                         case PrintJobEvent.NO_MORE_EVENTS :
250                             listener.printJobNoMoreEvents(event);
251                             break;
252 
253                         default:
254                             break;
255                     }
256                 }
257             }
258        }
259     }
260 
addPrintJobAttributeListener( PrintJobAttributeListener listener, PrintJobAttributeSet attributes)261     public void addPrintJobAttributeListener(
262                                   PrintJobAttributeListener listener,
263                                   PrintJobAttributeSet attributes) {
264         synchronized (this) {
265             if (listener == null) {
266                 return;
267             }
268             if (attrListeners == null) {
269                 attrListeners = new Vector<>();
270                 listenedAttributeSets = new Vector<>();
271             }
272             attrListeners.add(listener);
273             if (attributes == null) {
274                 attributes = new HashPrintJobAttributeSet();
275             }
276             listenedAttributeSets.add(attributes);
277         }
278     }
279 
removePrintJobAttributeListener( PrintJobAttributeListener listener)280     public void removePrintJobAttributeListener(
281                                         PrintJobAttributeListener listener) {
282         synchronized (this) {
283             if (listener == null || attrListeners == null ) {
284                 return;
285             }
286             int index = attrListeners.indexOf(listener);
287             if (index == -1) {
288                 return;
289             } else {
290                 attrListeners.remove(index);
291                 listenedAttributeSets.remove(index);
292                 if (attrListeners.isEmpty()) {
293                     attrListeners = null;
294                     listenedAttributeSets = null;
295                 }
296             }
297         }
298     }
299 
print(Doc doc, PrintRequestAttributeSet attributes)300     public void print(Doc doc, PrintRequestAttributeSet attributes)
301         throws PrintException {
302 
303         synchronized (this) {
304             if (printing) {
305                 throw new PrintException("already printing");
306             } else {
307                 printing = true;
308             }
309         }
310 
311         PrinterState prnState = service.getAttribute(PrinterState.class);
312         if (prnState == PrinterState.STOPPED) {
313             PrinterStateReasons prnStateReasons =
314                 service.getAttribute(PrinterStateReasons.class);
315                 if ((prnStateReasons != null) &&
316                     (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))
317                 {
318                     throw new PrintException("PrintService is no longer available.");
319                 }
320         }
321 
322         if (service.getAttribute(PrinterIsAcceptingJobs.class) ==
323             PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {
324             throw new PrintException("Printer is not accepting job.");
325         }
326 
327 
328         this.doc = doc;
329         /* check if the parameters are valid before doing much processing */
330         DocFlavor flavor = doc.getDocFlavor();
331         Object data;
332 
333         try {
334             data = doc.getPrintData();
335         } catch (IOException e) {
336             notifyEvent(PrintJobEvent.JOB_FAILED);
337             throw new PrintException("can't get print data: " + e.toString());
338         }
339 
340         if (data == null) {
341             throw new PrintException("Null print data.");
342         }
343 
344         if (flavor == null || (!service.isDocFlavorSupported(flavor))) {
345             notifyEvent(PrintJobEvent.JOB_FAILED);
346             throw new PrintJobFlavorException("invalid flavor", flavor);
347         }
348 
349         initializeAttributeSets(doc, attributes);
350 
351         getAttributeValues(flavor);
352 
353         String repClassName = flavor.getRepresentationClassName();
354 
355         if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
356             flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
357             flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
358             flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
359             flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
360             flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) {
361             try {
362                 instream = doc.getStreamForBytes();
363                 if (instream == null) {
364                     notifyEvent(PrintJobEvent.JOB_FAILED);
365                     throw new PrintException("No stream for data");
366                 }
367                 printableJob(new ImagePrinter(instream));
368                 service.wakeNotifier();
369                 return;
370             } catch (ClassCastException cce) {
371                 notifyEvent(PrintJobEvent.JOB_FAILED);
372                 throw new PrintException(cce);
373             } catch (IOException ioe) {
374                 notifyEvent(PrintJobEvent.JOB_FAILED);
375                 throw new PrintException(ioe);
376             }
377         } else if (flavor.equals(DocFlavor.URL.GIF) ||
378                    flavor.equals(DocFlavor.URL.JPEG) ||
379                    flavor.equals(DocFlavor.URL.PNG)) {
380             try {
381                 printableJob(new ImagePrinter((URL)data));
382                 service.wakeNotifier();
383                 return;
384             } catch (ClassCastException cce) {
385                 notifyEvent(PrintJobEvent.JOB_FAILED);
386                 throw new PrintException(cce);
387             }
388         } else if (repClassName.equals("java.awt.print.Pageable")) {
389             try {
390                 pageableJob((Pageable)doc.getPrintData());
391                 service.wakeNotifier();
392                 return;
393             } catch (ClassCastException cce) {
394                 notifyEvent(PrintJobEvent.JOB_FAILED);
395                 throw new PrintException(cce);
396             } catch (IOException ioe) {
397                 notifyEvent(PrintJobEvent.JOB_FAILED);
398                 throw new PrintException(ioe);
399             }
400         } else if (repClassName.equals("java.awt.print.Printable")) {
401             try {
402                 printableJob((Printable)doc.getPrintData());
403                 service.wakeNotifier();
404                 return;
405             } catch (ClassCastException cce) {
406                 notifyEvent(PrintJobEvent.JOB_FAILED);
407                 throw new PrintException(cce);
408             } catch (IOException ioe) {
409                 notifyEvent(PrintJobEvent.JOB_FAILED);
410                 throw new PrintException(ioe);
411             }
412         } else if (repClassName.equals("[B") ||
413                    repClassName.equals("java.io.InputStream") ||
414                    repClassName.equals("java.net.URL")) {
415 
416             if (repClassName.equals("java.net.URL")) {
417                 URL url = (URL)data;
418                 try {
419                     instream = url.openStream();
420                 } catch (IOException e) {
421                     notifyEvent(PrintJobEvent.JOB_FAILED);
422                     throw new PrintException(e.toString());
423                 }
424             } else {
425                 try {
426                     instream = doc.getStreamForBytes();
427                 } catch (IOException ioe) {
428                     notifyEvent(PrintJobEvent.JOB_FAILED);
429                     throw new PrintException(ioe.toString());
430                 }
431             }
432 
433             if (instream == null) {
434                 notifyEvent(PrintJobEvent.JOB_FAILED);
435                 throw new PrintException("No stream for data");
436             }
437 
438             if (mDestination != null) { // if destination attribute is set
439                 try {
440                     FileOutputStream fos = new FileOutputStream(mDestination);
441                     byte []buffer = new byte[1024];
442                     int cread;
443 
444                     while ((cread = instream.read(buffer, 0, buffer.length)) >=0) {
445                         fos.write(buffer, 0, cread);
446                     }
447                     fos.flush();
448                     fos.close();
449                 } catch (FileNotFoundException fnfe) {
450                     notifyEvent(PrintJobEvent.JOB_FAILED);
451                     throw new PrintException(fnfe.toString());
452                 } catch (IOException ioe) {
453                     notifyEvent(PrintJobEvent.JOB_FAILED);
454                     throw new PrintException(ioe.toString());
455                 }
456                 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
457                 notifyEvent(PrintJobEvent.JOB_COMPLETE);
458                 service.wakeNotifier();
459                 return;
460             }
461 
462             if (!startPrintRawData(service.getName(), jobName)) {
463                 notifyEvent(PrintJobEvent.JOB_FAILED);
464                 throw new PrintException("Print job failed to start.");
465             }
466             BufferedInputStream  bin = new BufferedInputStream(instream);
467             int bread = 0;
468             try {
469                 byte[] buffer = new byte[PRINTBUFFERLEN];
470 
471                 while ((bread = bin.read(buffer, 0, PRINTBUFFERLEN)) >=0) {
472                     if (!printRawData(buffer, bread)) {
473                         bin.close();
474                         notifyEvent(PrintJobEvent.JOB_FAILED);
475                         throw new PrintException ("Problem while spooling data");
476                     }
477                 }
478                 bin.close();
479                 if (!endPrintRawData()) {
480                     notifyEvent(PrintJobEvent.JOB_FAILED);
481                     throw new PrintException("Print job failed to close properly.");
482                 }
483                 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
484             } catch (IOException e) {
485                 notifyEvent(PrintJobEvent.JOB_FAILED);
486                 throw new PrintException (e.toString());
487             } finally {
488                 notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
489             }
490         } else {
491             notifyEvent(PrintJobEvent.JOB_FAILED);
492             throw new PrintException("unrecognized class: "+repClassName);
493         }
494         service.wakeNotifier();
495     }
496 
printableJob(Printable printable)497     public void printableJob(Printable printable) throws PrintException {
498         try {
499             synchronized(this) {
500                 if (job != null) { // shouldn't happen
501                     throw new PrintException("already printing");
502                 } else {
503                     job = new sun.awt.windows.WPrinterJob();
504                 }
505             }
506             PrintService svc = getPrintService();
507             job.setPrintService(svc);
508             if (copies == 0) {
509                 Copies c = (Copies)svc.getDefaultAttributeValue(Copies.class);
510                 copies = c.getValue();
511             }
512 
513             if (mediaName == null) {
514                 Object media = svc.getDefaultAttributeValue(Media.class);
515                 if (media instanceof MediaSizeName) {
516                     mediaName = (MediaSizeName) media;
517                     mediaSize = MediaSize.getMediaSizeForName(mediaName);
518                 }
519             }
520 
521             if (orient == null) {
522                 orient =
523                     (OrientationRequested)svc.getDefaultAttributeValue(OrientationRequested.class);
524             }
525 
526             job.setCopies(copies);
527             job.setJobName(jobName);
528             PageFormat pf = new PageFormat();
529             if (mediaSize != null) {
530                 Paper p = new Paper();
531                 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,
532                           mediaSize.getY(MediaSize.INCH)*72.0);
533                 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0,
534                                    p.getHeight()-144.0);
535                 pf.setPaper(p);
536             }
537             if (orient == OrientationRequested.REVERSE_LANDSCAPE) {
538                 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE);
539             } else if (orient == OrientationRequested.LANDSCAPE) {
540                 pf.setOrientation(PageFormat.LANDSCAPE);
541             }
542             job.setPrintable(printable, pf);
543             job.print(reqAttrSet);
544             notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
545             return;
546         } catch (PrinterException pe) {
547             notifyEvent(PrintJobEvent.JOB_FAILED);
548             throw new PrintException(pe);
549         } finally {
550             printReturned = true;
551             notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
552         }
553     }
554 
pageableJob(Pageable pageable)555     public void pageableJob(Pageable pageable) throws PrintException {
556         try {
557             synchronized(this) {
558                 if (job != null) { // shouldn't happen
559                     throw new PrintException("already printing");
560                 } else {
561                     job = new sun.awt.windows.WPrinterJob();
562                 }
563             }
564             PrintService svc = getPrintService();
565             job.setPrintService(svc);
566             if (copies == 0) {
567                 Copies c = (Copies)svc.getDefaultAttributeValue(Copies.class);
568                 copies = c.getValue();
569             }
570             job.setCopies(copies);
571             job.setJobName(jobName);
572             job.setPageable(pageable);
573             job.print(reqAttrSet);
574             notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
575             return;
576         } catch (PrinterException pe) {
577             notifyEvent(PrintJobEvent.JOB_FAILED);
578             throw new PrintException(pe);
579         } finally {
580             printReturned = true;
581             notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
582         }
583     }
584 
585     /* There's some inefficiency here as the job set is created even though
586      * it may never be requested.
587      */
588     private synchronized void
initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet)589         initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) {
590 
591         reqAttrSet = new HashPrintRequestAttributeSet();
592         jobAttrSet = new HashPrintJobAttributeSet();
593 
594         Attribute[] attrs;
595         if (reqSet != null) {
596             reqAttrSet.addAll(reqSet);
597             attrs = reqSet.toArray();
598             for (int i=0; i<attrs.length; i++) {
599                 if (attrs[i] instanceof PrintJobAttribute) {
600                     jobAttrSet.add(attrs[i]);
601                 }
602             }
603         }
604 
605         DocAttributeSet docSet = doc.getAttributes();
606         if (docSet != null) {
607             attrs = docSet.toArray();
608             for (int i=0; i<attrs.length; i++) {
609                 if (attrs[i] instanceof PrintRequestAttribute) {
610                     reqAttrSet.add(attrs[i]);
611                 }
612                 if (attrs[i] instanceof PrintJobAttribute) {
613                     jobAttrSet.add(attrs[i]);
614                 }
615             }
616         }
617 
618         /* add the user name to the job */
619         String userName = "";
620         try {
621           userName = System.getProperty("user.name");
622         } catch (SecurityException se) {
623         }
624 
625         if (userName == null || userName.isEmpty()) {
626             RequestingUserName ruName =
627                 (RequestingUserName)reqSet.get(RequestingUserName.class);
628             if (ruName != null) {
629                 jobAttrSet.add(
630                     new JobOriginatingUserName(ruName.getValue(),
631                                                ruName.getLocale()));
632             } else {
633                 jobAttrSet.add(new JobOriginatingUserName("", null));
634             }
635         } else {
636             jobAttrSet.add(new JobOriginatingUserName(userName, null));
637         }
638 
639         /* if no job name supplied use doc name (if supplied), if none and
640          * its a URL use that, else finally anything .. */
641         if (jobAttrSet.get(JobName.class) == null) {
642             JobName jobName;
643             if (docSet != null && docSet.get(DocumentName.class) != null) {
644                 DocumentName docName =
645                     (DocumentName)docSet.get(DocumentName.class);
646                 jobName = new JobName(docName.getValue(), docName.getLocale());
647                 jobAttrSet.add(jobName);
648             } else {
649                 String str = "JPS Job:" + doc;
650                 try {
651                     Object printData = doc.getPrintData();
652                     if (printData instanceof URL) {
653                         str = ((URL)(doc.getPrintData())).toString();
654                     }
655                 } catch (IOException e) {
656                 }
657                 jobName = new JobName(str, null);
658                 jobAttrSet.add(jobName);
659             }
660         }
661 
662         jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet);
663     }
664 
getAttributeValues(DocFlavor flavor)665     private void getAttributeValues(DocFlavor flavor) throws PrintException {
666 
667         if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) {
668             fidelity = true;
669         } else {
670             fidelity = false;
671         }
672 
673         Class<? extends Attribute> category;
674         Attribute [] attrs = reqAttrSet.toArray();
675         for (int i=0; i<attrs.length; i++) {
676             Attribute attr = attrs[i];
677             category = attr.getCategory();
678             if (fidelity == true) {
679                 if (!service.isAttributeCategorySupported(category)) {
680                     notifyEvent(PrintJobEvent.JOB_FAILED);
681                     throw new PrintJobAttributeException(
682                         "unsupported category: " + category, category, null);
683                 } else if
684                     (!service.isAttributeValueSupported(attr, flavor, null)) {
685                     notifyEvent(PrintJobEvent.JOB_FAILED);
686                     throw new PrintJobAttributeException(
687                         "unsupported attribute: " + attr, null, attr);
688                 }
689             }
690             if (category == Destination.class) {
691               URI uri = ((Destination)attr).getURI();
692               if (!"file".equals(uri.getScheme())) {
693                 notifyEvent(PrintJobEvent.JOB_FAILED);
694                 throw new PrintException("Not a file: URI");
695               } else {
696                 try {
697                   mDestination = (new File(uri)).getPath();
698                 } catch (Exception e) {
699                   throw new PrintException(e);
700                 }
701                 // check write access
702                 SecurityManager security = System.getSecurityManager();
703                 if (security != null) {
704                   try {
705                     security.checkWrite(mDestination);
706                   } catch (SecurityException se) {
707                     notifyEvent(PrintJobEvent.JOB_FAILED);
708                     throw new PrintException(se);
709                   }
710                 }
711               }
712             } else if (category == JobName.class) {
713                 jobName = ((JobName)attr).getValue();
714             } else if (category == Copies.class) {
715                 copies = ((Copies)attr).getValue();
716             } else if (category == Media.class) {
717               if (attr instanceof MediaSizeName) {
718                     mediaName = (MediaSizeName)attr;
719                     // If requested MediaSizeName is not supported,
720                     // get the corresponding media size - this will
721                     // be used to create a new PageFormat.
722                     if (!service.isAttributeValueSupported(attr, null, null)) {
723                         mediaSize = MediaSize.getMediaSizeForName(mediaName);
724                     }
725                 }
726             } else if (category == OrientationRequested.class) {
727                 orient = (OrientationRequested)attr;
728             }
729         }
730     }
731 
startPrintRawData(String printerName, String jobName)732     private native boolean startPrintRawData(String printerName,
733                                              String jobName);
printRawData(byte[] data, int count)734     private native boolean printRawData(byte[] data, int count);
endPrintRawData()735     private native boolean endPrintRawData();
736 
737     /* Cancel PrinterJob jobs that haven't yet completed. */
cancel()738    public void cancel() throws PrintException {
739         synchronized (this) {
740             if (!printing) {
741                 throw new PrintException("Job is not yet submitted.");
742             } else if (job != null && !printReturned) {
743                 job.cancel();
744                 notifyEvent(PrintJobEvent.JOB_CANCELED);
745                 return;
746             } else {
747                 throw new PrintException("Job could not be cancelled.");
748             }
749         }
750     }
751 }
752