1/*
2 * Copyright (c) 2000, 2017, 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
26package sun.print;
27
28import java.awt.Dimension;
29import java.awt.Frame;
30import java.awt.Graphics;
31import java.awt.Graphics2D;
32import java.awt.PrintJob;
33import java.awt.JobAttributes;
34import java.awt.JobAttributes.*;
35import java.awt.PageAttributes;
36import java.awt.PageAttributes.*;
37
38import java.awt.print.PageFormat;
39import java.awt.print.Paper;
40import java.awt.print.Printable;
41import java.awt.print.PrinterException;
42import java.awt.print.PrinterJob;
43
44import java.io.File;
45import java.io.FilePermission;
46import java.io.IOException;
47
48import java.net.URI;
49import java.net.URISyntaxException;
50
51import java.util.ArrayList;
52import java.util.Properties;
53
54import javax.print.PrintService;
55import javax.print.attribute.HashPrintRequestAttributeSet;
56import javax.print.attribute.PrintRequestAttributeSet;
57import javax.print.attribute.ResolutionSyntax;
58import javax.print.attribute.Size2DSyntax;
59import javax.print.attribute.standard.Chromaticity;
60import javax.print.attribute.standard.Copies;
61import javax.print.attribute.standard.Destination;
62import javax.print.attribute.standard.DialogTypeSelection;
63import javax.print.attribute.standard.JobName;
64import javax.print.attribute.standard.MediaSize;
65import javax.print.attribute.standard.PrintQuality;
66import javax.print.attribute.standard.PrinterResolution;
67import javax.print.attribute.standard.SheetCollate;
68import javax.print.attribute.standard.Sides;
69import javax.print.attribute.standard.Media;
70import javax.print.attribute.standard.OrientationRequested;
71import javax.print.attribute.standard.MediaSizeName;
72import javax.print.attribute.standard.PageRanges;
73
74import sun.print.SunPageSelection;
75import sun.print.SunMinMaxPage;
76
77/**
78 * A class which initiates and executes a print job using
79 * the underlying PrinterJob graphics conversions.
80 *
81 * @see java.awt.Toolkit#getPrintJob
82 *
83 */
84public class PrintJob2D extends PrintJob implements Printable, Runnable {
85
86    private static final MediaType SIZES[] = {
87        MediaType.ISO_4A0, MediaType.ISO_2A0, MediaType.ISO_A0,
88        MediaType.ISO_A1, MediaType.ISO_A2, MediaType.ISO_A3,
89        MediaType.ISO_A4, MediaType.ISO_A5, MediaType.ISO_A6,
90        MediaType.ISO_A7, MediaType.ISO_A8, MediaType.ISO_A9,
91        MediaType.ISO_A10, MediaType.ISO_B0, MediaType.ISO_B1,
92        MediaType.ISO_B2, MediaType.ISO_B3, MediaType.ISO_B4,
93        MediaType.ISO_B5, MediaType.ISO_B6, MediaType.ISO_B7,
94        MediaType.ISO_B8, MediaType.ISO_B9, MediaType.ISO_B10,
95        MediaType.JIS_B0, MediaType.JIS_B1, MediaType.JIS_B2,
96        MediaType.JIS_B3, MediaType.JIS_B4, MediaType.JIS_B5,
97        MediaType.JIS_B6, MediaType.JIS_B7, MediaType.JIS_B8,
98        MediaType.JIS_B9, MediaType.JIS_B10, MediaType.ISO_C0,
99        MediaType.ISO_C1, MediaType.ISO_C2, MediaType.ISO_C3,
100        MediaType.ISO_C4, MediaType.ISO_C5, MediaType.ISO_C6,
101        MediaType.ISO_C7, MediaType.ISO_C8, MediaType.ISO_C9,
102        MediaType.ISO_C10, MediaType.ISO_DESIGNATED_LONG,
103        MediaType.EXECUTIVE, MediaType.FOLIO, MediaType.INVOICE,
104        MediaType.LEDGER, MediaType.NA_LETTER, MediaType.NA_LEGAL,
105        MediaType.QUARTO, MediaType.A, MediaType.B,
106        MediaType.C, MediaType.D, MediaType.E,
107        MediaType.NA_10X15_ENVELOPE, MediaType.NA_10X14_ENVELOPE,
108        MediaType.NA_10X13_ENVELOPE, MediaType.NA_9X12_ENVELOPE,
109        MediaType.NA_9X11_ENVELOPE, MediaType.NA_7X9_ENVELOPE,
110        MediaType.NA_6X9_ENVELOPE, MediaType.NA_NUMBER_9_ENVELOPE,
111        MediaType.NA_NUMBER_10_ENVELOPE, MediaType.NA_NUMBER_11_ENVELOPE,
112        MediaType.NA_NUMBER_12_ENVELOPE, MediaType.NA_NUMBER_14_ENVELOPE,
113        MediaType.INVITE_ENVELOPE, MediaType.ITALY_ENVELOPE,
114        MediaType.MONARCH_ENVELOPE, MediaType.PERSONAL_ENVELOPE
115    };
116
117    /* This array maps the above array to the objects used by the
118     * javax.print APIs
119         */
120    private static final MediaSizeName JAVAXSIZES[] = {
121        null, null, MediaSizeName.ISO_A0,
122        MediaSizeName.ISO_A1, MediaSizeName.ISO_A2, MediaSizeName.ISO_A3,
123        MediaSizeName.ISO_A4, MediaSizeName.ISO_A5, MediaSizeName.ISO_A6,
124        MediaSizeName.ISO_A7, MediaSizeName.ISO_A8, MediaSizeName.ISO_A9,
125        MediaSizeName.ISO_A10, MediaSizeName.ISO_B0, MediaSizeName.ISO_B1,
126        MediaSizeName.ISO_B2, MediaSizeName.ISO_B3, MediaSizeName.ISO_B4,
127        MediaSizeName.ISO_B5,  MediaSizeName.ISO_B6, MediaSizeName.ISO_B7,
128        MediaSizeName.ISO_B8, MediaSizeName.ISO_B9, MediaSizeName.ISO_B10,
129        MediaSizeName.JIS_B0, MediaSizeName.JIS_B1, MediaSizeName.JIS_B2,
130        MediaSizeName.JIS_B3, MediaSizeName.JIS_B4, MediaSizeName.JIS_B5,
131        MediaSizeName.JIS_B6, MediaSizeName.JIS_B7, MediaSizeName.JIS_B8,
132        MediaSizeName.JIS_B9, MediaSizeName.JIS_B10, MediaSizeName.ISO_C0,
133        MediaSizeName.ISO_C1, MediaSizeName.ISO_C2, MediaSizeName.ISO_C3,
134        MediaSizeName.ISO_C4, MediaSizeName.ISO_C5, MediaSizeName.ISO_C6,
135        null, null, null, null,
136        MediaSizeName.ISO_DESIGNATED_LONG, MediaSizeName.EXECUTIVE,
137        MediaSizeName.FOLIO, MediaSizeName.INVOICE, MediaSizeName.LEDGER,
138        MediaSizeName.NA_LETTER, MediaSizeName.NA_LEGAL,
139        MediaSizeName.QUARTO, MediaSizeName.A, MediaSizeName.B,
140        MediaSizeName.C, MediaSizeName.D, MediaSizeName.E,
141        MediaSizeName.NA_10X15_ENVELOPE, MediaSizeName.NA_10X14_ENVELOPE,
142        MediaSizeName.NA_10X13_ENVELOPE, MediaSizeName.NA_9X12_ENVELOPE,
143        MediaSizeName.NA_9X11_ENVELOPE, MediaSizeName.NA_7X9_ENVELOPE,
144        MediaSizeName.NA_6X9_ENVELOPE,
145        MediaSizeName.NA_NUMBER_9_ENVELOPE,
146        MediaSizeName.NA_NUMBER_10_ENVELOPE,
147        MediaSizeName.NA_NUMBER_11_ENVELOPE,
148        MediaSizeName.NA_NUMBER_12_ENVELOPE,
149        MediaSizeName.NA_NUMBER_14_ENVELOPE,
150        null, MediaSizeName.ITALY_ENVELOPE,
151        MediaSizeName.MONARCH_ENVELOPE, MediaSizeName.PERSONAL_ENVELOPE,
152    };
153
154
155    // widths and lengths in PostScript points (1/72 in.)
156    private static final int WIDTHS[] = {
157        /*iso-4a0*/ 4768, /*iso-2a0*/ 3370, /*iso-a0*/ 2384, /*iso-a1*/ 1684,
158        /*iso-a2*/ 1191, /*iso-a3*/ 842, /*iso-a4*/ 595, /*iso-a5*/ 420,
159        /*iso-a6*/ 298, /*iso-a7*/ 210, /*iso-a8*/ 147, /*iso-a9*/ 105,
160        /*iso-a10*/ 74, /*iso-b0*/ 2835, /*iso-b1*/ 2004, /*iso-b2*/ 1417,
161        /*iso-b3*/ 1001, /*iso-b4*/ 709, /*iso-b5*/ 499, /*iso-b6*/ 354,
162        /*iso-b7*/ 249, /*iso-b8*/ 176, /*iso-b9*/ 125, /*iso-b10*/ 88,
163        /*jis-b0*/ 2920, /*jis-b1*/ 2064, /*jis-b2*/ 1460, /*jis-b3*/ 1032,
164        /*jis-b4*/ 729, /*jis-b5*/ 516, /*jis-b6*/ 363, /*jis-b7*/ 258,
165        /*jis-b8*/ 181, /*jis-b9*/ 128, /*jis-b10*/ 91, /*iso-c0*/ 2599,
166        /*iso-c1*/ 1837, /*iso-c2*/ 1298, /*iso-c3*/ 918, /*iso-c4*/ 649,
167        /*iso-c5*/ 459, /*iso-c6*/ 323, /*iso-c7*/ 230, /*iso-c8*/ 162,
168        /*iso-c9*/ 113, /*iso-c10*/ 79, /*iso-designated-long*/ 312,
169        /*executive*/ 522, /*folio*/ 612, /*invoice*/ 396, /*ledger*/ 792,
170        /*na-letter*/ 612, /*na-legal*/ 612, /*quarto*/ 609, /*a*/ 612,
171        /*b*/ 792, /*c*/ 1224, /*d*/ 1584, /*e*/ 2448,
172        /*na-10x15-envelope*/ 720, /*na-10x14-envelope*/ 720,
173        /*na-10x13-envelope*/ 720, /*na-9x12-envelope*/ 648,
174        /*na-9x11-envelope*/ 648, /*na-7x9-envelope*/ 504,
175        /*na-6x9-envelope*/ 432, /*na-number-9-envelope*/ 279,
176        /*na-number-10-envelope*/ 297, /*na-number-11-envelope*/ 324,
177        /*na-number-12-envelope*/ 342, /*na-number-14-envelope*/ 360,
178        /*invite-envelope*/ 624, /*italy-envelope*/ 312,
179        /*monarch-envelope*/ 279, /*personal-envelope*/ 261
180    };
181    private static final int LENGTHS[] = {
182        /*iso-4a0*/ 6741, /*iso-2a0*/ 4768, /*iso-a0*/ 3370, /*iso-a1*/ 2384,
183        /*iso-a2*/ 1684, /*iso-a3*/ 1191, /*iso-a4*/ 842, /*iso-a5*/ 595,
184        /*iso-a6*/ 420, /*iso-a7*/ 298, /*iso-a8*/ 210, /*iso-a9*/ 147,
185        /*iso-a10*/ 105, /*iso-b0*/ 4008, /*iso-b1*/ 2835, /*iso-b2*/ 2004,
186        /*iso-b3*/ 1417, /*iso-b4*/ 1001, /*iso-b5*/ 729, /*iso-b6*/ 499,
187        /*iso-b7*/ 354, /*iso-b8*/ 249, /*iso-b9*/ 176, /*iso-b10*/ 125,
188        /*jis-b0*/ 4127, /*jis-b1*/ 2920, /*jis-b2*/ 2064, /*jis-b3*/ 1460,
189        /*jis-b4*/ 1032, /*jis-b5*/ 729, /*jis-b6*/ 516, /*jis-b7*/ 363,
190        /*jis-b8*/ 258, /*jis-b9*/ 181, /*jis-b10*/ 128, /*iso-c0*/ 3677,
191        /*iso-c1*/ 2599, /*iso-c2*/ 1837, /*iso-c3*/ 1298, /*iso-c4*/ 918,
192        /*iso-c5*/ 649, /*iso-c6*/ 459, /*iso-c7*/ 323, /*iso-c8*/ 230,
193        /*iso-c9*/ 162, /*iso-c10*/ 113, /*iso-designated-long*/ 624,
194        /*executive*/ 756, /*folio*/ 936, /*invoice*/ 612, /*ledger*/ 1224,
195        /*na-letter*/ 792, /*na-legal*/ 1008, /*quarto*/ 780, /*a*/ 792,
196        /*b*/ 1224, /*c*/ 1584, /*d*/ 2448, /*e*/ 3168,
197        /*na-10x15-envelope*/ 1080, /*na-10x14-envelope*/ 1008,
198        /*na-10x13-envelope*/ 936, /*na-9x12-envelope*/ 864,
199        /*na-9x11-envelope*/ 792, /*na-7x9-envelope*/ 648,
200        /*na-6x9-envelope*/ 648, /*na-number-9-envelope*/ 639,
201        /*na-number-10-envelope*/ 684, /*na-number-11-envelope*/ 747,
202        /*na-number-12-envelope*/ 792, /*na-number-14-envelope*/ 828,
203        /*invite-envelope*/ 624, /*italy-envelope*/ 652,
204        /*monarch-envelope*/ 540, /*personal-envelope*/ 468
205    };
206
207
208    private Frame frame;
209    private String docTitle = "";
210    private JobAttributes jobAttributes;
211    private PageAttributes pageAttributes;
212    private PrintRequestAttributeSet attributes;
213
214    /*
215     * Displays the native or cross-platform dialog and allows the
216     * user to update job & page attributes
217     */
218
219    /**
220     * The PrinterJob being uses to implement the PrintJob.
221     */
222    private PrinterJob printerJob;
223
224    /**
225     * The size of the page being used for the PrintJob.
226     */
227    private PageFormat pageFormat;
228
229    /**
230     * The PrinterJob and the application run on different
231     * threads and communicate through a pair of message
232     * queues. This queue is the list of Graphics that
233     * the PrinterJob has requested rendering for, but
234     * for which the application has not yet called getGraphics().
235     * In practice the length of this message queue is always
236     * 0 or 1.
237     */
238    private MessageQ graphicsToBeDrawn = new MessageQ("tobedrawn");
239
240    /**
241     * Used to communicate between the application's thread
242     * and the PrinterJob's thread this message queue holds
243     * the list of Graphics into which the application has
244     * finished drawing, but that have not yet been returned
245     * to the PrinterJob thread. Again, in practice, the
246     * length of this message queue is always 0 or 1.
247     */
248    private MessageQ graphicsDrawn = new MessageQ("drawn");
249
250    /**
251     * The last Graphics returned to the application via
252     * getGraphics. This is the Graphics into which the
253     * application is currently drawing.
254     */
255    private Graphics2D currentGraphics;
256
257    /**
258     * The zero based index of the page currently being rendered
259     * by the application.
260     */
261    private int pageIndex = -1;
262
263    // The following Strings are maintained for backward-compatibility with
264    // Properties based print control.
265    private static final String DEST_PROP = "awt.print.destination";
266    private static final String PRINTER = "printer";
267    private static final String FILE = "file";
268
269    private static final String PRINTER_PROP = "awt.print.printer";
270
271    private static final String FILENAME_PROP = "awt.print.fileName";
272
273    private static final String NUMCOPIES_PROP = "awt.print.numCopies";
274
275    private static final String OPTIONS_PROP = "awt.print.options";
276
277    private static final String ORIENT_PROP = "awt.print.orientation";
278    private static final String PORTRAIT = "portrait";
279    private static final String LANDSCAPE = "landscape";
280
281    private static final String PAPERSIZE_PROP = "awt.print.paperSize";
282    private static final String LETTER = "letter";
283    private static final String LEGAL = "legal";
284    private static final String EXECUTIVE = "executive";
285    private static final String A4 = "a4";
286
287    private Properties props;
288
289    private String options = ""; // REMIND: needs implementation
290
291    /**
292     * The thread on which PrinterJob is running.
293     * This is different than the applications thread.
294     */
295    private Thread printerJobThread;
296
297    public PrintJob2D(Frame frame,  String doctitle,
298                      final Properties props) {
299        this.props = props;
300        this.jobAttributes = new JobAttributes();
301        this.pageAttributes = new PageAttributes();
302        translateInputProps();
303        initPrintJob2D(frame, doctitle,
304                       this.jobAttributes, this.pageAttributes);
305    }
306
307    public PrintJob2D(Frame frame,  String doctitle,
308                      JobAttributes jobAttributes,
309                      PageAttributes pageAttributes) {
310        initPrintJob2D(frame, doctitle, jobAttributes, pageAttributes);
311    }
312
313    private void initPrintJob2D(Frame frame,  String doctitle,
314                                JobAttributes jobAttributes,
315                                PageAttributes pageAttributes) {
316
317        SecurityManager security = System.getSecurityManager();
318        if (security != null) {
319            security.checkPrintJobAccess();
320        }
321
322        if (frame == null &&
323            (jobAttributes == null ||
324             jobAttributes.getDialog() == DialogType.NATIVE)) {
325            throw new NullPointerException("Frame must not be null");
326        }
327        this.frame = frame;
328
329        this.docTitle = (doctitle == null) ? "" : doctitle;
330        this.jobAttributes = (jobAttributes != null)
331            ? jobAttributes : new JobAttributes();
332        this.pageAttributes = (pageAttributes != null)
333            ? pageAttributes : new PageAttributes();
334
335        // Currently, we always reduce page ranges to xxx or xxx-xxx
336        int[][] pageRanges = this.jobAttributes.getPageRanges();
337        int first = pageRanges[0][0];
338        int last = pageRanges[pageRanges.length - 1][1];
339        this.jobAttributes.setPageRanges(new int[][] {
340            new int[] { first, last }
341        });
342        this.jobAttributes.setToPage(last);
343        this.jobAttributes.setFromPage(first);
344
345
346        // Verify that the cross feed and feed resolutions are the same
347        int[] res = this.pageAttributes.getPrinterResolution();
348        if (res[0] != res[1]) {
349            throw new IllegalArgumentException("Differing cross feed and feed"+
350                                               " resolutions not supported.");
351        }
352
353        // Verify that the app has access to the file system
354        DestinationType dest= this.jobAttributes.getDestination();
355        if (dest == DestinationType.FILE) {
356            throwPrintToFile();
357
358            // check if given filename is valid
359            String destStr = jobAttributes.getFileName();
360            if ((destStr != null) &&
361                (jobAttributes.getDialog() == JobAttributes.DialogType.NONE)) {
362
363                File f = new File(destStr);
364                try {
365                    // check if this is a new file and if filename chars are valid
366                    // createNewFile returns false if file exists
367                    if (f.createNewFile()) {
368                        f.delete();
369                    }
370                } catch (IOException ioe) {
371                    throw new IllegalArgumentException("Cannot write to file:"+
372                                                       destStr);
373                } catch (SecurityException se) {
374                    //There is already file read/write access so at this point
375                    // only delete access is denied.  Just ignore it because in
376                    // most cases the file created in createNewFile gets overwritten
377                    // anyway.
378                }
379
380                 File pFile = f.getParentFile();
381                 if ((f.exists() &&
382                      (!f.isFile() || !f.canWrite())) ||
383                     ((pFile != null) &&
384                      (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
385                     throw new IllegalArgumentException("Cannot write to file:"+
386                                                        destStr);
387                 }
388            }
389        }
390    }
391
392    public boolean printDialog() {
393
394        boolean proceedWithPrint = false;
395
396        printerJob = PrinterJob.getPrinterJob();
397        if (printerJob == null) {
398            return false;
399        }
400        DialogType d = this.jobAttributes.getDialog();
401        PrintService pServ = printerJob.getPrintService();
402        if ((pServ == null) &&  (d == DialogType.NONE)){
403            return false;
404        }
405        copyAttributes(pServ);
406
407        DefaultSelectionType select =
408            this.jobAttributes.getDefaultSelection();
409        if (select == DefaultSelectionType.RANGE) {
410            attributes.add(SunPageSelection.RANGE);
411        } else if (select == DefaultSelectionType.SELECTION) {
412            attributes.add(SunPageSelection.SELECTION);
413        } else {
414            attributes.add(SunPageSelection.ALL);
415        }
416
417        if (frame != null) {
418             attributes.add(new DialogOwner(frame));
419         }
420
421        if ( d == DialogType.NONE) {
422            proceedWithPrint = true;
423        } else {
424            if (d == DialogType.NATIVE) {
425                attributes.add(DialogTypeSelection.NATIVE);
426            }  else { //  (d == DialogType.COMMON)
427                attributes.add(DialogTypeSelection.COMMON);
428            }
429            if (proceedWithPrint = printerJob.printDialog(attributes)) {
430                if (pServ == null) {
431                    // Windows gives an option to install a service
432                    // when it detects there are no printers so
433                    // we make sure we get the updated print service.
434                    pServ = printerJob.getPrintService();
435                    if (pServ == null) {
436                        return false;
437                    }
438                }
439                updateAttributes();
440                translateOutputProps();
441            }
442        }
443
444        if (proceedWithPrint) {
445
446            JobName jname = (JobName)attributes.get(JobName.class);
447            if (jname != null) {
448                printerJob.setJobName(jname.toString());
449            }
450
451            pageFormat = new PageFormat();
452
453            Media media = (Media)attributes.get(Media.class);
454            MediaSize mediaSize =  null;
455            if (media != null  && media instanceof MediaSizeName) {
456                mediaSize = MediaSize.getMediaSizeForName((MediaSizeName)media);
457            }
458
459            Paper p = pageFormat.getPaper();
460            if (mediaSize != null) {
461                p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,
462                          mediaSize.getY(MediaSize.INCH)*72.0);
463            }
464
465            if (pageAttributes.getOrigin()==OriginType.PRINTABLE) {
466                // AWT uses 1/4" borders by default
467                p.setImageableArea(18.0, 18.0,
468                                   p.getWidth()-36.0,
469                                   p.getHeight()-36.0);
470            } else {
471                p.setImageableArea(0.0,0.0,p.getWidth(),p.getHeight());
472            }
473
474            pageFormat.setPaper(p);
475
476            OrientationRequested orient =
477               (OrientationRequested)attributes.get(OrientationRequested.class);
478            if (orient!= null &&
479                orient == OrientationRequested.REVERSE_LANDSCAPE) {
480                pageFormat.setOrientation(PageFormat.REVERSE_LANDSCAPE);
481            } else if (orient == OrientationRequested.LANDSCAPE) {
482                pageFormat.setOrientation(PageFormat.LANDSCAPE);
483            } else {
484                pageFormat.setOrientation(PageFormat.PORTRAIT);
485            }
486
487            PageRanges pageRangesAttr
488                    = (PageRanges) attributes.get(PageRanges.class);
489            if (pageRangesAttr != null) {
490                // Get the PageRanges from print dialog.
491                int[][] range = pageRangesAttr.getMembers();
492
493                int prevFromPage = this.jobAttributes.getFromPage();
494                int prevToPage = this.jobAttributes.getToPage();
495
496                int currFromPage = range[0][0];
497                int currToPage = range[range.length - 1][1];
498
499                // if from < to update fromPage first followed by toPage
500                // else update toPage first followed by fromPage
501                if (currFromPage < prevToPage) {
502                    this.jobAttributes.setFromPage(currFromPage);
503                    this.jobAttributes.setToPage(currToPage);
504                } else {
505                    this.jobAttributes.setToPage(currToPage);
506                    this.jobAttributes.setFromPage(currFromPage);
507                }
508            }
509            printerJob.setPrintable(this, pageFormat);
510
511        }
512
513        return proceedWithPrint;
514    }
515
516    private void updateAttributes() {
517        Copies c = (Copies)attributes.get(Copies.class);
518        jobAttributes.setCopies(c.getValue());
519
520        SunPageSelection sel =
521            (SunPageSelection)attributes.get(SunPageSelection.class);
522        if (sel == SunPageSelection.RANGE) {
523            jobAttributes.setDefaultSelection(DefaultSelectionType.RANGE);
524        } else if (sel == SunPageSelection.SELECTION) {
525            jobAttributes.setDefaultSelection(DefaultSelectionType.SELECTION);
526        } else {
527            jobAttributes.setDefaultSelection(DefaultSelectionType.ALL);
528        }
529
530        Destination dest = (Destination)attributes.get(Destination.class);
531        if (dest != null) {
532            jobAttributes.setDestination(DestinationType.FILE);
533            jobAttributes.setFileName(dest.getURI().getPath());
534        } else {
535            jobAttributes.setDestination(DestinationType.PRINTER);
536        }
537
538        PrintService serv = printerJob.getPrintService();
539        if (serv != null) {
540            jobAttributes.setPrinter(serv.getName());
541        }
542
543        PageRanges range = (PageRanges)attributes.get(PageRanges.class);
544        int[][] members = range.getMembers();
545        jobAttributes.setPageRanges(members);
546
547        SheetCollate collation =
548            (SheetCollate)attributes.get(SheetCollate.class);
549        if (collation == SheetCollate.COLLATED) {
550            jobAttributes.setMultipleDocumentHandling(
551            MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES);
552        } else {
553            jobAttributes.setMultipleDocumentHandling(
554            MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES);
555        }
556
557        Sides sides = (Sides)attributes.get(Sides.class);
558        if (sides == Sides.TWO_SIDED_LONG_EDGE) {
559            jobAttributes.setSides(SidesType.TWO_SIDED_LONG_EDGE);
560        } else if (sides == Sides.TWO_SIDED_SHORT_EDGE) {
561            jobAttributes.setSides(SidesType.TWO_SIDED_SHORT_EDGE);
562        } else {
563            jobAttributes.setSides(SidesType.ONE_SIDED);
564        }
565
566        // PageAttributes
567
568        Chromaticity color =
569            (Chromaticity)attributes.get(Chromaticity.class);
570        if (color == Chromaticity.COLOR) {
571            pageAttributes.setColor(ColorType.COLOR);
572        } else {
573            pageAttributes.setColor(ColorType.MONOCHROME);
574        }
575
576        OrientationRequested orient =
577            (OrientationRequested)attributes.get(OrientationRequested.class);
578        if (orient == OrientationRequested.LANDSCAPE) {
579            pageAttributes.setOrientationRequested(
580                                       OrientationRequestedType.LANDSCAPE);
581        } else {
582            pageAttributes.setOrientationRequested(
583                                       OrientationRequestedType.PORTRAIT);
584        }
585
586        PrintQuality qual = (PrintQuality)attributes.get(PrintQuality.class);
587        if (qual == PrintQuality.DRAFT) {
588            pageAttributes.setPrintQuality(PrintQualityType.DRAFT);
589        } else if (qual == PrintQuality.HIGH) {
590            pageAttributes.setPrintQuality(PrintQualityType.HIGH);
591        } else { // NORMAL
592            pageAttributes.setPrintQuality(PrintQualityType.NORMAL);
593        }
594
595        Media msn = (Media)attributes.get(Media.class);
596        if (msn != null && msn instanceof MediaSizeName) {
597            MediaType mType = unMapMedia((MediaSizeName)msn);
598
599            if (mType != null) {
600                pageAttributes.setMedia(mType);
601            }
602        }
603        debugPrintAttributes(false, false);
604    }
605
606    private void debugPrintAttributes(boolean ja, boolean pa ) {
607        if (ja) {
608            System.out.println("new Attributes\ncopies = "+
609                               jobAttributes.getCopies()+
610                               "\nselection = "+
611                               jobAttributes.getDefaultSelection()+
612                               "\ndest "+jobAttributes.getDestination()+
613                               "\nfile "+jobAttributes.getFileName()+
614                               "\nfromPage "+jobAttributes.getFromPage()+
615                               "\ntoPage "+jobAttributes.getToPage()+
616                               "\ncollation "+
617                               jobAttributes.getMultipleDocumentHandling()+
618                               "\nPrinter "+jobAttributes.getPrinter()+
619                               "\nSides2 "+jobAttributes.getSides()
620                               );
621        }
622
623        if (pa) {
624            System.out.println("new Attributes\ncolor = "+
625                               pageAttributes.getColor()+
626                               "\norientation = "+
627                               pageAttributes.getOrientationRequested()+
628                               "\nquality "+pageAttributes.getPrintQuality()+
629                               "\nMedia2 "+pageAttributes.getMedia()
630                               );
631        }
632    }
633
634
635    /* From JobAttributes we will copy job name and duplex printing
636     * and destination.
637     * The majority of the rest of the attributes are reflected
638     * attributes.
639     *
640     * From PageAttributes we copy color, media size, orientation,
641     * origin type, resolution and print quality.
642     * We use the media, orientation in creating the page format, and
643     * the origin type to set its imageable area.
644     *
645     * REMIND: Interpretation of resolution, additional media sizes.
646     */
647    private void copyAttributes(PrintService printServ) {
648
649        attributes = new HashPrintRequestAttributeSet();
650        attributes.add(new JobName(docTitle, null));
651        PrintService pServ = printServ;
652
653        String printerName = jobAttributes.getPrinter();
654        if (printerName != null && printerName != ""
655            && pServ != null && !printerName.equals(pServ.getName())) {
656
657            // Search for the given printerName in the list of PrintServices
658            PrintService []services = PrinterJob.lookupPrintServices();
659            try {
660                for (int i=0; i<services.length; i++) {
661                    if (printerName.equals(services[i].getName())) {
662                        printerJob.setPrintService(services[i]);
663                        pServ = services[i];
664                        break;
665                    }
666                }
667            } catch (PrinterException pe) {
668            }
669        }
670
671        DestinationType dest = jobAttributes.getDestination();
672        if (dest == DestinationType.FILE && pServ != null &&
673            pServ.isAttributeCategorySupported(Destination.class)) {
674
675            String fileName = jobAttributes.getFileName();
676
677            Destination defaultDest;
678            if (fileName == null && (defaultDest = (Destination)pServ.
679                    getDefaultAttributeValue(Destination.class)) != null) {
680                attributes.add(defaultDest);
681            } else {
682                URI uri = null;
683                try {
684                    if (fileName != null) {
685                        if (fileName.equals("")) {
686                            fileName = ".";
687                        }
688                    } else {
689                        // defaultDest should not be null.  The following code
690                        // is only added to safeguard against a possible
691                        // buggy implementation of a PrintService having a
692                        // null default Destination.
693                        fileName = "out.prn";
694                    }
695                    uri = (new File(fileName)).toURI();
696                } catch (SecurityException se) {
697                    try {
698                        // '\\' file separator is illegal character in opaque
699                        // part and causes URISyntaxException, so we replace
700                        // it with '/'
701                        fileName = fileName.replace('\\', '/');
702                        uri = new URI("file:"+fileName);
703                    } catch (URISyntaxException e) {
704                    }
705                }
706                if (uri != null) {
707                    attributes.add(new Destination(uri));
708                }
709            }
710        }
711        attributes.add(new SunMinMaxPage(jobAttributes.getMinPage(),
712                                         jobAttributes.getMaxPage()));
713        SidesType sType = jobAttributes.getSides();
714        if (sType == SidesType.TWO_SIDED_LONG_EDGE) {
715            attributes.add(Sides.TWO_SIDED_LONG_EDGE);
716        } else if (sType == SidesType.TWO_SIDED_SHORT_EDGE) {
717            attributes.add(Sides.TWO_SIDED_SHORT_EDGE);
718        } else if (sType == SidesType.ONE_SIDED) {
719            attributes.add(Sides.ONE_SIDED);
720        }
721
722        MultipleDocumentHandlingType hType =
723          jobAttributes.getMultipleDocumentHandling();
724        if (hType ==
725            MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES) {
726          attributes.add(SheetCollate.COLLATED);
727        } else {
728          attributes.add(SheetCollate.UNCOLLATED);
729        }
730
731        attributes.add(new Copies(jobAttributes.getCopies()));
732
733        attributes.add(new PageRanges(jobAttributes.getFromPage(),
734                                      jobAttributes.getToPage()));
735
736        if (pageAttributes.getColor() == ColorType.COLOR) {
737            attributes.add(Chromaticity.COLOR);
738        } else {
739            attributes.add(Chromaticity.MONOCHROME);
740        }
741
742        pageFormat = printerJob.defaultPage();
743        if (pageAttributes.getOrientationRequested() ==
744            OrientationRequestedType.LANDSCAPE) {
745            pageFormat.setOrientation(PageFormat.LANDSCAPE);
746                attributes.add(OrientationRequested.LANDSCAPE);
747        } else {
748                pageFormat.setOrientation(PageFormat.PORTRAIT);
749                attributes.add(OrientationRequested.PORTRAIT);
750        }
751
752        MediaType media = pageAttributes.getMedia();
753        MediaSizeName msn = mapMedia(media);
754        if (msn != null) {
755            attributes.add(msn);
756        }
757
758        PrintQualityType qType =
759            pageAttributes.getPrintQuality();
760        if (qType == PrintQualityType.DRAFT) {
761            attributes.add(PrintQuality.DRAFT);
762        } else if (qType == PrintQualityType.NORMAL) {
763            attributes.add(PrintQuality.NORMAL);
764        } else if (qType == PrintQualityType.HIGH) {
765            attributes.add(PrintQuality.HIGH);
766        }
767    }
768
769    /**
770     * Gets a Graphics object that will draw to the next page.
771     * The page is sent to the printer when the graphics
772     * object is disposed.  This graphics object will also implement
773     * the PrintGraphics interface.
774     * @see java.awt.PrintGraphics
775     */
776    public Graphics getGraphics() {
777
778        Graphics printGraphics = null;
779
780        synchronized (this) {
781            ++pageIndex;
782
783            // Thread should not be created after end has been called.
784            // One way to detect this is if any of the graphics queue
785            //  has been closed.
786            if (pageIndex == 0 && !graphicsToBeDrawn.isClosed()) {
787
788            /* We start a thread on which the PrinterJob will run.
789             * The PrinterJob will ask for pages on that thread
790             * and will use a message queue to fulfill the application's
791             * requests for a Graphics on the application's
792             * thread.
793             */
794
795                startPrinterJobThread();
796
797            }
798            notify();
799        }
800
801        /* If the application has already been handed back
802         * a graphics then we need to put that graphics into
803         * the drawn queue so that the PrinterJob thread can
804         * return to the print system.
805         */
806        if (currentGraphics != null) {
807            graphicsDrawn.append(currentGraphics);
808            currentGraphics = null;
809        }
810
811        /* We'll block here until a new graphics becomes
812         * available.
813         */
814
815        currentGraphics = graphicsToBeDrawn.pop();
816
817        if (currentGraphics instanceof PeekGraphics) {
818            ( (PeekGraphics) currentGraphics).setAWTDrawingOnly();
819            graphicsDrawn.append(currentGraphics);
820            currentGraphics = graphicsToBeDrawn.pop();
821        }
822
823
824        if (currentGraphics != null) {
825
826            /* In the PrintJob API, the origin is at the upper-
827             * left of the imageable area when using the new "printable"
828             * origin attribute, otherwise its the physical origin (for
829             * backwards compatibility. We emulate this by createing
830             * a PageFormat which matches and then performing the
831             * translate to the origin. This is a no-op if physical
832             * origin is specified.
833             */
834            currentGraphics.translate(pageFormat.getImageableX(),
835                                      pageFormat.getImageableY());
836
837            /* Scale to accommodate AWT's notion of printer resolution */
838            double awtScale = 72.0/getPageResolutionInternal();
839            currentGraphics.scale(awtScale, awtScale);
840
841            /* The caller wants a Graphics instance but we do
842             * not want them to make 2D calls. We can't hand
843             * back a Graphics2D. The returned Graphics also
844             * needs to implement PrintGraphics, so we wrap
845             * the Graphics2D instance. The PrintJob API has
846             * the application dispose of the Graphics so
847             * we create a copy of the one returned by PrinterJob.
848             */
849            printGraphics = new ProxyPrintGraphics(currentGraphics.create(),
850                                                   this);
851
852        }
853
854        return printGraphics;
855    }
856
857    /**
858     * Returns the dimensions of the page in pixels.
859     * The resolution of the page is chosen so that it
860     * is similar to the screen resolution.
861     * Except (since 1.3) when the application specifies a resolution.
862     * In that case it is scaled accordingly.
863     */
864    public Dimension getPageDimension() {
865        double wid, hgt, scale;
866        if (pageAttributes != null &&
867            pageAttributes.getOrigin()==OriginType.PRINTABLE) {
868            wid = pageFormat.getImageableWidth();
869            hgt = pageFormat.getImageableHeight();
870        } else {
871            wid = pageFormat.getWidth();
872            hgt = pageFormat.getHeight();
873        }
874        scale = getPageResolutionInternal() / 72.0;
875        return new Dimension((int)(wid * scale), (int)(hgt * scale));
876    }
877
878     private double getPageResolutionInternal() {
879        if (pageAttributes != null) {
880            int []res = pageAttributes.getPrinterResolution();
881            if (res[2] == 3) {
882                return res[0];
883            } else /* if (res[2] == 4) */ {
884                return (res[0] * 2.54);
885            }
886        } else {
887            return 72.0;
888        }
889    }
890
891    /**
892     * Returns the resolution of the page in pixels per inch.
893     * Note that this doesn't have to correspond to the physical
894     * resolution of the printer.
895     */
896    public int getPageResolution() {
897        return (int)getPageResolutionInternal();
898    }
899
900    /**
901     * Returns true if the last page will be printed first.
902     */
903    public boolean lastPageFirst() {
904        return false;
905    }
906
907    /**
908     * Ends the print job and does any necessary cleanup.
909     */
910    public synchronized void end() {
911
912        /* Prevent the PrinterJob thread from appending any more
913         * graphics to the to-be-drawn queue
914         */
915        graphicsToBeDrawn.close();
916
917        /* If we have a currentGraphics it was the last one returned to the
918         * PrintJob client. Append it to the drawn queue so that print()
919         * will return allowing the page to be flushed.
920         * This really ought to happen in dispose() but for whatever reason
921         * that isn't how the old PrintJob worked even though its spec
922         * said dispose() flushed the page.
923         */
924        if (currentGraphics != null) {
925            graphicsDrawn.append(currentGraphics);
926        }
927        graphicsDrawn.closeWhenEmpty();
928
929        /* Wait for the PrinterJob.print() thread to terminate, ensuring
930         * that RasterPrinterJob has made its end doc call, and resources
931         * are released, files closed etc.
932         */
933        if( printerJobThread != null && printerJobThread.isAlive() ){
934            try {
935                printerJobThread.join();
936            } catch (InterruptedException e) {
937            }
938        }
939    }
940
941    /**
942     * Ends this print job once it is no longer referenced.
943     * @see #end
944     */
945    @SuppressWarnings("deprecation")
946    public void finalize() {
947        end();
948    }
949
950    /**
951     * Prints the page at the specified index into the specified
952     * {@link Graphics} context in the specified
953     * format.  A {@code PrinterJob} calls the
954     * {@code Printable} interface to request that a page be
955     * rendered into the context specified by
956     * {@code graphics}.  The format of the page to be drawn is
957     * specified by {@code pageFormat}.  The zero based index
958     * of the requested page is specified by {@code pageIndex}.
959     * If the requested page does not exist then this method returns
960     * NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned.
961     * The {@code Graphics} class or subclass implements the
962     * {@link java.awt.PrintGraphics} interface to provide additional
963     * information.  If the {@code Printable} object
964     * aborts the print job then it throws a {@link PrinterException}.
965     * @param graphics the context into which the page is drawn
966     * @param pageFormat the size and orientation of the page being drawn
967     * @param pageIndex the zero based index of the page to be drawn
968     * @return PAGE_EXISTS if the page is rendered successfully
969     *         or NO_SUCH_PAGE if {@code pageIndex} specifies a
970     *         non-existent page.
971     * @exception java.awt.print.PrinterException
972     *         thrown when the print job is terminated.
973     */
974    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
975                 throws PrinterException {
976
977        int result;
978
979        /* This method will be called by the PrinterJob on a thread other
980         * that the application's thread. We hold on to the graphics
981         * until we can rendevous with the application's thread and
982         * hand over the graphics. The application then does all the
983         * drawing. When the application is done drawing we rendevous
984         * again with the PrinterJob thread and release the Graphics
985         * so that it knows we are done.
986         */
987
988        /* Add the graphics to the message queue of graphics to
989         * be rendered. This is really a one slot queue. The
990         * application's thread will come along and remove the
991         * graphics from the queue when the app asks for a graphics.
992         */
993        graphicsToBeDrawn.append( (Graphics2D) graphics);
994
995        /* We now wait for the app's thread to finish drawing on
996         * the Graphics. This thread will sleep until the application
997         * release the graphics by placing it in the graphics drawn
998         * message queue. If the application signals that it is
999         * finished drawing the entire document then we'll get null
1000         * returned when we try and pop a finished graphic.
1001         */
1002        if (graphicsDrawn.pop() != null) {
1003            result = PAGE_EXISTS;
1004        } else {
1005            result = NO_SUCH_PAGE;
1006        }
1007
1008        return result;
1009    }
1010
1011    private void startPrinterJobThread() {
1012        printerJobThread =
1013            new Thread(null, this, "printerJobThread", 0, false);
1014        printerJobThread.start();
1015    }
1016
1017
1018    public void run() {
1019
1020        try {
1021            attributes.remove(PageRanges.class);
1022            printerJob.print(attributes);
1023        } catch (PrinterException e) {
1024            //REMIND: need to store this away and not rethrow it.
1025        }
1026
1027        /* Close the message queues so that nobody is stuck
1028         * waiting for one.
1029         */
1030        graphicsToBeDrawn.closeWhenEmpty();
1031        graphicsDrawn.close();
1032    }
1033
1034    private class MessageQ {
1035
1036        private String qid="noname";
1037
1038        private ArrayList<Graphics2D> queue = new ArrayList<>();
1039
1040        MessageQ(String id) {
1041          qid = id;
1042        }
1043
1044        synchronized void closeWhenEmpty() {
1045
1046            while (queue != null && queue.size() > 0) {
1047                try {
1048                    wait(1000);
1049                } catch (InterruptedException e) {
1050                    // do nothing.
1051                }
1052            }
1053
1054            queue = null;
1055            notifyAll();
1056        }
1057
1058        synchronized void close() {
1059            queue = null;
1060            notifyAll();
1061        }
1062
1063        synchronized boolean append(Graphics2D g) {
1064
1065            boolean queued = false;
1066
1067            if (queue != null) {
1068                queue.add(g);
1069                queued = true;
1070                notify();
1071            }
1072
1073            return queued;
1074        }
1075
1076        synchronized Graphics2D pop() {
1077            Graphics2D g = null;
1078
1079            while (g == null && queue != null) {
1080
1081                if (queue.size() > 0) {
1082                    g = queue.remove(0);
1083                    notify();
1084
1085                } else {
1086                    try {
1087                        wait(2000);
1088                    } catch (InterruptedException e) {
1089                        // do nothing.
1090                    }
1091                }
1092            }
1093
1094            return g;
1095        }
1096
1097        synchronized boolean isClosed() {
1098            return queue == null;
1099        }
1100
1101    }
1102
1103
1104    private static int[] getSize(MediaType mType) {
1105        int []dim = new int[2];
1106        dim[0] = 612;
1107        dim[1] = 792;
1108
1109        for (int i=0; i < SIZES.length; i++) {
1110            if (SIZES[i] == mType) {
1111                dim[0] = WIDTHS[i];
1112                dim[1] = LENGTHS[i];
1113                break;
1114            }
1115        }
1116        return dim;
1117    }
1118
1119    public static MediaSizeName mapMedia(MediaType mType) {
1120        MediaSizeName media = null;
1121
1122        // JAVAXSIZES.length and SIZES.length must be equal!
1123        // Attempt to recover by getting the smaller size.
1124        int length = Math.min(SIZES.length, JAVAXSIZES.length);
1125
1126        for (int i=0; i < length; i++) {
1127            if (SIZES[i] == mType) {
1128                if ((JAVAXSIZES[i] != null) &&
1129                    MediaSize.getMediaSizeForName(JAVAXSIZES[i]) != null) {
1130                    media = JAVAXSIZES[i];
1131                    break;
1132                } else {
1133                    /* create Custom Media */
1134                    media = new CustomMediaSizeName(SIZES[i].toString());
1135
1136                    float w = (float)Math.rint(WIDTHS[i]  / 72.0);
1137                    float h = (float)Math.rint(LENGTHS[i] / 72.0);
1138                    if (w > 0.0 && h > 0.0) {
1139                        // add new created MediaSize to our static map
1140                        // so it will be found when we call findMedia
1141                        new MediaSize(w, h, Size2DSyntax.INCH, media);
1142                    }
1143
1144                    break;
1145                }
1146            }
1147        }
1148        return media;
1149    }
1150
1151
1152    public static MediaType unMapMedia(MediaSizeName mSize) {
1153        MediaType media = null;
1154
1155        // JAVAXSIZES.length and SIZES.length must be equal!
1156        // Attempt to recover by getting the smaller size.
1157        int length = Math.min(SIZES.length, JAVAXSIZES.length);
1158
1159        for (int i=0; i < length; i++) {
1160            if (JAVAXSIZES[i] == mSize) {
1161                if (SIZES[i] != null) {
1162                    media = SIZES[i];
1163                    break;
1164                }
1165            }
1166        }
1167        return media;
1168    }
1169
1170    private void translateInputProps() {
1171        if (props == null) {
1172            return;
1173        }
1174
1175        String str;
1176
1177        str = props.getProperty(DEST_PROP);
1178        if (str != null) {
1179            if (str.equals(PRINTER)) {
1180                jobAttributes.setDestination(DestinationType.PRINTER);
1181            } else if (str.equals(FILE)) {
1182                jobAttributes.setDestination(DestinationType.FILE);
1183            }
1184        }
1185        str = props.getProperty(PRINTER_PROP);
1186        if (str != null) {
1187            jobAttributes.setPrinter(str);
1188        }
1189        str = props.getProperty(FILENAME_PROP);
1190        if (str != null) {
1191            jobAttributes.setFileName(str);
1192        }
1193        str = props.getProperty(NUMCOPIES_PROP);
1194        if (str != null) {
1195            jobAttributes.setCopies(Integer.parseInt(str));
1196        }
1197
1198        this.options = props.getProperty(OPTIONS_PROP, "");
1199
1200        str = props.getProperty(ORIENT_PROP);
1201        if (str != null) {
1202            if (str.equals(PORTRAIT)) {
1203                pageAttributes.setOrientationRequested(
1204                                        OrientationRequestedType.PORTRAIT);
1205            } else if (str.equals(LANDSCAPE)) {
1206                pageAttributes.setOrientationRequested(
1207                                        OrientationRequestedType.LANDSCAPE);
1208            }
1209        }
1210        str = props.getProperty(PAPERSIZE_PROP);
1211        if (str != null) {
1212            if (str.equals(LETTER)) {
1213                pageAttributes.setMedia(SIZES[MediaType.LETTER.hashCode()]);
1214            } else if (str.equals(LEGAL)) {
1215                pageAttributes.setMedia(SIZES[MediaType.LEGAL.hashCode()]);
1216            } else if (str.equals(EXECUTIVE)) {
1217                pageAttributes.setMedia(SIZES[MediaType.EXECUTIVE.hashCode()]);
1218            } else if (str.equals(A4)) {
1219                pageAttributes.setMedia(SIZES[MediaType.A4.hashCode()]);
1220            }
1221        }
1222    }
1223
1224    private void translateOutputProps() {
1225        if (props == null) {
1226            return;
1227        }
1228
1229        String str;
1230
1231        props.setProperty(DEST_PROP,
1232            (jobAttributes.getDestination() == DestinationType.PRINTER) ?
1233                          PRINTER : FILE);
1234        str = jobAttributes.getPrinter();
1235        if (str != null && !str.equals("")) {
1236            props.setProperty(PRINTER_PROP, str);
1237        }
1238        str = jobAttributes.getFileName();
1239        if (str != null && !str.equals("")) {
1240            props.setProperty(FILENAME_PROP, str);
1241        }
1242        int copies = jobAttributes.getCopies();
1243        if (copies > 0) {
1244            props.setProperty(NUMCOPIES_PROP, "" + copies);
1245        }
1246        str = this.options;
1247        if (str != null && !str.equals("")) {
1248            props.setProperty(OPTIONS_PROP, str);
1249        }
1250        props.setProperty(ORIENT_PROP,
1251            (pageAttributes.getOrientationRequested() ==
1252             OrientationRequestedType.PORTRAIT)
1253                          ? PORTRAIT : LANDSCAPE);
1254        MediaType media = SIZES[pageAttributes.getMedia().hashCode()];
1255        if (media == MediaType.LETTER) {
1256            str = LETTER;
1257        } else if (media == MediaType.LEGAL) {
1258            str = LEGAL;
1259        } else if (media == MediaType.EXECUTIVE) {
1260            str = EXECUTIVE;
1261        } else if (media == MediaType.A4) {
1262            str = A4;
1263        } else {
1264            str = media.toString();
1265        }
1266        props.setProperty(PAPERSIZE_PROP, str);
1267    }
1268
1269    private void throwPrintToFile() {
1270        SecurityManager security = System.getSecurityManager();
1271        FilePermission printToFilePermission = null;
1272        if (security != null) {
1273            if (printToFilePermission == null) {
1274                printToFilePermission =
1275                    new FilePermission("<<ALL FILES>>", "read,write");
1276            }
1277            security.checkPermission(printToFilePermission);
1278        }
1279    }
1280
1281}
1282