1 /*
2  * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.print;
27 
28 import java.io.FilePermission;
29 
30 import java.awt.Color;
31 import java.awt.Dialog;
32 import java.awt.Frame;
33 import java.awt.Graphics2D;
34 import java.awt.GraphicsConfiguration;
35 import java.awt.GraphicsEnvironment;
36 import java.awt.HeadlessException;
37 import java.awt.KeyboardFocusManager;
38 import java.awt.Rectangle;
39 import java.awt.Shape;
40 import java.awt.geom.AffineTransform;
41 import java.awt.geom.Point2D;
42 import java.awt.geom.Rectangle2D;
43 import java.awt.image.BufferedImage;
44 import java.awt.print.Book;
45 import java.awt.print.Pageable;
46 import java.awt.print.PageFormat;
47 import java.awt.print.Paper;
48 import java.awt.print.Printable;
49 import java.awt.print.PrinterAbortException;
50 import java.awt.print.PrinterException;
51 import java.awt.print.PrinterJob;
52 import java.awt.Window;
53 import java.io.File;
54 import java.io.IOException;
55 import java.util.ArrayList;
56 import java.util.Locale;
57 import sun.awt.image.ByteInterleavedRaster;
58 
59 import javax.print.Doc;
60 import javax.print.DocFlavor;
61 import javax.print.DocPrintJob;
62 import javax.print.PrintException;
63 import javax.print.PrintService;
64 import javax.print.PrintServiceLookup;
65 import javax.print.ServiceUI;
66 import javax.print.StreamPrintService;
67 import javax.print.StreamPrintServiceFactory;
68 import javax.print.attribute.Attribute;
69 import javax.print.attribute.AttributeSet;
70 import javax.print.attribute.HashPrintRequestAttributeSet;
71 import javax.print.attribute.PrintRequestAttributeSet;
72 import javax.print.attribute.ResolutionSyntax;
73 import javax.print.attribute.Size2DSyntax;
74 import javax.print.attribute.standard.Copies;
75 import javax.print.attribute.standard.Destination;
76 import javax.print.attribute.standard.DialogTypeSelection;
77 import javax.print.attribute.standard.DialogOwner;
78 import javax.print.attribute.standard.Fidelity;
79 import javax.print.attribute.standard.JobName;
80 import javax.print.attribute.standard.JobSheets;
81 import javax.print.attribute.standard.Media;
82 import javax.print.attribute.standard.MediaPrintableArea;
83 import javax.print.attribute.standard.MediaSize;
84 import javax.print.attribute.standard.MediaSizeName;
85 import javax.print.attribute.standard.OrientationRequested;
86 import javax.print.attribute.standard.PageRanges;
87 import javax.print.attribute.standard.PrinterResolution;
88 import javax.print.attribute.standard.PrinterState;
89 import javax.print.attribute.standard.PrinterStateReason;
90 import javax.print.attribute.standard.PrinterStateReasons;
91 import javax.print.attribute.standard.PrinterIsAcceptingJobs;
92 import javax.print.attribute.standard.RequestingUserName;
93 import javax.print.attribute.standard.SheetCollate;
94 import javax.print.attribute.standard.Sides;
95 
96 /**
97  * A class which rasterizes a printer job.
98  *
99  * @author Richard Blanchard
100  */
101 public abstract class RasterPrinterJob extends PrinterJob {
102 
103  /* Class Constants */
104 
105      /* Printer destination type. */
106     protected static final int PRINTER = 0;
107 
108      /* File destination type.  */
109     protected static final int FILE = 1;
110 
111     /* Stream destination type.  */
112     protected static final int STREAM = 2;
113 
114     /**
115      * Pageable MAX pages
116      */
117     protected static final int MAX_UNKNOWN_PAGES = 9999;
118 
119     protected static final int PD_ALLPAGES = 0x00000000;
120     protected static final int PD_SELECTION = 0x00000001;
121     protected static final int PD_PAGENUMS = 0x00000002;
122     protected static final int PD_NOSELECTION = 0x00000004;
123 
124     /**
125      * Maximum amount of memory in bytes to use for the
126      * buffered image "band". 4Mb is a compromise between
127      * limiting the number of bands on hi-res printers and
128      * not using too much of the Java heap or causing paging
129      * on systems with little RAM.
130      */
131     private static final int MAX_BAND_SIZE = (1024 * 1024 * 4);
132 
133     /* Dots Per Inch */
134     private static final float DPI = 72.0f;
135 
136     /**
137      * Useful mainly for debugging, this system property
138      * can be used to force the printing code to print
139      * using a particular pipeline. The two currently
140      * supported values are FORCE_RASTER and FORCE_PDL.
141      */
142     private static final String FORCE_PIPE_PROP = "sun.java2d.print.pipeline";
143 
144     /**
145      * When the system property FORCE_PIPE_PROP has this value
146      * then each page of a print job will be rendered through
147      * the raster pipeline.
148      */
149     private static final String FORCE_RASTER = "raster";
150 
151     /**
152      * When the system property FORCE_PIPE_PROP has this value
153      * then each page of a print job will be rendered through
154      * the PDL pipeline.
155      */
156     private static final String FORCE_PDL = "pdl";
157 
158     /**
159      * When the system property SHAPE_TEXT_PROP has this value
160      * then text is always rendered as a shape, and no attempt is made
161      * to match the font through GDI
162      */
163     private static final String SHAPE_TEXT_PROP = "sun.java2d.print.shapetext";
164 
165     /**
166      * values obtained from System properties in static initialiser block
167      */
168     public static boolean forcePDL = false;
169     public static boolean forceRaster = false;
170     public static boolean shapeTextProp = false;
171 
172     static {
173         /* The system property FORCE_PIPE_PROP
174          * can be used to force the printing code to
175          * use a particular pipeline. Either the raster
176          * pipeline or the pdl pipeline can be forced.
177          */
178         @SuppressWarnings("removal")
179         String forceStr = java.security.AccessController.doPrivileged(
180                    new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));
181 
182         if (forceStr != null) {
183             if (forceStr.equalsIgnoreCase(FORCE_PDL)) {
184                 forcePDL = true;
185             } else if (forceStr.equalsIgnoreCase(FORCE_RASTER)) {
186                 forceRaster = true;
187             }
188         }
189 
190         @SuppressWarnings("removal")
191         String shapeTextStr =java.security.AccessController.doPrivileged(
192                    new sun.security.action.GetPropertyAction(SHAPE_TEXT_PROP));
193 
194         if (shapeTextStr != null) {
195             shapeTextProp = true;
196         }
197     }
198 
199     /* Instance Variables */
200 
201     /**
202      * Used to minimize GC & reallocation of band when printing
203      */
204     private int cachedBandWidth = 0;
205     private int cachedBandHeight = 0;
206     private BufferedImage cachedBand = null;
207 
208     /**
209      * The number of book copies to be printed.
210      */
211     private int mNumCopies = 1;
212 
213     /**
214      * Collation effects the order of the pages printed
215      * when multiple copies are requested. For two copies
216      * of a three page document the page order is:
217      *  mCollate true: 1, 2, 3, 1, 2, 3
218      *  mCollate false: 1, 1, 2, 2, 3, 3
219      */
220     private boolean mCollate = false;
221 
222     /**
223      * The zero based indices of the first and last
224      * pages to be printed. If 'mFirstPage' is
225      * UNDEFINED_PAGE_NUM then the first page to
226      * be printed is page 0. If 'mLastPage' is
227      * UNDEFINED_PAGE_NUM then the last page to
228      * be printed is the last one in the book.
229      */
230     private int mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
231     private int mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
232 
233     /**
234      * The previous print stream Paper
235      * Used to check if the paper size has changed such that the
236      * implementation needs to emit the new paper size information
237      * into the print stream.
238      * Since we do our own rotation, and the margins aren't relevant,
239      * Its strictly the dimensions of the paper that we will check.
240      */
241     private Paper previousPaper;
242 
243     /**
244      * The document to be printed. It is initialized to an
245      * empty (zero pages) book.
246      */
247 // MacOSX - made protected so subclasses can reference it.
248     protected Pageable mDocument = new Book();
249 
250     /**
251      * The name of the job being printed.
252      */
253     private String mDocName = "Java Printing";
254 
255 
256     /**
257      * Printing cancellation flags
258      */
259  // MacOSX - made protected so subclasses can reference it.
260     protected boolean performingPrinting = false;
261  // MacOSX - made protected so subclasses can reference it.
262     protected boolean userCancelled = false;
263 
264    /**
265     * Print to file permission variables.
266     */
267     private FilePermission printToFilePermission;
268 
269     /**
270      * List of areas & the graphics state for redrawing
271      */
272     private ArrayList<GraphicsState> redrawList = new ArrayList<>();
273 
274 
275     /* variables representing values extracted from an attribute set.
276      * These take precedence over values set on a printer job
277      */
278     private int copiesAttr;
279     private String jobNameAttr;
280     private String userNameAttr;
281     private PageRanges pageRangesAttr;
282     protected PrinterResolution printerResAttr;
283     protected Sides sidesAttr;
284     protected String destinationAttr;
285     protected boolean noJobSheet = false;
286     protected int mDestType = RasterPrinterJob.FILE;
287     protected String mDestination = "";
288     protected boolean collateAttReq = false;
289 
290     /**
291      * Device rotation flag, if it support 270, this is set to true;
292      */
293     protected boolean landscapeRotates270 = false;
294 
295    /**
296      * attributes used by no-args page and print dialog and print method to
297      * communicate state
298      */
299     protected PrintRequestAttributeSet attributes = null;
300 
301     /**
302      * Class to keep state information for redrawing areas
303      * "region" is an area at as a high a resolution as possible.
304      * The redrawing code needs to look at sx, sy to calculate the scale
305      * to device resolution.
306      */
307     private class GraphicsState {
308         Rectangle2D region;  // Area of page to repaint
309         Shape theClip;       // image drawing clip.
310         AffineTransform theTransform; // to transform clip to dev coords.
311         double sx;           // X scale from region to device resolution
312         double sy;           // Y scale from region to device resolution
313     }
314 
315     /**
316      * Service for this job
317      */
318     protected PrintService myService;
319 
320  /* Constructors */
321 
RasterPrinterJob()322     public RasterPrinterJob()
323     {
324     }
325 
326 /* Abstract Methods */
327 
328     /**
329      * Returns the resolution in dots per inch across the width
330      * of the page.
331      */
getXRes()332     protected abstract double getXRes();
333 
334     /**
335      * Returns the resolution in dots per inch down the height
336      * of the page.
337      */
getYRes()338     protected abstract double getYRes();
339 
340     /**
341      * Must be obtained from the current printer.
342      * Value is in device pixels.
343      * Not adjusted for orientation of the paper.
344      */
getPhysicalPrintableX(Paper p)345     protected abstract double getPhysicalPrintableX(Paper p);
346 
347     /**
348      * Must be obtained from the current printer.
349      * Value is in device pixels.
350      * Not adjusted for orientation of the paper.
351      */
getPhysicalPrintableY(Paper p)352     protected abstract double getPhysicalPrintableY(Paper p);
353 
354     /**
355      * Must be obtained from the current printer.
356      * Value is in device pixels.
357      * Not adjusted for orientation of the paper.
358      */
getPhysicalPrintableWidth(Paper p)359     protected abstract double getPhysicalPrintableWidth(Paper p);
360 
361     /**
362      * Must be obtained from the current printer.
363      * Value is in device pixels.
364      * Not adjusted for orientation of the paper.
365      */
getPhysicalPrintableHeight(Paper p)366     protected abstract double getPhysicalPrintableHeight(Paper p);
367 
368     /**
369      * Must be obtained from the current printer.
370      * Value is in device pixels.
371      * Not adjusted for orientation of the paper.
372      */
getPhysicalPageWidth(Paper p)373     protected abstract double getPhysicalPageWidth(Paper p);
374 
375     /**
376      * Must be obtained from the current printer.
377      * Value is in device pixels.
378      * Not adjusted for orientation of the paper.
379      */
getPhysicalPageHeight(Paper p)380     protected abstract double getPhysicalPageHeight(Paper p);
381 
382     /**
383      * Begin a new page.
384      */
startPage(PageFormat format, Printable painter, int index, boolean paperChanged)385     protected abstract void startPage(PageFormat format, Printable painter,
386                                       int index, boolean paperChanged)
387         throws PrinterException;
388 
389     /**
390      * End a page.
391      */
endPage(PageFormat format, Printable painter, int index)392     protected abstract void endPage(PageFormat format, Printable painter,
393                                     int index)
394         throws PrinterException;
395 
396     /**
397      * Prints the contents of the array of ints, 'data'
398      * to the current page. The band is placed at the
399      * location (x, y) in device coordinates on the
400      * page. The width and height of the band is
401      * specified by the caller.
402      */
printBand(byte[] data, int x, int y, int width, int height)403     protected abstract void printBand(byte[] data, int x, int y,
404                                       int width, int height)
405         throws PrinterException;
406 
407 /* Instance Methods */
408 
409     /**
410       * save graphics state of a PathGraphics for later redrawing
411       * of part of page represented by the region in that state
412       */
413 
saveState(AffineTransform at, Shape clip, Rectangle2D region, double sx, double sy)414     public void saveState(AffineTransform at, Shape clip,
415                           Rectangle2D region, double sx, double sy) {
416         GraphicsState gstate = new GraphicsState();
417         gstate.theTransform = at;
418         gstate.theClip = clip;
419         gstate.region = region;
420         gstate.sx = sx;
421         gstate.sy = sy;
422         redrawList.add(gstate);
423     }
424 
425 
426     /*
427      * A convenience method which returns the default service
428      * for 2D {@code PrinterJob}s.
429      * May return null if there is no suitable default (although there
430      * may still be 2D services available).
431      * @return default 2D print service, or null.
432      * @since     1.4
433      */
lookupDefaultPrintService()434     protected static PrintService lookupDefaultPrintService() {
435         PrintService service = PrintServiceLookup.lookupDefaultPrintService();
436 
437         /* Pageable implies Printable so checking both isn't strictly needed */
438         if (service != null &&
439             service.isDocFlavorSupported(
440                                 DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&
441             service.isDocFlavorSupported(
442                                 DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
443             return service;
444         } else {
445            PrintService []services =
446              PrintServiceLookup.lookupPrintServices(
447                                 DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);
448            if (services.length > 0) {
449                return services[0];
450            }
451         }
452         return null;
453     }
454 
455    /**
456      * Returns the service (printer) for this printer job.
457      * Implementations of this class which do not support print services
458      * may return null;
459      * @return the service for this printer job.
460      *
461      */
getPrintService()462     public PrintService getPrintService() {
463         if (myService == null) {
464             PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
465             if (svc != null &&
466                 svc.isDocFlavorSupported(
467                      DocFlavor.SERVICE_FORMATTED.PAGEABLE)) {
468                 try {
469                     setPrintService(svc);
470                     myService = svc;
471                 } catch (PrinterException e) {
472                 }
473             }
474             if (myService == null) {
475                 PrintService[] svcs = PrintServiceLookup.lookupPrintServices(
476                     DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);
477                 if (svcs.length > 0) {
478                     try {
479                         setPrintService(svcs[0]);
480                         myService = svcs[0];
481                     } catch (PrinterException e) {
482                     }
483                 }
484             }
485         }
486         return myService;
487     }
488 
489     /**
490      * Associate this PrinterJob with a new PrintService.
491      *
492      * Throws {@code PrinterException} if the specified service
493      * cannot support the {@code Pageable} and
494      * {@code Printable} interfaces necessary to support 2D printing.
495      * @param service print service which supports 2D printing.
496      *
497      * @throws PrinterException if the specified service does not support
498      * 2D printing or no longer available.
499      */
setPrintService(PrintService service)500     public void setPrintService(PrintService service)
501         throws PrinterException {
502         if (service == null) {
503             throw new PrinterException("Service cannot be null");
504         } else if (!(service instanceof StreamPrintService) &&
505                    service.getName() == null) {
506             throw new PrinterException("Null PrintService name.");
507         } else {
508             // Check the list of services.  This service may have been
509             // deleted already
510             PrinterState prnState = service.getAttribute(PrinterState.class);
511             if (prnState == PrinterState.STOPPED) {
512                 PrinterStateReasons prnStateReasons =
513                     service.getAttribute(PrinterStateReasons.class);
514                 if ((prnStateReasons != null) &&
515                     (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))
516                 {
517                     throw new PrinterException("PrintService is no longer available.");
518                 }
519             }
520 
521 
522             if (service.isDocFlavorSupported(
523                                              DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&
524                 service.isDocFlavorSupported(
525                                              DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
526                 myService = service;
527             } else {
528                 throw new PrinterException("Not a 2D print service: " + service);
529             }
530         }
531     }
532 
attributeToPageFormat(PrintService service, PrintRequestAttributeSet attSet)533     private PageFormat attributeToPageFormat(PrintService service,
534                                                PrintRequestAttributeSet attSet) {
535         PageFormat page = defaultPage();
536 
537         if (service == null) {
538             return page;
539         }
540 
541         OrientationRequested orient = (OrientationRequested)
542                                       attSet.get(OrientationRequested.class);
543         if (orient == null) {
544             orient = (OrientationRequested)
545                     service.getDefaultAttributeValue(OrientationRequested.class);
546         }
547         if (orient == OrientationRequested.REVERSE_LANDSCAPE) {
548             page.setOrientation(PageFormat.REVERSE_LANDSCAPE);
549         } else if (orient == OrientationRequested.LANDSCAPE) {
550             page.setOrientation(PageFormat.LANDSCAPE);
551         } else {
552             page.setOrientation(PageFormat.PORTRAIT);
553         }
554 
555         Media media = (Media)attSet.get(Media.class);
556         MediaSize size = getMediaSize(media, service, page);
557 
558         Paper paper = new Paper();
559         float[] dim = size.getSize(1); //units == 1 to avoid FP error
560         double w = Math.rint((dim[0]*72.0)/Size2DSyntax.INCH);
561         double h = Math.rint((dim[1]*72.0)/Size2DSyntax.INCH);
562         paper.setSize(w, h);
563         MediaPrintableArea area =
564              (MediaPrintableArea)
565              attSet.get(MediaPrintableArea.class);
566         if (area == null) {
567             area = getDefaultPrintableArea(page, w, h);
568         }
569 
570         double ix, iw, iy, ih;
571         // Should pass in same unit as updatePageAttributes
572         // to avoid rounding off errors.
573         ix = Math.rint(
574                 area.getX(MediaPrintableArea.INCH) * DPI);
575         iy = Math.rint(
576                 area.getY(MediaPrintableArea.INCH) * DPI);
577         iw = Math.rint(
578                 area.getWidth(MediaPrintableArea.INCH) * DPI);
579         ih = Math.rint(
580                 area.getHeight(MediaPrintableArea.INCH) * DPI);
581         paper.setImageableArea(ix, iy, iw, ih);
582         page.setPaper(paper);
583         return page;
584     }
getMediaSize(Media media, PrintService service, PageFormat page)585     protected MediaSize getMediaSize(Media media, PrintService service,
586             PageFormat page) {
587         if (media == null) {
588             media = (Media)service.getDefaultAttributeValue(Media.class);
589         }
590         if (!(media instanceof MediaSizeName)) {
591             media = MediaSizeName.NA_LETTER;
592         }
593         MediaSize size = MediaSize.getMediaSizeForName((MediaSizeName) media);
594         return size != null ? size : MediaSize.NA.LETTER;
595     }
596 
getDefaultPrintableArea(PageFormat page, double w, double h)597     protected MediaPrintableArea getDefaultPrintableArea(PageFormat page,
598             double w, double h) {
599         double ix, iw, iy, ih;
600         if (w >= 72.0 * 6.0) {
601             ix = 72.0;
602             iw = w - 2 * 72.0;
603         } else {
604             ix = w / 6.0;
605             iw = w * 0.75;
606         }
607         if (h >= 72.0 * 6.0) {
608             iy = 72.0;
609             ih = h - 2 * 72.0;
610         } else {
611             iy = h / 6.0;
612             ih = h * 0.75;
613         }
614 
615         return new MediaPrintableArea((float) (ix / DPI), (float) (iy / DPI),
616                 (float) (iw / DPI), (float) (ih / DPI), MediaPrintableArea.INCH);
617     }
618 
updatePageAttributes(PrintService service, PageFormat page)619     protected void updatePageAttributes(PrintService service,
620                                         PageFormat page) {
621         if (this.attributes == null) {
622             this.attributes = new HashPrintRequestAttributeSet();
623         }
624 
625         updateAttributesWithPageFormat(service, page, this.attributes);
626     }
627 
updateAttributesWithPageFormat(PrintService service, PageFormat page, PrintRequestAttributeSet pageAttributes)628     protected void updateAttributesWithPageFormat(PrintService service,
629                                         PageFormat page,
630                                         PrintRequestAttributeSet pageAttributes) {
631         if (service == null || page == null || pageAttributes == null) {
632             return;
633         }
634 
635         float x = (float)Math.rint(
636                          (page.getPaper().getWidth()*Size2DSyntax.INCH)/
637                          (72.0))/(float)Size2DSyntax.INCH;
638         float y = (float)Math.rint(
639                          (page.getPaper().getHeight()*Size2DSyntax.INCH)/
640                          (72.0))/(float)Size2DSyntax.INCH;
641 
642         // We should limit the list where we search the matching
643         // media, this will prevent mapping to wrong media ex. Ledger
644         // can be mapped to B.  Especially useful when creating
645         // custom MediaSize.
646         Media[] mediaList = (Media[])service.getSupportedAttributeValues(
647                                       Media.class, null, null);
648         Media media = null;
649         try {
650             media = CustomMediaSizeName.findMedia(mediaList, x, y,
651                                    Size2DSyntax.INCH);
652         } catch (IllegalArgumentException iae) {
653         }
654         if ((media == null) ||
655              !(service.isAttributeValueSupported(media, null, null))) {
656             media = (Media)service.getDefaultAttributeValue(Media.class);
657         }
658 
659         OrientationRequested orient;
660         switch (page.getOrientation()) {
661         case PageFormat.LANDSCAPE :
662             orient = OrientationRequested.LANDSCAPE;
663             break;
664         case PageFormat.REVERSE_LANDSCAPE:
665             orient = OrientationRequested.REVERSE_LANDSCAPE;
666             break;
667         default:
668             orient = OrientationRequested.PORTRAIT;
669         }
670 
671         if (media != null) {
672             pageAttributes.add(media);
673         }
674         pageAttributes.add(orient);
675 
676         float ix = (float)(page.getPaper().getImageableX()/DPI);
677         float iw = (float)(page.getPaper().getImageableWidth()/DPI);
678         float iy = (float)(page.getPaper().getImageableY()/DPI);
679         float ih = (float)(page.getPaper().getImageableHeight()/DPI);
680 
681         if (ix < 0) ix = 0; if (iy < 0) iy = 0;
682         if (iw <= 0) iw = (float)(page.getPaper().getWidth()/DPI) - (ix*2);
683 
684         // If iw is still negative, it means ix is too large to print
685         // anything inside printable area if we have to leave the same margin
686         // in the right side of paper so we go back to default mpa values
687         if (iw < 0) iw = 0;
688 
689         if (ih <= 0) ih = (float)(page.getPaper().getHeight()/DPI) - (iy*2);
690 
691         // If ih is still negative, it means iy is too large to print
692         // anything inside printable area if we have to leave the same margin
693         // in the bottom side of paper so we go back to default mpa values
694         if (ih < 0) ih = 0;
695         try {
696             pageAttributes.add(new MediaPrintableArea(ix, iy, iw, ih,
697                                                   MediaPrintableArea.INCH));
698         } catch (IllegalArgumentException iae) {
699         }
700     }
701 
702    /**
703      * Display a dialog to the user allowing the modification of a
704      * PageFormat instance.
705      * The {@code page} argument is used to initialize controls
706      * in the page setup dialog.
707      * If the user cancels the dialog, then the method returns the
708      * original {@code page} object unmodified.
709      * If the user okays the dialog then the method returns a new
710      * PageFormat object with the indicated changes.
711      * In either case the original {@code page} object will
712      * not be modified.
713      * @param     page    the default PageFormat presented to the user
714      *                    for modification
715      * @return    the original {@code page} object if the dialog
716      *            is cancelled, or a new PageFormat object containing
717      *            the format indicated by the user if the dialog is
718      *            acknowledged
719      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
720      * returns true.
721      * @see java.awt.GraphicsEnvironment#isHeadless
722      * @since     1.2
723      */
pageDialog(PageFormat page)724     public PageFormat pageDialog(PageFormat page)
725         throws HeadlessException {
726         if (GraphicsEnvironment.isHeadless()) {
727             throw new HeadlessException();
728         }
729 
730         final GraphicsConfiguration gc =
731           GraphicsEnvironment.getLocalGraphicsEnvironment().
732           getDefaultScreenDevice().getDefaultConfiguration();
733 
734         @SuppressWarnings("removal")
735         PrintService service = java.security.AccessController.doPrivileged(
736                                new java.security.PrivilegedAction<PrintService>() {
737                 public PrintService run() {
738                     PrintService service = getPrintService();
739                     if (service == null) {
740                         ServiceDialog.showNoPrintService(gc);
741                         return null;
742                     }
743                     return service;
744                 }
745             });
746 
747         if (service == null) {
748             return page;
749         }
750         updatePageAttributes(service, page);
751 
752         PageFormat newPage = null;
753         DialogTypeSelection dts =
754             (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
755         if (dts == DialogTypeSelection.NATIVE) {
756             // Remove DialogTypeSelection.NATIVE to prevent infinite loop in
757             // RasterPrinterJob.
758             attributes.remove(DialogTypeSelection.class);
759             newPage = pageDialog(attributes);
760             // restore attribute
761             attributes.add(DialogTypeSelection.NATIVE);
762         } else {
763             newPage = pageDialog(attributes);
764         }
765 
766         if (newPage == null) {
767             return page;
768         } else {
769             return newPage;
770         }
771     }
772 
773     /**
774      * return a PageFormat corresponding to the updated attributes,
775      * or null if the user cancelled the dialog.
776      */
777     @SuppressWarnings("deprecation")
pageDialog(final PrintRequestAttributeSet attributes)778     public PageFormat pageDialog(final PrintRequestAttributeSet attributes)
779         throws HeadlessException {
780         if (GraphicsEnvironment.isHeadless()) {
781             throw new HeadlessException();
782         }
783 
784         DialogTypeSelection dlg =
785             (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
786 
787         // Check for native, note that default dialog is COMMON.
788         if (dlg == DialogTypeSelection.NATIVE) {
789             PrintService pservice = getPrintService();
790             PageFormat pageFrmAttrib = attributeToPageFormat(pservice,
791                                                              attributes);
792             setParentWindowID(attributes);
793             PageFormat page = pageDialog(pageFrmAttrib);
794             clearParentWindowID();
795 
796             // If user cancels the dialog, pageDialog() will return the original
797             // page object and as per spec, we should return null in that case.
798             if (page == pageFrmAttrib) {
799                 return null;
800             }
801             updateAttributesWithPageFormat(pservice, page, attributes);
802             return page;
803         }
804 
805         GraphicsConfiguration grCfg = null;
806         Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
807         if (w != null) {
808             grCfg = w.getGraphicsConfiguration();
809         } else {
810             grCfg = GraphicsEnvironment.getLocalGraphicsEnvironment().
811                         getDefaultScreenDevice().getDefaultConfiguration();
812         }
813         final GraphicsConfiguration gc = grCfg;
814 
815         @SuppressWarnings("removal")
816         PrintService service = java.security.AccessController.doPrivileged(
817                                new java.security.PrivilegedAction<PrintService>() {
818                 public PrintService run() {
819                     PrintService service = getPrintService();
820                     if (service == null) {
821                         ServiceDialog.showNoPrintService(gc);
822                         return null;
823                     }
824                     return service;
825                 }
826             });
827 
828         if (service == null) {
829             return null;
830         }
831 
832         // we position the dialog a little beyond the upper-left corner of the window
833         // which is consistent with the NATIVE page dialog
834         Rectangle gcBounds = gc.getBounds();
835         int x = gcBounds.x+50;
836         int y = gcBounds.y+50;
837         ServiceDialog pageDialog;
838         boolean setOnTop = false;
839         if (onTop != null) {
840             attributes.add(onTop);
841             Window owner = onTop.getOwner();
842             if (owner != null) {
843                 w = owner; // use the one specifed by the app
844             } else if (DialogOwnerAccessor.getID(onTop) == 0) {
845                 setOnTop = true;
846             }
847         }
848             pageDialog = new ServiceDialog(gc, x, y, service,
849                                            DocFlavor.SERVICE_FORMATTED.PAGEABLE,
850                                            attributes, w);
851         if (setOnTop) {
852             try {
853                 pageDialog.setAlwaysOnTop(true);
854             } catch (SecurityException e) {
855             }
856         }
857 
858         Rectangle dlgBounds = pageDialog.getBounds();
859 
860         // if portion of dialog is not within the gc boundary
861         if (!gcBounds.contains(dlgBounds)) {
862             // check if dialog exceed window bounds at left or bottom
863             // Then position the dialog by moving it by the amount it exceeds
864             // the window bounds
865             // If it results in dialog moving beyond the window bounds at top/left
866             // then position it at window top/left
867             if (dlgBounds.x + dlgBounds.width > gcBounds.x + gcBounds.width) {
868                 if ((gcBounds.x + gcBounds.width - dlgBounds.width) > gcBounds.x) {
869                     x = (gcBounds.x + gcBounds.width) - dlgBounds.width;
870                 } else {
871                     x = gcBounds.x;
872                 }
873             }
874             if (dlgBounds.y + dlgBounds.height > gcBounds.y + gcBounds.height) {
875                 if ((gcBounds.y + gcBounds.height - dlgBounds.height) > gcBounds.y) {
876                     y = (gcBounds.y + gcBounds.height) - dlgBounds.height;
877                 } else {
878                     y = gcBounds.y;
879                 }
880             }
881             pageDialog.setBounds(x, y, dlgBounds.width, dlgBounds.height);
882         }
883         pageDialog.show();
884 
885         if (pageDialog.getStatus() == ServiceDialog.APPROVE) {
886             PrintRequestAttributeSet newas =
887                 pageDialog.getAttributes();
888             Class<?> amCategory = SunAlternateMedia.class;
889 
890             if (attributes.containsKey(amCategory) &&
891                 !newas.containsKey(amCategory)) {
892                 attributes.remove(amCategory);
893             }
894             attributes.addAll(newas);
895             return attributeToPageFormat(service, attributes);
896         } else {
897             return null;
898         }
899    }
900 
getPageFormatFromAttributes()901     protected PageFormat getPageFormatFromAttributes() {
902         Pageable pageable = null;
903         if (attributes == null || attributes.isEmpty() ||
904             !((pageable = getPageable()) instanceof OpenBook)) {
905             return null;
906         }
907 
908         PageFormat newPf = attributeToPageFormat(
909             getPrintService(), attributes);
910         PageFormat oldPf = null;
911         if ((oldPf = pageable.getPageFormat(0)) != null) {
912             // If orientation, media, imageable area attributes are not in
913             // "attributes" set, then use respective values of the existing
914             // page format "oldPf".
915             if (attributes.get(OrientationRequested.class) == null) {
916                 newPf.setOrientation(oldPf.getOrientation());
917             }
918 
919             Paper newPaper = newPf.getPaper();
920             Paper oldPaper = oldPf.getPaper();
921             boolean oldPaperValWasSet = false;
922             if (attributes.get(MediaSizeName.class) == null) {
923                 newPaper.setSize(oldPaper.getWidth(), oldPaper.getHeight());
924                 oldPaperValWasSet = true;
925             }
926             if (attributes.get(MediaPrintableArea.class) == null) {
927                 newPaper.setImageableArea(
928                     oldPaper.getImageableX(), oldPaper.getImageableY(),
929                     oldPaper.getImageableWidth(),
930                     oldPaper.getImageableHeight());
931                 oldPaperValWasSet = true;
932             }
933             if (oldPaperValWasSet) {
934                 newPf.setPaper(newPaper);
935             }
936         }
937         return newPf;
938     }
939 
940 
941    /**
942      * Presents the user a dialog for changing properties of the
943      * print job interactively.
944      * The services browsable here are determined by the type of
945      * service currently installed.
946      * If the application installed a StreamPrintService on this
947      * PrinterJob, only the available StreamPrintService (factories) are
948      * browsable.
949      *
950      * @param attributes to store changed properties.
951      * @return false if the user cancels the dialog and true otherwise.
952      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
953      * returns true.
954      * @see java.awt.GraphicsEnvironment#isHeadless
955      */
956     @SuppressWarnings("removal")
printDialog(final PrintRequestAttributeSet attributes)957     public boolean printDialog(final PrintRequestAttributeSet attributes)
958         throws HeadlessException {
959         if (GraphicsEnvironment.isHeadless()) {
960             throw new HeadlessException();
961         }
962 
963         DialogTypeSelection dlg =
964             (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
965 
966         // Check for native, note that default dialog is COMMON.
967         if (dlg == DialogTypeSelection.NATIVE) {
968             this.attributes = attributes;
969             try {
970                 debug_println("calling setAttributes in printDialog");
971                 setAttributes(attributes);
972 
973             } catch (PrinterException e) {
974 
975             }
976 
977             setParentWindowID(attributes);
978             boolean ret = printDialog();
979             clearParentWindowID();
980             this.attributes = attributes;
981             return ret;
982 
983         }
984 
985         /* A security check has already been performed in the
986          * java.awt.print.printerJob.getPrinterJob method.
987          * So by the time we get here, it is OK for the current thread
988          * to print either to a file (from a Dialog we control!) or
989          * to a chosen printer.
990          *
991          * We raise privilege when we put up the dialog, to avoid
992          * the "warning applet window" banner.
993          */
994         GraphicsConfiguration grCfg = null;
995         Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
996         if (w != null) {
997             grCfg = w.getGraphicsConfiguration();
998              /* Add DialogOwner attribute to set the owner of this print dialog
999               * only if it is not set already
1000               * (it might be set in java.awt.PrintJob.printDialog)
1001               */
1002             if (attributes.get(DialogOwner.class) == null) {
1003                 attributes.add(new DialogOwner(w));
1004             }
1005         } else {
1006             grCfg = GraphicsEnvironment.getLocalGraphicsEnvironment().
1007                         getDefaultScreenDevice().getDefaultConfiguration();
1008         }
1009         final GraphicsConfiguration gc = grCfg;
1010 
1011         PrintService service = java.security.AccessController.doPrivileged(
1012                                new java.security.PrivilegedAction<PrintService>() {
1013                 public PrintService run() {
1014                     PrintService service = getPrintService();
1015                     if (service == null) {
1016                         ServiceDialog.showNoPrintService(gc);
1017                         return null;
1018                     }
1019                     return service;
1020                 }
1021             });
1022 
1023         if (service == null) {
1024             return false;
1025         }
1026 
1027         PrintService[] services;
1028         StreamPrintServiceFactory[] spsFactories = null;
1029         if (service instanceof StreamPrintService) {
1030             spsFactories = lookupStreamPrintServices(null);
1031             services = new StreamPrintService[spsFactories.length];
1032             for (int i=0; i<spsFactories.length; i++) {
1033                 services[i] = spsFactories[i].getPrintService(null);
1034             }
1035         } else {
1036             services = java.security.AccessController.doPrivileged(
1037                        new java.security.PrivilegedAction<PrintService[]>() {
1038                 public PrintService[] run() {
1039                     PrintService[] services = PrinterJob.lookupPrintServices();
1040                     return services;
1041                 }
1042             });
1043 
1044             if ((services == null) || (services.length == 0)) {
1045                 /*
1046                  * No services but default PrintService exists?
1047                  * Create services using defaultService.
1048                  */
1049                 services = new PrintService[1];
1050                 services[0] = service;
1051             }
1052         }
1053 
1054         // we position the dialog a little beyond the upper-left corner of the window
1055         // which is consistent with the NATIVE print dialog
1056         int x = 50;
1057         int y = 50;
1058         PrintService newService;
1059         // temporarily add an attribute pointing back to this job.
1060         PrinterJobWrapper jobWrapper = new PrinterJobWrapper(this);
1061         attributes.add(jobWrapper);
1062         PageRanges pgRng = (PageRanges)attributes.get(PageRanges.class);
1063         if (pgRng == null && mDocument.getNumberOfPages() > 1) {
1064             attributes.add(new PageRanges(1, mDocument.getNumberOfPages()));
1065         }
1066         try {
1067             newService =
1068             ServiceUI.printDialog(gc, x, y,
1069                                   services, service,
1070                                   DocFlavor.SERVICE_FORMATTED.PAGEABLE,
1071                                   attributes);
1072         } catch (IllegalArgumentException iae) {
1073             newService = ServiceUI.printDialog(gc, x, y,
1074                                   services, services[0],
1075                                   DocFlavor.SERVICE_FORMATTED.PAGEABLE,
1076                                   attributes);
1077         }
1078         attributes.remove(PrinterJobWrapper.class);
1079         attributes.remove(DialogOwner.class);
1080 
1081         if (newService == null) {
1082             return false;
1083         }
1084 
1085         if (!service.equals(newService)) {
1086             try {
1087                 setPrintService(newService);
1088             } catch (PrinterException e) {
1089                 /*
1090                  * The only time it would throw an exception is when
1091                  * newService is no longer available but we should still
1092                  * select this printer.
1093                  */
1094                 myService = newService;
1095             }
1096         }
1097         return true;
1098     }
1099 
1100    /**
1101      * Presents the user a dialog for changing properties of the
1102      * print job interactively.
1103      * @return false if the user cancels the dialog and
1104      *         true otherwise.
1105      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
1106      * returns true.
1107      * @see java.awt.GraphicsEnvironment#isHeadless
1108      */
printDialog()1109     public boolean printDialog() throws HeadlessException {
1110 
1111         if (GraphicsEnvironment.isHeadless()) {
1112             throw new HeadlessException();
1113         }
1114 
1115         PrintRequestAttributeSet attributes =
1116           new HashPrintRequestAttributeSet();
1117         attributes.add(new Copies(getCopies()));
1118         attributes.add(new JobName(getJobName(), null));
1119         boolean doPrint = printDialog(attributes);
1120         if (doPrint) {
1121             JobName jobName = (JobName)attributes.get(JobName.class);
1122             if (jobName != null) {
1123                 setJobName(jobName.getValue());
1124             }
1125             Copies copies = (Copies)attributes.get(Copies.class);
1126             if (copies != null) {
1127                 setCopies(copies.getValue());
1128             }
1129 
1130             Destination dest = (Destination)attributes.get(Destination.class);
1131 
1132             if (dest != null) {
1133                 try {
1134                     mDestType = RasterPrinterJob.FILE;
1135                     mDestination = (new File(dest.getURI())).getPath();
1136                 } catch (Exception e) {
1137                     mDestination = "out.prn";
1138                     PrintService ps = getPrintService();
1139                     if (ps != null) {
1140                         Destination defaultDest = (Destination)ps.
1141                             getDefaultAttributeValue(Destination.class);
1142                         if (defaultDest != null) {
1143                             mDestination = (new File(defaultDest.getURI())).getPath();
1144                         }
1145                     }
1146                 }
1147             } else {
1148                 mDestType = RasterPrinterJob.PRINTER;
1149                 PrintService ps = getPrintService();
1150                 if (ps != null) {
1151                     mDestination = ps.getName();
1152                 }
1153             }
1154         }
1155 
1156         return doPrint;
1157     }
1158 
1159     /**
1160      * The pages in the document to be printed by this PrinterJob
1161      * are drawn by the Printable object 'painter'. The PageFormat
1162      * for each page is the default page format.
1163      * @param painter Called to render each page of the document.
1164      */
setPrintable(Printable painter)1165     public void setPrintable(Printable painter) {
1166         setPageable(new OpenBook(defaultPage(new PageFormat()), painter));
1167     }
1168 
1169     /**
1170      * The pages in the document to be printed by this PrinterJob
1171      * are drawn by the Printable object 'painter'. The PageFormat
1172      * of each page is 'format'.
1173      * @param painter Called to render each page of the document.
1174      * @param format  The size and orientation of each page to
1175      *                be printed.
1176      */
setPrintable(Printable painter, PageFormat format)1177     public void setPrintable(Printable painter, PageFormat format) {
1178         setPageable(new OpenBook(format, painter));
1179         updatePageAttributes(getPrintService(), format);
1180     }
1181 
1182     /**
1183      * The pages in the document to be printed are held by the
1184      * Pageable instance 'document'. 'document' will be queried
1185      * for the number of pages as well as the PageFormat and
1186      * Printable for each page.
1187      * @param document The document to be printed. It may not be null.
1188      * @exception NullPointerException the Pageable passed in was null.
1189      * @see PageFormat
1190      * @see Printable
1191      */
setPageable(Pageable document)1192     public void setPageable(Pageable document) throws NullPointerException {
1193         if (document != null) {
1194             mDocument = document;
1195 
1196         } else {
1197             throw new NullPointerException();
1198         }
1199     }
1200 
initPrinter()1201     protected void initPrinter() {
1202         return;
1203     }
1204 
isSupportedValue(Attribute attrval, PrintRequestAttributeSet attrset)1205     protected boolean isSupportedValue(Attribute attrval,
1206                                      PrintRequestAttributeSet attrset) {
1207         PrintService ps = getPrintService();
1208         return
1209             (attrval != null && ps != null &&
1210              ps.isAttributeValueSupported(attrval,
1211                                           DocFlavor.SERVICE_FORMATTED.PAGEABLE,
1212                                           attrset));
1213     }
1214 
1215     /**
1216      * Set the device resolution.
1217      * Overridden and used only by the postscript code.
1218      * Windows code pulls the information from the attribute set itself.
1219      */
setXYRes(double x, double y)1220     protected void setXYRes(double x, double y) {
1221     }
1222 
1223     /* subclasses may need to pull extra information out of the attribute set
1224      * They can override this method & call super.setAttributes()
1225      */
setAttributes(PrintRequestAttributeSet attributes)1226     protected  void setAttributes(PrintRequestAttributeSet attributes)
1227         throws PrinterException {
1228         /*  reset all values to defaults */
1229         setCollated(false);
1230         sidesAttr = null;
1231         printerResAttr = null;
1232         pageRangesAttr = null;
1233         copiesAttr = 0;
1234         jobNameAttr = null;
1235         userNameAttr = null;
1236         destinationAttr = null;
1237         collateAttReq = false;
1238 
1239         PrintService service = getPrintService();
1240         if (attributes == null  || service == null) {
1241             return;
1242         }
1243 
1244         boolean fidelity = false;
1245         Fidelity attrFidelity = (Fidelity)attributes.get(Fidelity.class);
1246         if (attrFidelity != null && attrFidelity == Fidelity.FIDELITY_TRUE) {
1247             fidelity = true;
1248         }
1249 
1250         if (fidelity == true) {
1251            AttributeSet unsupported =
1252                service.getUnsupportedAttributes(
1253                                          DocFlavor.SERVICE_FORMATTED.PAGEABLE,
1254                                          attributes);
1255            if (unsupported != null) {
1256                throw new PrinterException("Fidelity cannot be satisfied");
1257            }
1258         }
1259 
1260         /*
1261          * Since we have verified supported values if fidelity is true,
1262          * we can either ignore unsupported values, or substitute a
1263          * reasonable alternative
1264          */
1265 
1266         SheetCollate collateAttr =
1267             (SheetCollate)attributes.get(SheetCollate.class);
1268         if (isSupportedValue(collateAttr,  attributes)) {
1269             setCollated(collateAttr == SheetCollate.COLLATED);
1270         }
1271 
1272         sidesAttr = (Sides)attributes.get(Sides.class);
1273         if (!isSupportedValue(sidesAttr,  attributes)) {
1274             sidesAttr = Sides.ONE_SIDED;
1275         }
1276 
1277         printerResAttr = (PrinterResolution)attributes.get(PrinterResolution.class);
1278         if (service.isAttributeCategorySupported(PrinterResolution.class)) {
1279             if (!isSupportedValue(printerResAttr,  attributes)) {
1280                printerResAttr = (PrinterResolution)
1281                    service.getDefaultAttributeValue(PrinterResolution.class);
1282             }
1283             if (printerResAttr != null) {
1284                 double xr =
1285                         printerResAttr.getCrossFeedResolution(ResolutionSyntax.DPI);
1286                 double yr = printerResAttr.getFeedResolution(ResolutionSyntax.DPI);
1287                 setXYRes(xr, yr);
1288             }
1289         }
1290 
1291         pageRangesAttr =  (PageRanges)attributes.get(PageRanges.class);
1292         if (!isSupportedValue(pageRangesAttr, attributes)) {
1293             pageRangesAttr = null;
1294             setPageRange(-1, -1);
1295         } else {
1296             if ((SunPageSelection)attributes.get(SunPageSelection.class)
1297                      == SunPageSelection.RANGE) {
1298                 // get to, from, min, max page ranges
1299                 int[][] range = pageRangesAttr.getMembers();
1300                 // setPageRanges uses 0-based indexing so we subtract 1
1301                 setPageRange(range[0][0] - 1, range[0][1] - 1);
1302             } else {
1303                setPageRange(-1, - 1);
1304             }
1305         }
1306 
1307         Copies copies = (Copies)attributes.get(Copies.class);
1308         if (isSupportedValue(copies,  attributes) ||
1309             (!fidelity && copies != null)) {
1310             copiesAttr = copies.getValue();
1311             setCopies(copiesAttr);
1312         } else {
1313             copiesAttr = getCopies();
1314         }
1315 
1316         Destination destination =
1317             (Destination)attributes.get(Destination.class);
1318 
1319         if (isSupportedValue(destination,  attributes)) {
1320             try {
1321                 // Old code (new File(destination.getURI())).getPath()
1322                 // would generate a "URI is not hierarchical" IAE
1323                 // for "file:out.prn" so we use getSchemeSpecificPart instead
1324                 destinationAttr = "" + new File(destination.getURI().
1325                                                 getSchemeSpecificPart());
1326             } catch (Exception e) { // paranoid exception
1327                 Destination defaultDest = (Destination)service.
1328                     getDefaultAttributeValue(Destination.class);
1329                 if (defaultDest != null) {
1330                     destinationAttr = "" + new File(defaultDest.getURI().
1331                                                 getSchemeSpecificPart());
1332                 }
1333             }
1334         }
1335 
1336         JobSheets jobSheets = (JobSheets)attributes.get(JobSheets.class);
1337         if (jobSheets != null) {
1338             noJobSheet = jobSheets == JobSheets.NONE;
1339         }
1340 
1341         JobName jobName = (JobName)attributes.get(JobName.class);
1342         if (isSupportedValue(jobName,  attributes) ||
1343             (!fidelity && jobName != null)) {
1344             jobNameAttr = jobName.getValue();
1345             setJobName(jobNameAttr);
1346         } else {
1347             jobNameAttr = getJobName();
1348         }
1349 
1350         RequestingUserName userName =
1351             (RequestingUserName)attributes.get(RequestingUserName.class);
1352         if (isSupportedValue(userName,  attributes) ||
1353             (!fidelity && userName != null)) {
1354             userNameAttr = userName.getValue();
1355         } else {
1356             try {
1357                 userNameAttr = getUserName();
1358             } catch (SecurityException e) {
1359                 userNameAttr = "";
1360             }
1361         }
1362 
1363         /* OpenBook is used internally only when app uses Printable.
1364          * This is the case when we use the values from the attribute set.
1365          */
1366         Media media = (Media)attributes.get(Media.class);
1367         OrientationRequested orientReq =
1368            (OrientationRequested)attributes.get(OrientationRequested.class);
1369         MediaPrintableArea mpa =
1370             (MediaPrintableArea)attributes.get(MediaPrintableArea.class);
1371 
1372         if ((orientReq != null || media != null || mpa != null) &&
1373             getPageable() instanceof OpenBook) {
1374 
1375             /* We could almost(!) use PrinterJob.getPageFormat() except
1376              * here we need to start with the PageFormat from the OpenBook :
1377              */
1378             Pageable pageable = getPageable();
1379             Printable printable = pageable.getPrintable(0);
1380             PageFormat pf = (PageFormat)pageable.getPageFormat(0).clone();
1381             Paper paper = pf.getPaper();
1382 
1383             /* If there's a media but no media printable area, we can try
1384              * to retrieve the default value for mpa and use that.
1385              */
1386             if (mpa == null && media != null &&
1387                 service.
1388                 isAttributeCategorySupported(MediaPrintableArea.class)) {
1389                 Object mpaVals = service.
1390                     getSupportedAttributeValues(MediaPrintableArea.class,
1391                                                 null, attributes);
1392                 if (mpaVals instanceof MediaPrintableArea[] &&
1393                     ((MediaPrintableArea[])mpaVals).length > 0) {
1394                     mpa = ((MediaPrintableArea[])mpaVals)[0];
1395                 }
1396             }
1397 
1398             if (isSupportedValue(orientReq, attributes) ||
1399                 (!fidelity && orientReq != null)) {
1400                 int orient;
1401                 if (orientReq.equals(OrientationRequested.REVERSE_LANDSCAPE)) {
1402                     orient = PageFormat.REVERSE_LANDSCAPE;
1403                 } else if (orientReq.equals(OrientationRequested.LANDSCAPE)) {
1404                     orient = PageFormat.LANDSCAPE;
1405                 } else {
1406                     orient = PageFormat.PORTRAIT;
1407                 }
1408                 pf.setOrientation(orient);
1409             }
1410 
1411             if (isSupportedValue(media, attributes) ||
1412                 (!fidelity && media != null)) {
1413                 if (media instanceof MediaSizeName) {
1414                     MediaSizeName msn = (MediaSizeName)media;
1415                     MediaSize msz = MediaSize.getMediaSizeForName(msn);
1416                     if (msz != null) {
1417                         float paperWid =  msz.getX(MediaSize.INCH) * 72.0f;
1418                         float paperHgt =  msz.getY(MediaSize.INCH) * 72.0f;
1419                         paper.setSize(paperWid, paperHgt);
1420                         if (mpa == null) {
1421                             paper.setImageableArea(72.0, 72.0,
1422                                                    paperWid-144.0,
1423                                                    paperHgt-144.0);
1424                         }
1425                     }
1426                 }
1427             }
1428 
1429             if (isSupportedValue(mpa, attributes) ||
1430                 (!fidelity && mpa != null)) {
1431                 float [] printableArea =
1432                     mpa.getPrintableArea(MediaPrintableArea.INCH);
1433                 for (int i=0; i < printableArea.length; i++) {
1434                     printableArea[i] = printableArea[i]*72.0f;
1435                 }
1436                 paper.setImageableArea(printableArea[0], printableArea[1],
1437                                        printableArea[2], printableArea[3]);
1438             }
1439 
1440             pf.setPaper(paper);
1441             pf = validatePage(pf);
1442             setPrintable(printable, pf);
1443         } else {
1444             // for AWT where pageable is not an instance of OpenBook,
1445             // we need to save paper info
1446             this.attributes = attributes;
1447         }
1448 
1449     }
1450 
1451     /*
1452      * Services we don't recognize as built-in services can't be
1453      * implemented as subclasses of PrinterJob, therefore we create
1454      * a DocPrintJob from their service and pass a Doc representing
1455      * the application's printjob
1456      */
1457 // MacOSX - made protected so subclasses can reference it.
spoolToService(PrintService psvc, PrintRequestAttributeSet attributes)1458     protected void spoolToService(PrintService psvc,
1459                                 PrintRequestAttributeSet attributes)
1460         throws PrinterException {
1461 
1462         if (psvc == null) {
1463             throw new PrinterException("No print service found.");
1464         }
1465 
1466         DocPrintJob job = psvc.createPrintJob();
1467         Doc doc = new PageableDoc(getPageable());
1468         if (attributes == null) {
1469             attributes = new HashPrintRequestAttributeSet();
1470             attributes.add(new Copies(getCopies()));
1471             attributes.add(new JobName(getJobName(), null));
1472         }
1473         try {
1474             job.print(doc, attributes);
1475         } catch (PrintException e) {
1476             throw new PrinterException(e.toString());
1477         }
1478     }
1479 
1480     /**
1481      * Prints a set of pages.
1482      * @exception java.awt.print.PrinterException an error in the print system
1483      *                                          caused the job to be aborted
1484      * @see java.awt.print.Book
1485      * @see java.awt.print.Pageable
1486      * @see java.awt.print.Printable
1487      */
print()1488     public void print() throws PrinterException {
1489         print(attributes);
1490     }
1491 
1492     public static boolean debugPrint = false;
debug_println(String str)1493     protected void debug_println(String str) {
1494         if (debugPrint) {
1495             System.out.println("RasterPrinterJob "+str+" "+this);
1496         }
1497     }
1498 
print(PrintRequestAttributeSet attributes)1499     public void print(PrintRequestAttributeSet attributes)
1500         throws PrinterException {
1501 
1502         /*
1503          * In the future PrinterJob will probably always dispatch
1504          * the print job to the PrintService.
1505          * This is how third party 2D Print Services will be invoked
1506          * when applications use the PrinterJob API.
1507          * However the JRE's concrete PrinterJob implementations have
1508          * not yet been re-worked to be implemented as standalone
1509          * services, and are implemented only as subclasses of PrinterJob.
1510          * So here we dispatch only those services we do not recognize
1511          * as implemented through platform subclasses of PrinterJob
1512          * (and this class).
1513          */
1514         PrintService psvc = getPrintService();
1515         debug_println("psvc = "+psvc);
1516         if (psvc == null) {
1517             throw new PrinterException("No print service found.");
1518         }
1519 
1520         // Check the list of services.  This service may have been
1521         // deleted already
1522         PrinterState prnState = psvc.getAttribute(PrinterState.class);
1523         if (prnState == PrinterState.STOPPED) {
1524             PrinterStateReasons prnStateReasons =
1525                     psvc.getAttribute(PrinterStateReasons.class);
1526                 if ((prnStateReasons != null) &&
1527                     (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))
1528                 {
1529                     throw new PrinterException("PrintService is no longer available.");
1530                 }
1531         }
1532 
1533         if ((psvc.getAttribute(PrinterIsAcceptingJobs.class)) ==
1534                          PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {
1535             throw new PrinterException("Printer is not accepting job.");
1536         }
1537 
1538         /*
1539          * Check the default job-sheet value on underlying platform. If IPP
1540          * reports job-sheets=none, then honour that and modify noJobSheet since
1541          * by default, noJobSheet is false which mean jdk will print banner page.
1542          * This is because if "attributes" is null (if user directly calls print()
1543          * without specifying any attributes and without showing printdialog) then
1544          * setAttribute will return without changing noJobSheet value.
1545          * Also, we do this before setAttributes() call so as to allow the user
1546          * to override this via explicitly adding JobSheets attributes to
1547          * PrintRequestAttributeSet while calling print(attributes)
1548          */
1549         JobSheets js = (JobSheets)psvc.getDefaultAttributeValue(JobSheets.class);
1550         if (js != null && js.equals(JobSheets.NONE)) {
1551             noJobSheet = true;
1552         }
1553 
1554         if ((psvc instanceof SunPrinterJobService) &&
1555             ((SunPrinterJobService)psvc).usesClass(getClass())) {
1556             setAttributes(attributes);
1557             // throw exception for invalid destination
1558             if (destinationAttr != null) {
1559                 validateDestination(destinationAttr);
1560             }
1561         } else {
1562             spoolToService(psvc, attributes);
1563             return;
1564         }
1565         /* We need to make sure that the collation and copies
1566          * settings are initialised */
1567         initPrinter();
1568 
1569         int numCollatedCopies = getCollatedCopies();
1570         int numNonCollatedCopies = getNoncollatedCopies();
1571         debug_println("getCollatedCopies()  "+numCollatedCopies
1572               + " getNoncollatedCopies() "+ numNonCollatedCopies);
1573 
1574         /* Get the range of pages we are to print. If the
1575          * last page to print is unknown, then we print to
1576          * the end of the document. Note that firstPage
1577          * and lastPage are 0 based page indices.
1578          */
1579         int numPages = mDocument.getNumberOfPages();
1580         if (numPages == 0) {
1581             return;
1582         }
1583 
1584         int firstPage = getFirstPage();
1585         int lastPage = getLastPage();
1586         if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES){
1587             int totalPages = mDocument.getNumberOfPages();
1588             if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) {
1589                 lastPage = mDocument.getNumberOfPages() - 1;
1590             }
1591         }
1592 
1593         try {
1594             synchronized (this) {
1595                 performingPrinting = true;
1596                 userCancelled = false;
1597             }
1598 
1599             startDoc();
1600             if (isCancelled()) {
1601                 cancelDoc();
1602             }
1603 
1604             // PageRanges can be set even if RANGE is not selected
1605             // so we need to check if it is selected.
1606             boolean rangeIsSelected = true;
1607             if (attributes != null) {
1608                 SunPageSelection pages =
1609                     (SunPageSelection)attributes.get(SunPageSelection.class);
1610                 if ((pages != null) && (pages != SunPageSelection.RANGE)) {
1611                     rangeIsSelected = false;
1612                 }
1613             }
1614 
1615 
1616             debug_println("after startDoc rangeSelected? "+rangeIsSelected
1617                       + " numNonCollatedCopies "+ numNonCollatedCopies);
1618 
1619 
1620             /* Three nested loops iterate over the document. The outer loop
1621              * counts the number of collated copies while the inner loop
1622              * counts the number of nonCollated copies. Normally, one of
1623              * these two loops will only execute once; that is we will
1624              * either print collated copies or noncollated copies. The
1625              * middle loop iterates over the pages.
1626              * If a PageRanges attribute is used, it constrains the pages
1627              * that are imaged. If a platform subclass (though a user dialog)
1628              * requests a page range via setPageRange(). it too can
1629              * constrain the page ranges that are imaged.
1630              * It is expected that only one of these will be used in a
1631              * job but both should be able to co-exist.
1632              */
1633             for(int collated = 0; collated < numCollatedCopies; collated++) {
1634                 for(int i = firstPage, pageResult = Printable.PAGE_EXISTS;
1635                     (i <= lastPage ||
1636                      lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES)
1637                     && pageResult == Printable.PAGE_EXISTS;
1638                     i++)
1639                 {
1640 
1641                     if ((pageRangesAttr != null) && rangeIsSelected ){
1642                         int nexti = pageRangesAttr.next(i);
1643                         if (nexti == -1) {
1644                             break;
1645                         } else if (nexti != i+1) {
1646                             continue;
1647                         }
1648                     }
1649 
1650                     for(int nonCollated = 0;
1651                         nonCollated < numNonCollatedCopies
1652                         && pageResult == Printable.PAGE_EXISTS;
1653                         nonCollated++)
1654                     {
1655                         if (isCancelled()) {
1656                             cancelDoc();
1657                         }
1658                         debug_println("printPage "+i);
1659                         pageResult = printPage(mDocument, i);
1660 
1661                     }
1662                 }
1663             }
1664 
1665             if (isCancelled()) {
1666                 cancelDoc();
1667             }
1668 
1669         } finally {
1670             // reset previousPaper in case this job is invoked again.
1671             previousPaper = null;
1672             synchronized (this) {
1673                 if (performingPrinting) {
1674                     endDoc();
1675                 }
1676                 performingPrinting = false;
1677                 notify();
1678             }
1679         }
1680     }
1681 
validateDestination(String dest)1682     protected void validateDestination(String dest) throws PrinterException {
1683         if (dest == null) {
1684             return;
1685         }
1686         // dest is null for Destination(new URI(""))
1687         // because isAttributeValueSupported returns false in setAttributes
1688 
1689         // Destination(new URI(" ")) throws URISyntaxException
1690         File f = new File(dest);
1691         try {
1692             // check if this is a new file and if filename chars are valid
1693             if (f.createNewFile()) {
1694                 f.delete();
1695             }
1696         } catch (IOException ioe) {
1697             throw new PrinterException("Cannot write to file:"+
1698                                        dest);
1699         } catch (SecurityException se) {
1700             //There is already file read/write access so at this point
1701             // only delete access is denied.  Just ignore it because in
1702             // most cases the file created in createNewFile gets overwritten
1703             // anyway.
1704         }
1705 
1706         File pFile = f.getParentFile();
1707         if ((f.exists() &&
1708              (!f.isFile() || !f.canWrite())) ||
1709             ((pFile != null) &&
1710              (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
1711             if (f.exists()) {
1712                 f.delete();
1713             }
1714             throw new PrinterException("Cannot write to file:"+
1715                                        dest);
1716         }
1717     }
1718 
1719     /**
1720      * updates a Paper object to reflect the current printer's selected
1721      * paper size and imageable area for that paper size.
1722      * Default implementation copies settings from the original, applies
1723      * applies some validity checks, changes them only if they are
1724      * clearly unreasonable, then sets them into the new Paper.
1725      * Subclasses are expected to override this method to make more
1726      * informed decisons.
1727      */
validatePaper(Paper origPaper, Paper newPaper)1728     protected void validatePaper(Paper origPaper, Paper newPaper) {
1729         if (origPaper == null || newPaper == null) {
1730             return;
1731         } else {
1732             double wid = origPaper.getWidth();
1733             double hgt = origPaper.getHeight();
1734             double ix = origPaper.getImageableX();
1735             double iy = origPaper.getImageableY();
1736             double iw = origPaper.getImageableWidth();
1737             double ih = origPaper.getImageableHeight();
1738 
1739             /* Assume any +ve values are legal. Overall paper dimensions
1740              * take precedence. Make sure imageable area fits on the paper.
1741              */
1742             Paper defaultPaper = new Paper();
1743             wid = ((wid > 0.0) ? wid : defaultPaper.getWidth());
1744             hgt = ((hgt > 0.0) ? hgt : defaultPaper.getHeight());
1745             ix = ((ix > 0.0) ? ix : defaultPaper.getImageableX());
1746             iy = ((iy > 0.0) ? iy : defaultPaper.getImageableY());
1747             iw = ((iw > 0.0) ? iw : defaultPaper.getImageableWidth());
1748             ih = ((ih > 0.0) ? ih : defaultPaper.getImageableHeight());
1749             /* full width/height is not likely to be imageable, but since we
1750              * don't know the limits we have to allow it
1751              */
1752             if (iw > wid) {
1753                 iw = wid;
1754             }
1755             if (ih > hgt) {
1756                 ih = hgt;
1757             }
1758             if ((ix + iw) > wid) {
1759                 ix = wid - iw;
1760             }
1761             if ((iy + ih) > hgt) {
1762                 iy = hgt - ih;
1763             }
1764             newPaper.setSize(wid, hgt);
1765             newPaper.setImageableArea(ix, iy, iw, ih);
1766         }
1767     }
1768 
1769     /**
1770      * The passed in PageFormat will be copied and altered to describe
1771      * the default page size and orientation of the PrinterJob's
1772      * current printer.
1773      * Platform subclasses which can access the actual default paper size
1774      * for a printer may override this method.
1775      */
defaultPage(PageFormat page)1776     public PageFormat defaultPage(PageFormat page) {
1777         PageFormat newPage = (PageFormat)page.clone();
1778         newPage.setOrientation(PageFormat.PORTRAIT);
1779         Paper newPaper = new Paper();
1780         double ptsPerInch = 72.0;
1781         double w, h;
1782         Media media = null;
1783 
1784         PrintService service = getPrintService();
1785         if (service != null) {
1786             MediaSize size;
1787             media =
1788                 (Media)service.getDefaultAttributeValue(Media.class);
1789 
1790             if (media instanceof MediaSizeName &&
1791                ((size = MediaSize.getMediaSizeForName((MediaSizeName)media)) !=
1792                 null)) {
1793                 w =  size.getX(MediaSize.INCH) * ptsPerInch;
1794                 h =  size.getY(MediaSize.INCH) * ptsPerInch;
1795                 newPaper.setSize(w, h);
1796                 newPaper.setImageableArea(ptsPerInch, ptsPerInch,
1797                                           w - 2.0*ptsPerInch,
1798                                           h - 2.0*ptsPerInch);
1799                 newPage.setPaper(newPaper);
1800                 return newPage;
1801 
1802             }
1803         }
1804 
1805         /* Default to A4 paper outside North America.
1806          */
1807         String defaultCountry = Locale.getDefault().getCountry();
1808         if (!Locale.getDefault().equals(Locale.ENGLISH) && // ie "C"
1809             defaultCountry != null &&
1810             !defaultCountry.equals(Locale.US.getCountry()) &&
1811             !defaultCountry.equals(Locale.CANADA.getCountry())) {
1812 
1813             double mmPerInch = 25.4;
1814             w = Math.rint((210.0*ptsPerInch)/mmPerInch);
1815             h = Math.rint((297.0*ptsPerInch)/mmPerInch);
1816             newPaper.setSize(w, h);
1817             newPaper.setImageableArea(ptsPerInch, ptsPerInch,
1818                                       w - 2.0*ptsPerInch,
1819                                       h - 2.0*ptsPerInch);
1820         }
1821 
1822         newPage.setPaper(newPaper);
1823 
1824         return newPage;
1825     }
1826 
1827     /**
1828      * The passed in PageFormat is cloned and altered to be usable on
1829      * the PrinterJob's current printer.
1830      */
validatePage(PageFormat page)1831     public PageFormat validatePage(PageFormat page) {
1832         PageFormat newPage = (PageFormat)page.clone();
1833         Paper newPaper = new Paper();
1834         validatePaper(newPage.getPaper(), newPaper);
1835         newPage.setPaper(newPaper);
1836 
1837         return newPage;
1838     }
1839 
1840     /**
1841      * Set the number of copies to be printed.
1842      */
setCopies(int copies)1843     public void setCopies(int copies) {
1844         mNumCopies = copies;
1845     }
1846 
1847     /**
1848      * Get the number of copies to be printed.
1849      */
getCopies()1850     public int getCopies() {
1851         return mNumCopies;
1852     }
1853 
1854    /* Used when executing a print job where an attribute set may
1855      * over ride API values.
1856      */
getCopiesInt()1857     protected int getCopiesInt() {
1858         return (copiesAttr > 0) ? copiesAttr : getCopies();
1859     }
1860 
1861     /**
1862      * Get the name of the printing user.
1863      * The caller must have security permission to read system properties.
1864      */
getUserName()1865     public String getUserName() {
1866         return System.getProperty("user.name");
1867     }
1868 
1869    /* Used when executing a print job where an attribute set may
1870      * over ride API values.
1871      */
getUserNameInt()1872     protected String getUserNameInt() {
1873         if  (userNameAttr != null) {
1874             return userNameAttr;
1875         } else {
1876             try {
1877                 return  getUserName();
1878             } catch (SecurityException e) {
1879                 return "";
1880             }
1881         }
1882     }
1883 
1884     /**
1885      * Set the name of the document to be printed.
1886      * The document name can not be null.
1887      */
setJobName(String jobName)1888     public void setJobName(String jobName) {
1889         if (jobName != null) {
1890             mDocName = jobName;
1891         } else {
1892             throw new NullPointerException();
1893         }
1894     }
1895 
1896     /**
1897      * Get the name of the document to be printed.
1898      */
getJobName()1899     public String getJobName() {
1900         return mDocName;
1901     }
1902 
1903     /* Used when executing a print job where an attribute set may
1904      * over ride API values.
1905      */
getJobNameInt()1906     protected String getJobNameInt() {
1907         return (jobNameAttr != null) ? jobNameAttr : getJobName();
1908     }
1909 
1910     /**
1911      * Set the range of pages from a Book to be printed.
1912      * Both 'firstPage' and 'lastPage' are zero based
1913      * page indices. If either parameter is less than
1914      * zero then the page range is set to be from the
1915      * first page to the last.
1916      */
setPageRange(int firstPage, int lastPage)1917     protected void setPageRange(int firstPage, int lastPage) {
1918         if(firstPage >= 0 && lastPage >= 0) {
1919             mFirstPage = firstPage;
1920             mLastPage = lastPage;
1921             if(mLastPage < mFirstPage) mLastPage = mFirstPage;
1922         } else {
1923             mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
1924             mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
1925         }
1926     }
1927 
1928     /**
1929      * Return the zero based index of the first page to
1930      * be printed in this job.
1931      */
getFirstPage()1932     protected int getFirstPage() {
1933         return mFirstPage == Book.UNKNOWN_NUMBER_OF_PAGES ? 0 : mFirstPage;
1934     }
1935 
1936     /**
1937      * Return the zero based index of the last page to
1938      * be printed in this job.
1939      */
getLastPage()1940     protected int getLastPage() {
1941         return mLastPage;
1942     }
1943 
1944     /**
1945      * Set whether copies should be collated or not.
1946      * Two collated copies of a three page document
1947      * print in this order: 1, 2, 3, 1, 2, 3 while
1948      * uncollated copies print in this order:
1949      * 1, 1, 2, 2, 3, 3.
1950      * This is set when request is using an attribute set.
1951      */
setCollated(boolean collate)1952     protected void setCollated(boolean collate) {
1953         mCollate = collate;
1954         collateAttReq = true;
1955     }
1956 
1957     /**
1958      * Return true if collated copies will be printed as determined
1959      * in an attribute set.
1960      */
isCollated()1961     protected boolean isCollated() {
1962             return mCollate;
1963     }
1964 
getSelectAttrib()1965     protected final int getSelectAttrib() {
1966         if (attributes != null) {
1967             SunPageSelection pages =
1968                 (SunPageSelection)attributes.get(SunPageSelection.class);
1969             if (pages == SunPageSelection.RANGE) {
1970                 return PD_PAGENUMS;
1971             } else if (pages == SunPageSelection.SELECTION) {
1972                 return PD_SELECTION;
1973             } else if (pages ==  SunPageSelection.ALL) {
1974                 return PD_ALLPAGES;
1975             }
1976         }
1977         return PD_NOSELECTION;
1978     }
1979 
1980     //returns 1-based index for "From" page
getFromPageAttrib()1981     protected final int getFromPageAttrib() {
1982         if (attributes != null) {
1983             PageRanges pageRangesAttr =
1984                 (PageRanges)attributes.get(PageRanges.class);
1985             if (pageRangesAttr != null) {
1986                 int[][] range = pageRangesAttr.getMembers();
1987                 return range[0][0];
1988             }
1989         }
1990         return getMinPageAttrib();
1991     }
1992 
1993     //returns 1-based index for "To" page
getToPageAttrib()1994     protected final int getToPageAttrib() {
1995         if (attributes != null) {
1996             PageRanges pageRangesAttr =
1997                 (PageRanges)attributes.get(PageRanges.class);
1998             if (pageRangesAttr != null) {
1999                 int[][] range = pageRangesAttr.getMembers();
2000                 return range[range.length-1][1];
2001             }
2002         }
2003         return getMaxPageAttrib();
2004     }
2005 
getMinPageAttrib()2006     protected final int getMinPageAttrib() {
2007         if (attributes != null) {
2008             SunMinMaxPage s =
2009                 (SunMinMaxPage)attributes.get(SunMinMaxPage.class);
2010             if (s != null) {
2011                 return s.getMin();
2012             }
2013         }
2014         return 1;
2015     }
2016 
getMaxPageAttrib()2017     protected final int getMaxPageAttrib() {
2018         if (attributes != null) {
2019             SunMinMaxPage s =
2020                 (SunMinMaxPage)attributes.get(SunMinMaxPage.class);
2021             if (s != null) {
2022                 return s.getMax();
2023             }
2024         }
2025 
2026         Pageable pageable = getPageable();
2027         if (pageable != null) {
2028             int numPages = pageable.getNumberOfPages();
2029             if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) {
2030                 numPages = MAX_UNKNOWN_PAGES;
2031             }
2032             return  ((numPages == 0) ? 1 : numPages);
2033         }
2034 
2035         return Integer.MAX_VALUE;
2036     }
2037     /**
2038      * Called by the print() method at the start of
2039      * a print job.
2040      */
startDoc()2041     protected abstract void startDoc() throws PrinterException;
2042 
2043     /**
2044      * Called by the print() method at the end of
2045      * a print job.
2046      */
endDoc()2047     protected abstract void endDoc() throws PrinterException;
2048 
2049     /* Called by cancelDoc */
abortDoc()2050     protected abstract void abortDoc();
2051 
2052 // MacOSX - made protected so subclasses can reference it.
cancelDoc()2053     protected void cancelDoc() throws PrinterAbortException {
2054         abortDoc();
2055         synchronized (this) {
2056             userCancelled = false;
2057             performingPrinting = false;
2058             notify();
2059         }
2060         throw new PrinterAbortException();
2061     }
2062 
2063     /**
2064      * Returns how many times the entire book should
2065      * be printed by the PrintJob. If the printer
2066      * itself supports collation then this method
2067      * should return 1 indicating that the entire
2068      * book need only be printed once and the copies
2069      * will be collated and made in the printer.
2070      */
getCollatedCopies()2071     protected int getCollatedCopies() {
2072         return isCollated() ? getCopiesInt() : 1;
2073     }
2074 
2075     /**
2076      * Returns how many times each page in the book
2077      * should be consecutively printed by PrintJob.
2078      * If the printer makes copies itself then this
2079      * method should return 1.
2080      */
getNoncollatedCopies()2081     protected int getNoncollatedCopies() {
2082         return isCollated() ? 1 : getCopiesInt();
2083     }
2084 
2085 
2086     /* The printer graphics config is cached on the job, so that it can
2087      * be created once, and updated only as needed (for now only to change
2088      * the bounds if when using a Pageable the page sizes changes).
2089      */
2090 
2091     private int deviceWidth, deviceHeight;
2092     private AffineTransform defaultDeviceTransform;
2093     private PrinterGraphicsConfig pgConfig;
2094 
setGraphicsConfigInfo(AffineTransform at, double pw, double ph)2095     synchronized void setGraphicsConfigInfo(AffineTransform at,
2096                                             double pw, double ph) {
2097         Point2D.Double pt = new Point2D.Double(pw, ph);
2098         at.transform(pt, pt);
2099 
2100         if (pgConfig == null ||
2101             defaultDeviceTransform == null ||
2102             !at.equals(defaultDeviceTransform) ||
2103             deviceWidth != (int)pt.getX() ||
2104             deviceHeight != (int)pt.getY()) {
2105 
2106                 deviceWidth = (int)pt.getX();
2107                 deviceHeight = (int)pt.getY();
2108                 defaultDeviceTransform = at;
2109                 pgConfig = null;
2110         }
2111     }
2112 
getPrinterGraphicsConfig()2113     synchronized PrinterGraphicsConfig getPrinterGraphicsConfig() {
2114         if (pgConfig != null) {
2115             return pgConfig;
2116         }
2117         String deviceID = "Printer Device";
2118         PrintService service = getPrintService();
2119         if (service != null) {
2120             deviceID = service.toString();
2121         }
2122         pgConfig = new PrinterGraphicsConfig(deviceID,
2123                                              defaultDeviceTransform,
2124                                              deviceWidth, deviceHeight);
2125         return pgConfig;
2126     }
2127 
2128     /**
2129      * Print a page from the provided document.
2130      * @return int Printable.PAGE_EXISTS if the page existed and was drawn and
2131      *             Printable.NO_SUCH_PAGE if the page did not exist.
2132      * @see java.awt.print.Printable
2133      */
printPage(Pageable document, int pageIndex)2134     protected int printPage(Pageable document, int pageIndex)
2135         throws PrinterException
2136     {
2137         PageFormat page;
2138         PageFormat origPage;
2139         Printable painter;
2140         try {
2141             origPage = document.getPageFormat(pageIndex);
2142             page = (PageFormat)origPage.clone();
2143             painter = document.getPrintable(pageIndex);
2144         } catch (Exception e) {
2145             PrinterException pe =
2146                     new PrinterException("Error getting page or printable.[ " +
2147                                           e +" ]");
2148             pe.initCause(e);
2149             throw pe;
2150         }
2151 
2152         /* Get the imageable area from Paper instead of PageFormat
2153          * because we do not want it adjusted by the page orientation.
2154          */
2155         Paper paper = page.getPaper();
2156         // if non-portrait and 270 degree landscape rotation
2157         if (page.getOrientation() != PageFormat.PORTRAIT &&
2158             landscapeRotates270) {
2159 
2160             double left = paper.getImageableX();
2161             double top = paper.getImageableY();
2162             double width = paper.getImageableWidth();
2163             double height = paper.getImageableHeight();
2164             paper.setImageableArea(paper.getWidth()-left-width,
2165                                    paper.getHeight()-top-height,
2166                                    width, height);
2167             page.setPaper(paper);
2168             if (page.getOrientation() == PageFormat.LANDSCAPE) {
2169                 page.setOrientation(PageFormat.REVERSE_LANDSCAPE);
2170             } else {
2171                 page.setOrientation(PageFormat.LANDSCAPE);
2172             }
2173         }
2174 
2175         double xScale = getXRes() / 72.0;
2176         double yScale = getYRes() / 72.0;
2177 
2178         /* The deviceArea is the imageable area in the printer's
2179          * resolution.
2180          */
2181         Rectangle2D deviceArea =
2182             new Rectangle2D.Double(paper.getImageableX() * xScale,
2183                                    paper.getImageableY() * yScale,
2184                                    paper.getImageableWidth() * xScale,
2185                                    paper.getImageableHeight() * yScale);
2186 
2187         /* Build and hold on to a uniform transform so that
2188          * we can get back to device space at the beginning
2189          * of each band.
2190          */
2191         AffineTransform uniformTransform = new AffineTransform();
2192 
2193         /* The scale transform is used to switch from the
2194          * device space to the user's 72 dpi space.
2195          */
2196         AffineTransform scaleTransform = new AffineTransform();
2197         scaleTransform.scale(xScale, yScale);
2198 
2199         /* bandwidth is multiple of 4 as the data is used in a win32 DIB and
2200          * some drivers behave badly if scanlines aren't multiples of 4 bytes.
2201          */
2202         int bandWidth = (int) deviceArea.getWidth();
2203         if (bandWidth % 4 != 0) {
2204             bandWidth += (4 - (bandWidth % 4));
2205         }
2206         if (bandWidth <= 0) {
2207             throw new PrinterException("Paper's imageable width is too small.");
2208         }
2209 
2210         int deviceAreaHeight = (int)deviceArea.getHeight();
2211         if (deviceAreaHeight <= 0) {
2212             throw new PrinterException("Paper's imageable height is too small.");
2213         }
2214 
2215         /* Figure out the number of lines that will fit into
2216          * our maximum band size. The hard coded 3 reflects the
2217          * fact that we can only create 24 bit per pixel 3 byte BGR
2218          * BufferedImages. FIX.
2219          */
2220         int bandHeight = (MAX_BAND_SIZE / bandWidth / 3);
2221 
2222         int deviceLeft = (int)Math.rint(paper.getImageableX() * xScale);
2223         int deviceTop  = (int)Math.rint(paper.getImageableY() * yScale);
2224 
2225         /* The device transform is used to move the band down
2226          * the page using translates. Normally this is all it
2227          * would do, but since, when printing, the Window's
2228          * DIB format wants the last line to be first (lowest) in
2229          * memory, the deviceTransform moves the origin to the
2230          * bottom of the band and flips the origin. This way the
2231          * app prints upside down into the band which is the DIB
2232          * format.
2233          */
2234         AffineTransform deviceTransform = new AffineTransform();
2235         deviceTransform.translate(-deviceLeft, deviceTop);
2236         deviceTransform.translate(0, bandHeight);
2237         deviceTransform.scale(1, -1);
2238 
2239         /* Create a BufferedImage to hold the band. We set the clip
2240          * of the band to be tight around the bits so that the
2241          * application can use it to figure what part of the
2242          * page needs to be drawn. The clip is never altered in
2243          * this method, but we do translate the band's coordinate
2244          * system so that the app will see the clip moving down the
2245          * page though it s always around the same set of pixels.
2246          */
2247         BufferedImage pBand = new BufferedImage(1, 1,
2248                                                 BufferedImage.TYPE_3BYTE_BGR);
2249 
2250         /* Have the app draw into a PeekGraphics object so we can
2251          * learn something about the needs of the print job.
2252          */
2253 
2254         PeekGraphics peekGraphics = createPeekGraphics(pBand.createGraphics(),
2255                                                        this);
2256 
2257         Rectangle2D.Double pageFormatArea =
2258             new Rectangle2D.Double(page.getImageableX(),
2259                                    page.getImageableY(),
2260                                    page.getImageableWidth(),
2261                                    page.getImageableHeight());
2262         peekGraphics.transform(scaleTransform);
2263         peekGraphics.translate(-getPhysicalPrintableX(paper) / xScale,
2264                                -getPhysicalPrintableY(paper) / yScale);
2265         peekGraphics.transform(new AffineTransform(page.getMatrix()));
2266         initPrinterGraphics(peekGraphics, pageFormatArea);
2267         AffineTransform pgAt = peekGraphics.getTransform();
2268 
2269         /* Update the information used to return a GraphicsConfiguration
2270          * for this printer device. It needs to be updated per page as
2271          * not all pages in a job may be the same size (different bounds)
2272          * The transform is the scaling transform as this corresponds to
2273          * the default transform for the device. The width and height are
2274          * those of the paper, not the page format, as we want to describe
2275          * the bounds of the device in its natural coordinate system of
2276          * device coordinate whereas a page format may be in a rotated context.
2277          */
2278         setGraphicsConfigInfo(scaleTransform,
2279                               paper.getWidth(), paper.getHeight());
2280         int pageResult = painter.print(peekGraphics, origPage, pageIndex);
2281         debug_println("pageResult "+pageResult);
2282         if (pageResult == Printable.PAGE_EXISTS) {
2283             debug_println("startPage "+pageIndex);
2284 
2285             /* We need to check if the paper size is changed.
2286              * Note that it is not sufficient to ask for the pageformat
2287              * of "pageIndex-1", since PageRanges mean that pages can be
2288              * skipped. So we have to look at the actual last paper size used.
2289              */
2290             Paper thisPaper = page.getPaper();
2291             boolean paperChanged =
2292                 previousPaper == null ||
2293                 thisPaper.getWidth() != previousPaper.getWidth() ||
2294                 thisPaper.getHeight() != previousPaper.getHeight();
2295             previousPaper = thisPaper;
2296 
2297             startPage(page, painter, pageIndex, paperChanged);
2298             Graphics2D pathGraphics = createPathGraphics(peekGraphics, this,
2299                                                          painter, page,
2300                                                          pageIndex);
2301 
2302             /* If we can convert the page directly to the
2303              * underlying graphics system then we do not
2304              * need to rasterize. We also may not need to
2305              * create the 'band' if all the pages can take
2306              * this path.
2307              */
2308             if (pathGraphics != null) {
2309                 pathGraphics.transform(scaleTransform);
2310                 // user (0,0) should be origin of page, not imageable area
2311                 pathGraphics.translate(-getPhysicalPrintableX(paper) / xScale,
2312                                        -getPhysicalPrintableY(paper) / yScale);
2313                 pathGraphics.transform(new AffineTransform(page.getMatrix()));
2314                 initPrinterGraphics(pathGraphics, pageFormatArea);
2315 
2316                 redrawList.clear();
2317 
2318                 AffineTransform initialTx = pathGraphics.getTransform();
2319 
2320                 painter.print(pathGraphics, origPage, pageIndex);
2321 
2322                 for (int i=0;i<redrawList.size();i++) {
2323                    GraphicsState gstate = redrawList.get(i);
2324                    pathGraphics.setTransform(initialTx);
2325                    ((PathGraphics)pathGraphics).redrawRegion(
2326                                                          gstate.region,
2327                                                          gstate.sx,
2328                                                          gstate.sy,
2329                                                          gstate.theClip,
2330                                                          gstate.theTransform);
2331                 }
2332 
2333             /* This is the banded-raster printing loop.
2334              * It should be moved into its own method.
2335              */
2336             } else {
2337                 BufferedImage band = cachedBand;
2338                 if (cachedBand == null ||
2339                     bandWidth != cachedBandWidth ||
2340                     bandHeight != cachedBandHeight) {
2341                     band = new BufferedImage(bandWidth, bandHeight,
2342                                              BufferedImage.TYPE_3BYTE_BGR);
2343                     cachedBand = band;
2344                     cachedBandWidth = bandWidth;
2345                     cachedBandHeight = bandHeight;
2346                 }
2347                 Graphics2D bandGraphics = band.createGraphics();
2348 
2349                 Rectangle2D.Double clipArea =
2350                     new Rectangle2D.Double(0, 0, bandWidth, bandHeight);
2351 
2352                 initPrinterGraphics(bandGraphics, clipArea);
2353 
2354                 ProxyGraphics2D painterGraphics =
2355                     new ProxyGraphics2D(bandGraphics, this);
2356 
2357                 Graphics2D clearGraphics = band.createGraphics();
2358                 clearGraphics.setColor(Color.white);
2359 
2360                 /* We need the actual bits of the BufferedImage to send to
2361                  * the native Window's code. 'data' points to the actual
2362                  * pixels. Right now these are in ARGB format with 8 bits
2363                  * per component. We need to use a monochrome BufferedImage
2364                  * for monochrome printers when this is supported by
2365                  * BufferedImage. FIX
2366                  */
2367                 ByteInterleavedRaster tile = (ByteInterleavedRaster)band.getRaster();
2368                 byte[] data = tile.getDataStorage();
2369 
2370                 /* Loop over the page moving our band down the page,
2371                  * calling the app to render the band, and then send the band
2372                  * to the printer.
2373                  */
2374                 int deviceBottom = deviceTop + deviceAreaHeight;
2375 
2376                 /* device's printable x,y is really addressable origin
2377                  * we address relative to media origin so when we print a
2378                  * band we need to adjust for the different methods of
2379                  * addressing it.
2380                  */
2381                 int deviceAddressableX = (int)getPhysicalPrintableX(paper);
2382                 int deviceAddressableY = (int)getPhysicalPrintableY(paper);
2383 
2384                 for (int bandTop = 0; bandTop <= deviceAreaHeight;
2385                      bandTop += bandHeight)
2386                 {
2387 
2388                     /* Put the band back into device space and
2389                      * erase the contents of the band.
2390                      */
2391                     clearGraphics.fillRect(0, 0, bandWidth, bandHeight);
2392 
2393                     /* Put the band into the correct location on the
2394                      * page. Once the band is moved we translate the
2395                      * device transform so that the band will move down
2396                      * the page on the next iteration of the loop.
2397                      */
2398                     bandGraphics.setTransform(uniformTransform);
2399                     bandGraphics.transform(deviceTransform);
2400                     deviceTransform.translate(0, -bandHeight);
2401 
2402                     /* Switch the band from device space to user,
2403                      * 72 dpi, space.
2404                      */
2405                     bandGraphics.transform(scaleTransform);
2406                     bandGraphics.transform(new AffineTransform(page.getMatrix()));
2407 
2408                     Rectangle clip = bandGraphics.getClipBounds();
2409                     clip = pgAt.createTransformedShape(clip).getBounds();
2410 
2411                     if ((clip == null) || peekGraphics.hitsDrawingArea(clip) &&
2412                         (bandWidth > 0 && bandHeight > 0)) {
2413 
2414                         /* if the client has specified an imageable X or Y
2415                          * which is off than the physically addressable
2416                          * area of the page, then we need to adjust for that
2417                          * here so that we pass only non -ve band coordinates
2418                          * We also need to translate by the adjusted amount
2419                          * so that printing appears in the correct place.
2420                          */
2421                         int bandX = deviceLeft - deviceAddressableX;
2422                         if (bandX < 0) {
2423                             bandGraphics.translate(bandX/xScale,0);
2424                             bandX = 0;
2425                         }
2426                         int bandY = deviceTop + bandTop - deviceAddressableY;
2427                         if (bandY < 0) {
2428                             bandGraphics.translate(0,bandY/yScale);
2429                             bandY = 0;
2430                         }
2431                         /* Have the app's painter image into the band
2432                          * and then send the band to the printer.
2433                          */
2434                         painterGraphics.setDelegate((Graphics2D) bandGraphics.create());
2435                         painter.print(painterGraphics, origPage, pageIndex);
2436                         painterGraphics.dispose();
2437                         printBand(data, bandX, bandY, bandWidth, bandHeight);
2438                     }
2439                 }
2440 
2441                 clearGraphics.dispose();
2442                 bandGraphics.dispose();
2443 
2444             }
2445             debug_println("calling endPage "+pageIndex);
2446             endPage(page, painter, pageIndex);
2447         }
2448 
2449         return pageResult;
2450     }
2451 
2452     /**
2453      * If a print job is in progress, print() has been
2454      * called but has not returned, then this signals
2455      * that the job should be cancelled and the next
2456      * chance. If there is no print job in progress then
2457      * this call does nothing.
2458      */
cancel()2459     public void cancel() {
2460         synchronized (this) {
2461             if (performingPrinting) {
2462                 userCancelled = true;
2463             }
2464             notify();
2465         }
2466     }
2467 
2468     /**
2469      * Returns true is a print job is ongoing but will
2470      * be cancelled and the next opportunity. false is
2471      * returned otherwise.
2472      */
isCancelled()2473     public boolean isCancelled() {
2474 
2475         boolean cancelled = false;
2476 
2477         synchronized (this) {
2478             cancelled = (performingPrinting && userCancelled);
2479             notify();
2480         }
2481 
2482         return cancelled;
2483     }
2484 
2485     /**
2486      * Return the Pageable describing the pages to be printed.
2487      */
getPageable()2488     protected Pageable getPageable() {
2489         return mDocument;
2490     }
2491 
2492     /**
2493      * Examine the metrics captured by the
2494      * {@code PeekGraphics} instance and
2495      * if capable of directly converting this
2496      * print job to the printer's control language
2497      * or the native OS's graphics primitives, then
2498      * return a {@code PathGraphics} to perform
2499      * that conversion. If there is not an object
2500      * capable of the conversion then return
2501      * {@code null}. Returning {@code null}
2502      * causes the print job to be rasterized.
2503      */
createPathGraphics(PeekGraphics graphics, PrinterJob printerJob, Printable painter, PageFormat pageFormat, int pageIndex)2504     protected Graphics2D createPathGraphics(PeekGraphics graphics,
2505                                             PrinterJob printerJob,
2506                                             Printable painter,
2507                                             PageFormat pageFormat,
2508                                             int pageIndex) {
2509 
2510         return null;
2511     }
2512 
2513     /**
2514      * Create and return an object that will
2515      * gather and hold metrics about the print
2516      * job. This method is passed a {@code Graphics2D}
2517      * object that can be used as a proxy for the
2518      * object gathering the print job matrics. The
2519      * method is also supplied with the instance
2520      * controlling the print job, {@code printerJob}.
2521      */
createPeekGraphics(Graphics2D graphics, PrinterJob printerJob)2522     protected PeekGraphics createPeekGraphics(Graphics2D graphics,
2523                                               PrinterJob printerJob) {
2524 
2525         return new PeekGraphics(graphics, printerJob);
2526     }
2527 
2528     /**
2529      * Configure the passed in Graphics2D so that
2530      * is contains the defined initial settings
2531      * for a print job. These settings are:
2532      *      color:  black.
2533      *      clip:   <as passed in>
2534      */
2535 // MacOSX - made protected so subclasses can reference it.
initPrinterGraphics(Graphics2D g, Rectangle2D clip)2536     protected void initPrinterGraphics(Graphics2D g, Rectangle2D clip) {
2537 
2538         g.setClip(clip);
2539         g.setPaint(Color.black);
2540     }
2541 
2542 
2543    /**
2544     * User dialogs should disable "File" buttons if this returns false.
2545     *
2546     */
checkAllowedToPrintToFile()2547     public boolean checkAllowedToPrintToFile() {
2548         try {
2549             throwPrintToFile();
2550             return true;
2551         } catch (SecurityException e) {
2552             return false;
2553         }
2554     }
2555 
2556     /**
2557      * Break this out as it may be useful when we allow API to
2558      * specify printing to a file. In that case its probably right
2559      * to throw a SecurityException if the permission is not granted
2560      */
throwPrintToFile()2561     private void throwPrintToFile() {
2562         @SuppressWarnings("removal")
2563         SecurityManager security = System.getSecurityManager();
2564         if (security != null) {
2565             if (printToFilePermission == null) {
2566                 printToFilePermission =
2567                     new FilePermission("<<ALL FILES>>", "read,write");
2568             }
2569             security.checkPermission(printToFilePermission);
2570         }
2571     }
2572 
2573     /* On-screen drawString renders most control chars as the missing glyph
2574      * and have the non-zero advance of that glyph.
2575      * Exceptions are \t, \n and \r which are considered zero-width.
2576      * This is a utility method used by subclasses to remove them so we
2577      * don't have to worry about platform or font specific handling of them.
2578      */
removeControlChars(String s)2579     protected String removeControlChars(String s) {
2580         char[] in_chars = s.toCharArray();
2581         int len = in_chars.length;
2582         char[] out_chars = new char[len];
2583         int pos = 0;
2584 
2585         for (int i = 0; i < len; i++) {
2586             char c = in_chars[i];
2587             if (c > '\r' || c < '\t' || c == '\u000b' || c == '\u000c')  {
2588                out_chars[pos++] = c;
2589             }
2590         }
2591         if (pos == len) {
2592             return s; // no need to make a new String.
2593         } else {
2594             return new String(out_chars, 0, pos);
2595         }
2596     }
2597 
2598     private DialogOwner onTop = null;
2599 
2600     private long parentWindowID = 0L;
2601 
2602     /* Called from native code */
getParentWindowID()2603     private long getParentWindowID() {
2604         return parentWindowID;
2605     }
2606 
clearParentWindowID()2607     private void clearParentWindowID() {
2608         parentWindowID = 0L;
2609         onTop = null;
2610     }
2611 
setParentWindowID(PrintRequestAttributeSet attrs)2612     private void setParentWindowID(PrintRequestAttributeSet attrs) {
2613         parentWindowID = 0L;
2614         onTop = (DialogOwner)attrs.get(DialogOwner.class);
2615         if (onTop != null) {
2616             parentWindowID = DialogOwnerAccessor.getID(onTop);
2617         }
2618     }
2619 }
2620