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