View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, 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.awt.windows;
27  
28  import java.awt.Color;
29  import java.awt.Font;
30  import java.awt.Graphics2D;
31  import java.awt.GraphicsEnvironment;
32  import java.awt.HeadlessException;
33  import java.awt.KeyboardFocusManager;
34  import java.awt.Toolkit;
35  import java.awt.BasicStroke;
36  import java.awt.Button;
37  import java.awt.Component;
38  import java.awt.Dimension;
39  import java.awt.Event;
40  import java.awt.event.ActionEvent;
41  import java.awt.event.ActionListener;
42  import java.awt.FileDialog;
43  import java.awt.Dialog;
44  import java.awt.Label;
45  import java.awt.Panel;
46  import java.awt.Rectangle;
47  import java.awt.Window;
48  
49  import java.awt.image.BufferedImage;
50  import java.awt.image.IndexColorModel;
51  
52  import java.awt.print.Pageable;
53  import java.awt.print.PageFormat;
54  import java.awt.print.Paper;
55  import java.awt.print.Printable;
56  import java.awt.print.PrinterJob;
57  import java.awt.print.PrinterException;
58  import javax.print.PrintService;
59  
60  import java.io.IOException;
61  import java.io.File;
62  
63  import java.util.Hashtable;
64  import java.util.Properties;
65  import java.util.MissingResourceException;
66  import java.util.ResourceBundle;
67  
68  import sun.awt.Win32GraphicsEnvironment;
69  
70  import sun.print.PeekGraphics;
71  import sun.print.PeekMetrics;
72  
73  import java.net.URL;
74  import java.net.URI;
75  import java.net.URISyntaxException;
76  
77  import javax.print.PrintServiceLookup;
78  import javax.print.attribute.PrintRequestAttributeSet;
79  import javax.print.attribute.HashPrintServiceAttributeSet;
80  import javax.print.attribute.HashPrintRequestAttributeSet;
81  import javax.print.attribute.Attribute;
82  import javax.print.attribute.standard.Sides;
83  import javax.print.attribute.standard.Chromaticity;
84  import javax.print.attribute.standard.PrintQuality;
85  import javax.print.attribute.standard.PrinterResolution;
86  import javax.print.attribute.standard.SheetCollate;
87  import javax.print.attribute.IntegerSyntax;
88  import javax.print.attribute.standard.Copies;
89  import javax.print.attribute.standard.Destination;
90  import javax.print.attribute.standard.OrientationRequested;
91  import javax.print.attribute.standard.Media;
92  import javax.print.attribute.standard.MediaSizeName;
93  import javax.print.attribute.standard.MediaSize;
94  import javax.print.attribute.standard.MediaTray;
95  import javax.print.attribute.standard.PrinterName;
96  import javax.print.attribute.standard.JobMediaSheetsSupported;
97  import javax.print.attribute.standard.PageRanges;
98  import javax.print.attribute.Size2DSyntax;
99  
100 import sun.awt.Win32FontManager;
101 
102 import sun.print.RasterPrinterJob;
103 import sun.print.SunAlternateMedia;
104 import sun.print.SunPageSelection;
105 import sun.print.SunMinMaxPage;
106 import sun.print.Win32MediaTray;
107 import sun.print.Win32PrintService;
108 import sun.print.Win32PrintServiceLookup;
109 import sun.print.ServiceDialog;
110 import sun.print.DialogOwner;
111 
112 import java.awt.Frame;
113 import java.io.FilePermission;
114 
115 import sun.java2d.Disposer;
116 import sun.java2d.DisposerRecord;
117 import sun.java2d.DisposerTarget;
118 
119 /**
120  * A class which initiates and executes a Win32 printer job.
121  *
122  * @author Richard Blanchard
123  */
124 public class WPrinterJob extends RasterPrinterJob implements DisposerTarget {
125 
126  /* Class Constants */
127 
128 
129 /* Instance Variables */
130 
131     /**
132      * These are Windows' ExtCreatePen End Cap Styles
133      * and must match the values in <WINGDI.h>
134      */
135     protected static final long PS_ENDCAP_ROUND  = 0x00000000;
136     protected static final long PS_ENDCAP_SQUARE   = 0x00000100;
137     protected static final long PS_ENDCAP_FLAT   =   0x00000200;
138 
139     /**
140      * These are Windows' ExtCreatePen Line Join Styles
141      * and must match the values in <WINGDI.h>
142      */
143     protected static final long PS_JOIN_ROUND   =    0x00000000;
144     protected static final long PS_JOIN_BEVEL   =    0x00001000;
145     protected static final long PS_JOIN_MITER   =    0x00002000;
146 
147     /**
148      * This is the Window's Polygon fill rule which
149      * Selects alternate mode (fills the area between odd-numbered
150      * and even-numbered polygon sides on each scan line).
151      * It must match the value in <WINGDI.h> It can be passed
152      * to setPolyFillMode().
153      */
154     protected static final int POLYFILL_ALTERNATE = 1;
155 
156     /**
157      * This is the Window's Polygon fill rule which
158      * Selects winding mode which fills any region
159      * with a nonzero winding value). It must match
160      * the value in <WINGDI.h> It can be passed
161      * to setPolyFillMode().
162      */
163     protected static final int POLYFILL_WINDING = 2;
164 
165     /**
166      * The maximum value for a Window's color component
167      * as passed to selectSolidBrush.
168      */
169     private static final int MAX_WCOLOR = 255;
170 
171     /**
172      * Flags for setting values from devmode in native code.
173      * Values must match those defined in awt_PrintControl.cpp
174      */
175     private static final int SET_DUP_VERTICAL = 0x00000010;
176     private static final int SET_DUP_HORIZONTAL = 0x00000020;
177     private static final int SET_RES_HIGH = 0x00000040;
178     private static final int SET_RES_LOW = 0x00000080;
179     private static final int SET_COLOR = 0x00000200;
180     private static final int SET_ORIENTATION = 0x00004000;
181     private static final int SET_COLLATED    = 0x00008000;
182 
183     /**
184      * Values must match those defined in wingdi.h & commdlg.h
185      */
186     private static final int PD_COLLATE = 0x00000010;
187     private static final int PD_PRINTTOFILE = 0x00000020;
188     private static final int DM_ORIENTATION   = 0x00000001;
189     private static final int DM_PAPERSIZE     = 0x00000002;
190     private static final int DM_COPIES        = 0x00000100;
191     private static final int DM_DEFAULTSOURCE = 0x00000200;
192     private static final int DM_PRINTQUALITY  = 0x00000400;
193     private static final int DM_COLOR         = 0x00000800;
194     private static final int DM_DUPLEX        = 0x00001000;
195     private static final int DM_YRESOLUTION   = 0x00002000;
196     private static final int DM_COLLATE       = 0x00008000;
197 
198     private static final short DMCOLLATE_FALSE  = 0;
199     private static final short DMCOLLATE_TRUE   = 1;
200 
201     private static final short DMORIENT_PORTRAIT  = 1;
202     private static final short DMORIENT_LANDSCAPE = 2;
203 
204     private static final short DMCOLOR_MONOCHROME = 1;
205     private static final short DMCOLOR_COLOR      = 2;
206 
207     private static final short DMRES_DRAFT  = -1;
208     private static final short DMRES_LOW    = -2;
209     private static final short DMRES_MEDIUM = -3;
210     private static final short DMRES_HIGH   = -4;
211 
212     private static final short DMDUP_SIMPLEX    = 1;
213     private static final short DMDUP_VERTICAL   = 2;
214     private static final short DMDUP_HORIZONTAL = 3;
215 
216     /**
217      * Pageable MAX pages
218      */
219     private static final int MAX_UNKNOWN_PAGES = 9999;
220 
221 
222     /* Collation and copy flags.
223      * The Windows PRINTDLG struct has a nCopies field which on return
224      * indicates how many copies of a print job an application must render.
225      * There is also a PD_COLLATE member of the flags field which if
226      * set on return indicates the application generated copies should be
227      * collated.
228      * Windows printer drivers typically - but not always - support
229      * generating multiple copies themselves, but uncollated is more
230      * universal than collated copies.
231      * When they do, they read the initial values from the PRINTDLG structure
232      * and set them into the driver's DEVMODE structure and intialise
233      * the printer DC based on that, so that when printed those settings
234      * will be used.
235      * For drivers supporting both these capabilities via DEVMODE, then on
236      * return from the Print Dialog, nCopies is set to 1 and the PD_COLLATE is
237      * cleared, so that the application will only render 1 copy and the
238      * driver takes care of the rest.
239      *
240      * Applications which want to know what's going on have to be DEVMODE
241      * savvy and peek at that.
242      * DM_COPIES flag indicates support for multiple driver copies
243      * and dmCopies is the number of copies the driver will print
244      * DM_COLLATE flag indicates support for collated driver copies and
245      * dmCollate == DMCOLLATE_TRUE indicates the option is in effect.
246      *
247      * Multiple copies from Java applications:
248      * We provide API to get & set the number of copies as well as allowing the
249      * user to choose it, so we need to be savvy about DEVMODE, so that
250      * we can accurately report back the number of copies selected by
251      * the user, as well as make use of the driver to render multiple copies.
252      *
253      * Collation and Java applications:
254      * We presently provide no API for specifying collation, but its
255      * present on the Windows Print Dialog, and when a user checks it
256      * they expect it to be obeyed.
257      * The best thing to do is to detect exactly the cases where the
258      * driver doesn't support this and render multiple copies ourselves.
259      * To support all this we need several flags which signal the
260      * printer's capabilities and the user's requests.
261      * Its questionable if we (yet) need to make a distinction between
262      * the user requesting collation and the driver supporting it.
263      * Since for now we only need to know whether we need to render the
264      * copies. However it allows the logic to be clearer.
265      * These fields are changed by native code which detects the driver's
266      * capabilities and the user's choices.
267      */
268 
269     //initialize to false because the Flags that we initialized in PRINTDLG
270     // tells GDI that we can handle our own collation and multiple copies
271      private boolean driverDoesMultipleCopies = false;
272      private boolean driverDoesCollation = false;
273      private boolean userRequestedCollation = false;
274      private boolean noDefaultPrinter = false;
275 
276     /* The HandleRecord holds the native resources that need to be freed
277      * when this WPrinterJob is GC'd.
278      */
279     static class HandleRecord implements DisposerRecord {
280         /**
281          * The Windows device context we will print into.
282          * This variable is set after the Print dialog
283          * is okayed by the user. If the user cancels
284          * the print dialog, then this variable is 0.
285          * Much of the configuration information for a printer is
286          * obtained through printer device specific handles.
287          * We need to associate these with, and free with, the mPrintDC.
288          */
289         private long mPrintDC;
290         private long mPrintHDevMode;
291         private long mPrintHDevNames;
292 
293         public void dispose() {
294             WPrinterJob.deleteDC(mPrintDC, mPrintHDevMode, mPrintHDevNames);
295         }
296     }
297 
298     private HandleRecord handleRecord = new HandleRecord();
299 
300     private int mPrintPaperSize;
301 
302     /* These fields are directly set in upcalls from the values
303      * obtained from calling DeviceCapabilities()
304      */
305     private int mPrintXRes;   // pixels per inch in x direction
306 
307     private int mPrintYRes;   // pixels per inch in y direction
308 
309     private int mPrintPhysX;  // x offset in pixels of printable area
310 
311     private int mPrintPhysY;  // y offset in pixels of printable area
312 
313     private int mPrintWidth;  // width in pixels of printable area
314 
315     private int mPrintHeight; // height in pixels of printable area
316 
317     private int mPageWidth;   // width in pixels of entire page
318 
319     private int mPageHeight;  // height in pixels of entire page
320 
321     /* The values of the following variables are pulled directly
322      * into native code (even bypassing getter methods) when starting a doc.
323      * So these need to be synced up from any resulting native changes
324      * by a user dialog.
325      * But the native changes call up to into the attributeset, and that
326      * should be sufficient, since before heading down to native either
327      * to (re-)display a dialog, or to print the doc, these are all
328      * re-populated from the AttributeSet,
329      * Nonetheless having them in sync with the attributeset and native
330      * state is probably safer.
331      * Also whereas the startDoc native code pulls the variables directly,
332      * the dialog code does use getter to pull some of these values.
333      * That's an inconsistency we should fix if it causes problems.
334      */
335     private int mAttSides;
336     private int mAttChromaticity;
337     private int mAttXRes;
338     private int mAttYRes;
339     private int mAttQuality;
340     private int mAttCollate;
341     private int mAttCopies;
342     private int mAttMediaSizeName;
343     private int mAttMediaTray;
344 
345     private String mDestination = null;
346 
347     /**
348      * The last color set into the print device context or
349      * <code>null</code> if no color has been set.
350      */
351     private Color mLastColor;
352 
353     /**
354      * The last text color set into the print device context or
355      * <code>null</code> if no color has been set.
356      */
357     private Color mLastTextColor;
358 
359     /**
360      * Define the most recent java font set as a GDI font in the printer
361      * device context. mLastFontFamily will be NULL if no
362      * GDI font has been set.
363      */
364     private String mLastFontFamily;
365     private float mLastFontSize;
366     private int mLastFontStyle;
367     private int mLastRotation;
368     private float mLastAwScale;
369 
370     // for AwtPrintControl::InitPrintDialog
371     private PrinterJob pjob;
372 
373     private java.awt.peer.ComponentPeer dialogOwnerPeer = null;
374 
375  /* Static Initializations */
376 
377     static {
378         // AWT has to be initialized for the native code to function correctly.
379         Toolkit.getDefaultToolkit();
380 
381         initIDs();
382 
383         Win32FontManager.registerJREFontsForPrinting();
384     }
385 
386     /* Constructors */
387 
388     public WPrinterJob()
389     {
390         Disposer.addRecord(disposerReferent,
391                            handleRecord = new HandleRecord());
392         initAttributeMembers();
393     }
394 
395     /* Implement DisposerTarget. Weak references to an Object can delay
396      * its storage reclaimation marginally.
397      * It won't make the native resources be release any more quickly, but
398      * by pointing the reference held by Disposer at an object which becomes
399      * no longer strongly reachable when this WPrinterJob is no longer
400      * strongly reachable, we allow the WPrinterJob to be freed more promptly
401      * than if it were the referenced object.
402      */
403     private Object disposerReferent = new Object();
404 
405     public Object getDisposerReferent() {
406         return disposerReferent;
407     }
408 
409 /* Instance Methods */
410 
411     /**
412      * Display a dialog to the user allowing the modification of a
413      * PageFormat instance.
414      * The <code>page</code> argument is used to initialize controls
415      * in the page setup dialog.
416      * If the user cancels the dialog, then the method returns the
417      * original <code>page</code> object unmodified.
418      * If the user okays the dialog then the method returns a new
419      * PageFormat object with the indicated changes.
420      * In either case the original <code>page</code> object will
421      * not be modified.
422      * @param     page    the default PageFormat presented to the user
423      *                    for modification
424      * @return    the original <code>page</code> object if the dialog
425      *            is cancelled, or a new PageFormat object containing
426      *            the format indicated by the user if the dialog is
427      *            acknowledged
428      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
429      * returns true.
430      * @see java.awt.GraphicsEnvironment#isHeadless
431      * @since     JDK1.2
432      */
433     public PageFormat pageDialog(PageFormat page) throws HeadlessException {
434         if (GraphicsEnvironment.isHeadless()) {
435             throw new HeadlessException();
436         }
437 
438         if (!(getPrintService() instanceof Win32PrintService)) {
439             return super.pageDialog(page);
440         }
441 
442         PageFormat pageClone = (PageFormat) page.clone();
443         boolean result = false;
444 
445         /*
446          * Fix for 4507585: show the native modal dialog the same way printDialog() does so
447          * that it won't block event dispatching when called on EventDispatchThread.
448          */
449         WPageDialog dialog = new WPageDialog((Frame)null, this,
450                                      pageClone, null);
451         dialog.setRetVal(false);
452         dialog.setVisible(true);
453         result = dialog.getRetVal();
454         dialog.dispose();
455 
456         // myService => current PrintService
457         if (result && (myService != null)) {
458             // It's possible that current printer is changed through
459             // the "Printer..." button so we query again from native.
460             String printerName = getNativePrintService();
461             if (!myService.getName().equals(printerName)) {
462                 // native printer is different !
463                 // we update the current PrintService
464                 try {
465                     setPrintService(Win32PrintServiceLookup.
466                                     getWin32PrintLUS().
467                                     getPrintServiceByName(printerName));
468                 } catch (PrinterException e) {
469                 }
470             }
471             // Update attributes, this will preserve the page settings.
472             //  - same code as in RasterPrinterJob.java
473             updatePageAttributes(myService, pageClone);
474 
475             return pageClone;
476         } else {
477             return page;
478         }
479     }
480 
481 
482     private boolean displayNativeDialog() {
483         // "attributes" is required for getting the updated attributes
484         if (attributes == null) {
485             return false;
486         }
487 
488         DialogOwner dlgOwner = (DialogOwner)attributes.get(DialogOwner.class);
489         Frame ownerFrame = (dlgOwner != null) ? dlgOwner.getOwner() : null;
490 
491         WPrintDialog dialog = new WPrintDialog(ownerFrame, this);
492         dialog.setRetVal(false);
493         dialog.setVisible(true);
494         boolean prv = dialog.getRetVal();
495         dialog.dispose();
496 
497         Destination dest =
498                 (Destination)attributes.get(Destination.class);
499         if ((dest == null) || !prv){
500                 return prv;
501         } else {
502             String title = null;
503             String strBundle = "sun.print.resources.serviceui";
504             ResourceBundle rb = ResourceBundle.getBundle(strBundle);
505             try {
506                 title = rb.getString("dialog.printtofile");
507             } catch (MissingResourceException e) {
508             }
509             FileDialog fileDialog = new FileDialog(ownerFrame, title,
510                                                    FileDialog.SAVE);
511 
512             URI destURI = dest.getURI();
513             // Old code destURI.getPath() would return null for "file:out.prn"
514             // so we use getSchemeSpecificPart instead.
515             String pathName = (destURI != null) ?
516                 destURI.getSchemeSpecificPart() : null;
517             if (pathName != null) {
518                File file = new File(pathName);
519                fileDialog.setFile(file.getName());
520                File parent = file.getParentFile();
521                if (parent != null) {
522                    fileDialog.setDirectory(parent.getPath());
523                }
524             } else {
525                 fileDialog.setFile("out.prn");
526             }
527 
528             fileDialog.setVisible(true);
529             String fileName = fileDialog.getFile();
530             if (fileName == null) {
531                 fileDialog.dispose();
532                 return false;
533             }
534             String fullName = fileDialog.getDirectory() + fileName;
535             File f = new File(fullName);
536             File pFile = f.getParentFile();
537             while ((f.exists() &&
538                       (!f.isFile() || !f.canWrite())) ||
539                    ((pFile != null) &&
540                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
541 
542                 (new PrintToFileErrorDialog(ownerFrame,
543                                 ServiceDialog.getMsg("dialog.owtitle"),
544                                 ServiceDialog.getMsg("dialog.writeerror")+" "+fullName,
545                                 ServiceDialog.getMsg("button.ok"))).setVisible(true);
546 
547                 fileDialog.setVisible(true);
548                 fileName = fileDialog.getFile();
549                 if (fileName == null) {
550                     fileDialog.dispose();
551                     return false;
552                 }
553                 fullName = fileDialog.getDirectory() + fileName;
554                 f = new File(fullName);
555                 pFile = f.getParentFile();
556             }
557             fileDialog.dispose();
558             attributes.add(new Destination(f.toURI()));
559             return true;
560         }
561 
562     }
563 
564     /**
565      * Presents the user a dialog for changing properties of the
566      * print job interactively.
567      * @returns false if the user cancels the dialog and
568      *          true otherwise.
569      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
570      * returns true.
571      * @see java.awt.GraphicsEnvironment#isHeadless
572      */
573     public boolean printDialog() throws HeadlessException {
574 
575         if (GraphicsEnvironment.isHeadless()) {
576             throw new HeadlessException();
577         }
578         // current request attribute set should be reflected to the print dialog.
579         // If null, create new instance of HashPrintRequestAttributeSet.
580         if (attributes == null) {
581             attributes = new HashPrintRequestAttributeSet();
582         }
583 
584         if (!(getPrintService() instanceof Win32PrintService)) {
585             return super.printDialog(attributes);
586         }
587 
588         if (noDefaultPrinter == true) {
589             return false;
590         } else {
591             return displayNativeDialog();
592         }
593     }
594 
595      /**
596      * Associate this PrinterJob with a new PrintService.
597      *
598      * Throws <code>PrinterException</code> if the specified service
599      * cannot support the <code>Pageable</code> and
600      * </code>Printable</code> interfaces necessary to support 2D printing.
601      * @param a print service which supports 2D printing.
602      *
603      * @throws PrinterException if the specified service does not support
604      * 2D printing.
605      */
606     public void setPrintService(PrintService service)
607         throws PrinterException {
608         super.setPrintService(service);
609         if (!(service instanceof Win32PrintService)) {
610             return;
611         }
612         driverDoesMultipleCopies = false;
613         driverDoesCollation = false;
614         setNativePrintServiceIfNeeded(service.getName());
615     }
616 
617     /* associates this job with the specified native service */
618     private native void setNativePrintService(String name)
619         throws PrinterException;
620 
621     private String lastNativeService = null;
622     private void setNativePrintServiceIfNeeded(String name)
623         throws PrinterException {
624 
625         if (name != null && !(name.equals(lastNativeService))) {
626             setNativePrintService(name);
627             lastNativeService = name;
628         }
629     }
630 
631     public PrintService getPrintService() {
632         if (myService == null) {
633             String printerName = getNativePrintService();
634 
635             if (printerName != null) {
636                 myService = Win32PrintServiceLookup.getWin32PrintLUS().
637                     getPrintServiceByName(printerName);
638                 // no need to call setNativePrintService as this name is
639                 // currently set in native
640                 if (myService != null) {
641                     return myService;
642                 }
643             }
644 
645             myService = PrintServiceLookup.lookupDefaultPrintService();
646             if (myService instanceof Win32PrintService) {
647                 try {
648                     setNativePrintServiceIfNeeded(myService.getName());
649                 } catch (Exception e) {
650                     myService = null;
651                 }
652             }
653 
654           }
655           return myService;
656     }
657 
658     private native String getNativePrintService();
659 
660     private void initAttributeMembers() {
661             mAttSides = 0;
662             mAttChromaticity = 0;
663             mAttXRes = 0;
664             mAttYRes = 0;
665             mAttQuality = 0;
666             mAttCollate = -1;
667             mAttCopies = 0;
668             mAttMediaTray = 0;
669             mAttMediaSizeName = 0;
670             mDestination = null;
671 
672     }
673 
674     /**
675      * copy the attributes to the native print job
676      * Note that this method, and hence the re-initialisation
677      * of the GDI values is done on each entry to the print dialog since
678      * an app could redisplay the print dialog for the same job and
679      * 1) the application may have changed attribute settings
680      * 2) the application may have changed the printer.
681      * In the event that the user changes the printer using the
682       dialog, then it is up to GDI to report back all changed values.
683      */
684     protected void setAttributes(PrintRequestAttributeSet attributes)
685         throws PrinterException {
686 
687         // initialize attribute values
688         initAttributeMembers();
689         super.setAttributes(attributes);
690 
691         mAttCopies = getCopiesInt();
692         mDestination = destinationAttr;
693 
694         if (attributes == null) {
695             return; // now always use attributes, so this shouldn't happen.
696         }
697         Attribute[] attrs = attributes.toArray();
698         for (int i=0; i< attrs.length; i++) {
699             Attribute attr = attrs[i];
700             try {
701                  if (attr.getCategory() == Sides.class) {
702                     setSidesAttrib(attr);
703                 }
704                 else if (attr.getCategory() == Chromaticity.class) {
705                     setColorAttrib(attr);
706                 }
707                 else if (attr.getCategory() == PrinterResolution.class) {
708                     setResolutionAttrib(attr);
709                 }
710                 else if (attr.getCategory() == PrintQuality.class) {
711                     setQualityAttrib(attr);
712                 }
713                 else if (attr.getCategory() == SheetCollate.class) {
714                     setCollateAttrib(attr);
715                 }  else if (attr.getCategory() == Media.class ||
716                             attr.getCategory() == SunAlternateMedia.class) {
717                     /* SunAlternateMedia is used if its a tray, and
718                      * any Media that is specified is not a tray.
719                      */
720                     if (attr.getCategory() == SunAlternateMedia.class) {
721                         Media media = (Media)attributes.get(Media.class);
722                         if (media == null ||
723                             !(media instanceof MediaTray)) {
724                             attr = ((SunAlternateMedia)attr).getMedia();
725                         }
726                     }
727                     if (attr instanceof MediaSizeName) {
728                         setWin32MediaAttrib(attr);
729                     }
730                     if (attr instanceof MediaTray) {
731                         setMediaTrayAttrib(attr);
732                     }
733                 }
734 
735             } catch (ClassCastException e) {
736             }
737         }
738     }
739 
740     /**
741      * Alters the orientation and Paper to match defaults obtained
742      * from a printer.
743      */
744     private native void getDefaultPage(PageFormat page);
745 
746     /**
747      * The passed in PageFormat will be copied and altered to describe
748      * the default page size and orientation of the PrinterJob's
749      * current printer.
750      * Note: PageFormat.getPaper() returns a clone and getDefaultPage()
751      * gets that clone so it won't overwrite the original paper.
752      */
753     public PageFormat defaultPage(PageFormat page) {
754         PageFormat newPage = (PageFormat)page.clone();
755         getDefaultPage(newPage);
756         return newPage;
757     }
758 
759     /**
760      * validate the paper size against the current printer.
761      */
762     protected native void validatePaper(Paper origPaper, Paper newPaper );
763 
764     /**
765      * Examine the metrics captured by the
766      * <code>PeekGraphics</code> instance and
767      * if capable of directly converting this
768      * print job to the printer's control language
769      * or the native OS's graphics primitives, then
770      * return a <code>PathGraphics</code> to perform
771      * that conversion. If there is not an object
772      * capable of the conversion then return
773      * <code>null</code>. Returning <code>null</code>
774      * causes the print job to be rasterized.
775      */
776 
777     protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
778                                             PrinterJob printerJob,
779                                             Printable painter,
780                                             PageFormat pageFormat,
781                                             int pageIndex) {
782 
783         WPathGraphics pathGraphics;
784         PeekMetrics metrics = peekGraphics.getMetrics();
785 
786         /* If the application has drawn anything that
787          * out PathGraphics class can not handle then
788          * return a null PathGraphics. If the property
789          * to force the raster pipeline has been set then
790          * we also want to avoid the path (pdl) pipeline
791          * and return null.
792          */
793        if (forcePDL == false && (forceRaster == true
794                                   || metrics.hasNonSolidColors()
795                                   || metrics.hasCompositing()
796                                   )) {
797             pathGraphics = null;
798         } else {
799             BufferedImage bufferedImage = new BufferedImage(8, 8,
800                                             BufferedImage.TYPE_INT_RGB);
801             Graphics2D bufferedGraphics = bufferedImage.createGraphics();
802 
803             boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
804             pathGraphics =  new WPathGraphics(bufferedGraphics, printerJob,
805                                               painter, pageFormat, pageIndex,
806                                               canRedraw);
807         }
808 
809         return pathGraphics;
810     }
811 
812 
813     protected double getXRes() {
814         if (mAttXRes != 0) {
815             return mAttXRes;
816         } else {
817             return mPrintXRes;
818         }
819     }
820 
821     protected double getYRes() {
822         if (mAttYRes != 0) {
823             return mAttYRes;
824         } else {
825             return mPrintYRes;
826         }
827     }
828 
829     protected double getPhysicalPrintableX(Paper p) {
830         return mPrintPhysX;
831     }
832 
833     protected double getPhysicalPrintableY(Paper p) {
834         return mPrintPhysY;
835     }
836 
837     protected double getPhysicalPrintableWidth(Paper p) {
838         return mPrintWidth;
839     }
840 
841     protected double getPhysicalPrintableHeight(Paper p) {
842         return mPrintHeight;
843     }
844 
845     protected double getPhysicalPageWidth(Paper p) {
846         return mPageWidth;
847     }
848 
849     protected double getPhysicalPageHeight(Paper p) {
850         return mPageHeight;
851     }
852 
853     /**
854      * We don't (yet) provide API to support collation, and
855      * when we do the logic here will require adjustment, but
856      * this method is currently necessary to honour user-originated
857      * collation requests - which can only originate from the print dialog.
858      * REMIND: check if this can be deleted already.
859      */
860     protected boolean isCollated() {
861         return userRequestedCollation;
862     }
863 
864     /**
865      * Returns how many times the entire book should
866      * be printed by the PrintJob. If the printer
867      * itself supports collation then this method
868      * should return 1 indicating that the entire
869      * book need only be printed once and the copies
870      * will be collated and made in the printer.
871      */
872     protected int getCollatedCopies() {
873         debug_println("driverDoesMultipleCopies="+driverDoesMultipleCopies
874                       +" driverDoesCollation="+driverDoesCollation);
875         if  (super.isCollated() && !driverDoesCollation) {
876             // we will do our own collation so we need to
877             // tell the printer to not collate
878             mAttCollate = 0;
879             mAttCopies = 1;
880             return getCopies();
881         }
882 
883         return 1;
884     }
885 
886     /**
887      * Returns how many times each page in the book
888      * should be consecutively printed by PrinterJob.
889      * If the underlying Window's driver will
890      * generate the copies, rather than having RasterPrinterJob
891      * iterate over the number of copies, this method always returns
892      * 1.
893      */
894     protected int getNoncollatedCopies() {
895         if (driverDoesMultipleCopies || super.isCollated()) {
896             return 1;
897         } else {
898             return getCopies();
899         }
900     }
901 
902     /* These getter/setters are called from native code */
903 
904     /**
905      * Return the Window's device context that we are printing
906      * into.
907      */
908     private long getPrintDC() {
909         return handleRecord.mPrintDC;
910     }
911 
912     private void setPrintDC(long mPrintDC) {
913         handleRecord.mPrintDC = mPrintDC;
914     }
915 
916     private long getDevMode() {
917         return handleRecord.mPrintHDevMode;
918     }
919 
920     private void setDevMode(long mPrintHDevMode) {
921         handleRecord.mPrintHDevMode = mPrintHDevMode;
922     }
923 
924     private long getDevNames() {
925         return handleRecord.mPrintHDevNames;
926     }
927 
928     private void setDevNames(long mPrintHDevNames) {
929         handleRecord.mPrintHDevNames = mPrintHDevNames;
930     }
931 
932     protected void beginPath() {
933         beginPath(getPrintDC());
934     }
935 
936     protected void endPath() {
937         endPath(getPrintDC());
938     }
939 
940     protected void closeFigure() {
941         closeFigure(getPrintDC());
942     }
943 
944     protected void fillPath() {
945         fillPath(getPrintDC());
946     }
947 
948     protected void moveTo(float x, float y) {
949         moveTo(getPrintDC(), x, y);
950     }
951 
952     protected void lineTo(float x, float y) {
953         lineTo(getPrintDC(), x, y);
954     }
955 
956     protected void polyBezierTo(float control1x, float control1y,
957                                 float control2x, float control2y,
958                                 float endX, float endY) {
959 
960         polyBezierTo(getPrintDC(), control1x, control1y,
961                                control2x, control2y,
962                                endX, endY);
963     }
964 
965     /**
966      * Set the current polgon fill rule into the printer device context.
967      * The <code>fillRule</code> should
968      * be one of the following Windows constants:
969      * <code>ALTERNATE</code> or <code>WINDING</code>.
970      */
971     protected void setPolyFillMode(int fillRule) {
972         setPolyFillMode(getPrintDC(), fillRule);
973     }
974 
975     /*
976      * Create a Window's solid brush for the color specified
977      * by <code>(red, green, blue)</code>. Once the brush
978      * is created, select it in the current printing device
979      * context and free the old brush.
980      */
981     protected void selectSolidBrush(Color color) {
982 
983         /* We only need to select a brush if the color has changed.
984         */
985         if (color.equals(mLastColor) == false) {
986             mLastColor = color;
987             float[] rgb = color.getRGBColorComponents(null);
988 
989             selectSolidBrush(getPrintDC(),
990                              (int) (rgb[0] * MAX_WCOLOR),
991                              (int) (rgb[1] * MAX_WCOLOR),
992                              (int) (rgb[2] * MAX_WCOLOR));
993         }
994     }
995 
996     /**
997      * Return the x coordinate of the current pen
998      * position in the print device context.
999      */
1000     protected int getPenX() {
1001 
1002         return getPenX(getPrintDC());
1003     }
1004 
1005 
1006     /**
1007      * Return the y coordinate of the current pen
1008      * position in the print device context.
1009      */
1010     protected int getPenY() {
1011 
1012         return getPenY(getPrintDC());
1013     }
1014 
1015     /**
1016      * Set the current path in the printer device's
1017      * context to be clipping path.
1018      */
1019     protected void selectClipPath() {
1020         selectClipPath(getPrintDC());
1021     }
1022 
1023 
1024     protected void frameRect(float x, float y, float width, float height) {
1025         frameRect(getPrintDC(), x, y, width, height);
1026     }
1027 
1028     protected void fillRect(float x, float y, float width, float height,
1029                             Color color) {
1030         float[] rgb = color.getRGBColorComponents(null);
1031 
1032         fillRect(getPrintDC(), x, y, width, height,
1033                  (int) (rgb[0] * MAX_WCOLOR),
1034                  (int) (rgb[1] * MAX_WCOLOR),
1035                  (int) (rgb[2] * MAX_WCOLOR));
1036     }
1037 
1038 
1039     protected void selectPen(float width, Color color) {
1040 
1041         float[] rgb = color.getRGBColorComponents(null);
1042 
1043         selectPen(getPrintDC(), width,
1044                   (int) (rgb[0] * MAX_WCOLOR),
1045                   (int) (rgb[1] * MAX_WCOLOR),
1046                   (int) (rgb[2] * MAX_WCOLOR));
1047     }
1048 
1049 
1050     protected boolean selectStylePen(int cap, int join, float width,
1051                                      Color color) {
1052 
1053         long endCap;
1054         long lineJoin;
1055 
1056         float[] rgb = color.getRGBColorComponents(null);
1057 
1058         switch(cap) {
1059         case BasicStroke.CAP_BUTT: endCap = PS_ENDCAP_FLAT; break;
1060         case BasicStroke.CAP_ROUND: endCap = PS_ENDCAP_ROUND; break;
1061         default:
1062         case BasicStroke.CAP_SQUARE: endCap = PS_ENDCAP_SQUARE; break;
1063         }
1064 
1065         switch(join) {
1066         case BasicStroke.JOIN_BEVEL:lineJoin = PS_JOIN_BEVEL; break;
1067         default:
1068         case BasicStroke.JOIN_MITER:lineJoin = PS_JOIN_MITER; break;
1069         case BasicStroke.JOIN_ROUND:lineJoin = PS_JOIN_ROUND; break;
1070         }
1071 
1072         return (selectStylePen(getPrintDC(), endCap, lineJoin, width,
1073                                (int) (rgb[0] * MAX_WCOLOR),
1074                                (int) (rgb[1] * MAX_WCOLOR),
1075                                (int) (rgb[2] * MAX_WCOLOR)));
1076     }
1077 
1078     /**
1079      * Set a GDI font capable of drawing the java Font
1080      * passed in.
1081      */
1082     protected boolean setFont(String family, float size, int style,
1083                               int rotation, float awScale) {
1084 
1085         boolean didSetFont = true;
1086 
1087         if (!family.equals(mLastFontFamily) ||
1088             size     != mLastFontSize       ||
1089             style    != mLastFontStyle      ||
1090             rotation != mLastRotation       ||
1091             awScale  != mLastAwScale) {
1092 
1093             didSetFont = setFont(getPrintDC(),
1094                                  family,
1095                                  size,
1096                                  (style & Font.BOLD) != 0,
1097                                  (style & Font.ITALIC) != 0,
1098                                  rotation, awScale);
1099             if (didSetFont) {
1100                 mLastFontFamily   = family;
1101                 mLastFontSize     = size;
1102                 mLastFontStyle    = style;
1103                 mLastRotation     = rotation;
1104                 mLastAwScale      = awScale;
1105             }
1106         }
1107         return didSetFont;
1108     }
1109 
1110     /**
1111      * Set the GDI color for text drawing.
1112      */
1113     protected void setTextColor(Color color) {
1114 
1115         /* We only need to select a brush if the color has changed.
1116         */
1117         if (color.equals(mLastTextColor) == false) {
1118             mLastTextColor = color;
1119             float[] rgb = color.getRGBColorComponents(null);
1120 
1121             setTextColor(getPrintDC(),
1122                          (int) (rgb[0] * MAX_WCOLOR),
1123                          (int) (rgb[1] * MAX_WCOLOR),
1124                          (int) (rgb[2] * MAX_WCOLOR));
1125         }
1126     }
1127 
1128     /**
1129      * Remove control characters.
1130      */
1131     protected String removeControlChars(String str) {
1132         return super.removeControlChars(str);
1133     }
1134 
1135     /**
1136      * Draw the string <code>text</code> to the printer's
1137      * device context at the specified position.
1138      */
1139     protected void textOut(String str, float x, float y,
1140                            float[] positions) {
1141         /* Don't leave handling of control chars to GDI.
1142          * If control chars are removed,  'positions' isn't valid.
1143          * This means the caller needs to be aware of this and remove
1144          * control chars up front if supplying positions. Since the
1145          * caller is tightly integrated here, that's acceptable.
1146          */
1147         String text = removeControlChars(str);
1148         assert (positions == null) || (text.length() == str.length());
1149         if (text.length() == 0) {
1150             return;
1151         }
1152         textOut(getPrintDC(), text, text.length(), false, x, y, positions);
1153     }
1154 
1155    /**
1156      * Draw the glyphs <code>glyphs</code> to the printer's
1157      * device context at the specified position.
1158      */
1159     protected void glyphsOut(int []glyphs, float x, float y,
1160                              float[] positions) {
1161 
1162         /* TrueType glyph codes are 16 bit values, so can be packed
1163          * in a unicode string, and that's how GDI expects them.
1164          * A flag bit is set to indicate to GDI that these are glyphs,
1165          * not characters. The positions array must always be non-null
1166          * here for our purposes, although if not supplied, GDI should
1167          * just use the default advances for the glyphs.
1168          * Mask out upper 16 bits to remove any slot from a composite.
1169          */
1170         char[] glyphCharArray = new char[glyphs.length];
1171         for (int i=0;i<glyphs.length;i++) {
1172             glyphCharArray[i] = (char)(glyphs[i] & 0xffff);
1173         }
1174         String glyphStr = new String(glyphCharArray);
1175         textOut(getPrintDC(), glyphStr, glyphs.length, true, x, y, positions);
1176     }
1177 
1178 
1179     /**
1180      * Get the advance of this text that GDI returns for the
1181      * font currently selected into the GDI device context for
1182      * this job. Note that the removed control characters are
1183      * interpreted as zero-width by JDK and we remove them for
1184      * rendering so also remove them for measurement so that
1185      * this measurement can be properly compared with JDK measurement.
1186      */
1187     protected int getGDIAdvance(String text) {
1188         /* Don't leave handling of control chars to GDI. */
1189         text = removeControlChars(text);
1190         if (text.length() == 0) {
1191             return 0;
1192         }
1193         return getGDIAdvance(getPrintDC(), text);
1194     }
1195 
1196      /**
1197      * Draw the 24 bit BGR image buffer represented by
1198      * <code>image</code> to the GDI device context
1199      * <code>printDC</code>. The image is drawn at
1200      * <code>(destX, destY)</code> in device coordinates.
1201      * The image is scaled into a square of size
1202      * specified by <code>destWidth</code> and
1203      * <code>destHeight</code>. The portion of the
1204      * source image copied into that square is specified
1205      * by <code>srcX</code>, <code>srcY</code>,
1206      * <code>srcWidth</code>, and srcHeight.
1207      */
1208     protected void drawImage3ByteBGR(byte[] image,
1209                                      float destX, float destY,
1210                                      float destWidth, float destHeight,
1211                                      float srcX, float srcY,
1212                                      float srcWidth, float srcHeight) {
1213 
1214 
1215         drawDIBImage(getPrintDC(), image,
1216                      destX, destY,
1217                      destWidth, destHeight,
1218                      srcX, srcY,
1219                      srcWidth, srcHeight,
1220                      24, null);
1221 
1222     }
1223 
1224     /* If 'icm' is null we expect its 24 bit (ie 3BYTE_BGR).
1225      * If 'icm' is non-null we expect its no more than 8 bpp and
1226      * specifically must be a valid DIB sizes : 1, 4 or 8 bpp.
1227      * Then we need to extract the colours into a byte array of the
1228      * format required by GDI which is an array of 'RGBQUAD'
1229      * RGBQUAD looks like :
1230      * typedef struct tagRGBQUAD {
1231      *    BYTE    rgbBlue;
1232      *    BYTE    rgbGreen;
1233      *    BYTE    rgbRed;
1234      *    BYTE    rgbReserved; // must be zero.
1235      * } RGBQUAD;
1236      * There's no alignment problem as GDI expects this to be packed
1237      * and each struct will start on a 4 byte boundary anyway.
1238      */
1239     protected void drawDIBImage(byte[] image,
1240                                 float destX, float destY,
1241                                 float destWidth, float destHeight,
1242                                 float srcX, float srcY,
1243                                 float srcWidth, float srcHeight,
1244                                 int sampleBitsPerPixel,
1245                                 IndexColorModel icm) {
1246         int bitCount = 24;
1247         byte[] bmiColors = null;
1248 
1249         if (icm != null) {
1250             bitCount = sampleBitsPerPixel;
1251             bmiColors = new byte[(1<<icm.getPixelSize())*4];
1252             for (int i=0;i<icm.getMapSize(); i++) {
1253                 bmiColors[i*4+0]=(byte)(icm.getBlue(i)&0xff);
1254                 bmiColors[i*4+1]=(byte)(icm.getGreen(i)&0xff);
1255                 bmiColors[i*4+2]=(byte)(icm.getRed(i)&0xff);
1256             }
1257         }
1258 
1259         drawDIBImage(getPrintDC(), image,
1260                      destX, destY,
1261                      destWidth, destHeight,
1262                      srcX, srcY,
1263                      srcWidth, srcHeight,
1264                      bitCount, bmiColors);
1265     }
1266 
1267     /**
1268      * Begin a new page.
1269      */
1270     protected void startPage(PageFormat format, Printable painter,
1271                              int index, boolean paperChanged) {
1272 
1273         /* Invalidate any device state caches we are
1274          * maintaining. Win95/98 resets the device
1275          * context attributes to default values at
1276          * the start of each page.
1277          */
1278         invalidateCachedState();
1279 
1280         deviceStartPage(format, painter, index, paperChanged);
1281     }
1282 
1283     /**
1284      * End a page.
1285      */
1286     protected void endPage(PageFormat format, Printable painter,
1287                            int index) {
1288 
1289         deviceEndPage(format, painter, index);
1290     }
1291 
1292     /**
1293      * Forget any device state we may have cached.
1294      */
1295     private void invalidateCachedState() {
1296         mLastColor = null;
1297         mLastTextColor = null;
1298         mLastFontFamily = null;
1299     }
1300 
1301     private boolean defaultCopies = true;
1302     /**
1303      * Set the number of copies to be printed.
1304      */
1305     public void setCopies(int copies) {
1306         super.setCopies(copies);
1307         defaultCopies = false;
1308         mAttCopies = copies;
1309         setNativeCopies(copies);
1310     }
1311 
1312 
1313  /* Native Methods */
1314 
1315     /**
1316      * Set copies in device.
1317      */
1318     public native void setNativeCopies(int copies);
1319 
1320     /**
1321      * Displays the print dialog and records the user's settings
1322      * into this object. Return false if the user cancels the
1323      * dialog.
1324      * If the dialog is to use a set of attributes, useAttributes is true.
1325      */
1326     private native boolean jobSetup(Pageable doc, boolean allowPrintToFile);
1327 
1328     /* Make sure printer DC is intialised and that info about the printer
1329      * is reflected back up to Java code
1330      */
1331     protected native void initPrinter();
1332 
1333     /**
1334      * Call Window's StartDoc routine to begin a
1335      * print job. The DC from the print dialog is
1336      * used. If the print dialog was not displayed
1337      * then a DC for the default printer is created.
1338      * The native StartDoc returns false if the end-user cancelled
1339      * printing. This is possible if the printer is connected to FILE:
1340      * in which case windows queries the user for a destination and the
1341      * user may cancel out of it. Note that the implementation of
1342      * cancel() throws PrinterAbortException to indicate the user cancelled.
1343      */
1344     private native boolean _startDoc(String dest, String jobName)
1345                                      throws PrinterException;
1346     protected void startDoc() throws PrinterException {
1347         if (!_startDoc(mDestination, getJobName())) {
1348             cancel();
1349         }
1350     }
1351 
1352     /**
1353      * Call Window's EndDoc routine to end a
1354      * print job.
1355      */
1356     protected native void endDoc();
1357 
1358     /**
1359      * Call Window's AbortDoc routine to abort a
1360      * print job.
1361      */
1362     protected native void abortDoc();
1363 
1364     /**
1365      * Call Windows native resource freeing APIs
1366      */
1367     private static native void deleteDC(long dc, long devmode, long devnames);
1368 
1369     /**
1370      * Begin a new page. This call's Window's
1371      * StartPage routine.
1372      */
1373     protected native void deviceStartPage(PageFormat format, Printable painter,
1374                                           int index, boolean paperChanged);
1375     /**
1376      * End a page. This call's Window's EndPage
1377      * routine.
1378      */
1379     protected native void deviceEndPage(PageFormat format, Printable painter,
1380                                         int index);
1381 
1382     /**
1383      * Prints the contents of the array of ints, 'data'
1384      * to the current page. The band is placed at the
1385      * location (x, y) in device coordinates on the
1386      * page. The width and height of the band is
1387      * specified by the caller.
1388      */
1389     protected native void printBand(byte[] data, int x, int y,
1390                                     int width, int height);
1391 
1392     /**
1393      * Begin a Window's rendering path in the device
1394      * context <code>printDC</code>.
1395      */
1396     protected native void beginPath(long printDC);
1397 
1398     /**
1399      * End a Window's rendering path in the device
1400      * context <code>printDC</code>.
1401      */
1402     protected native void endPath(long printDC);
1403 
1404     /**
1405      * Close a subpath in a Window's rendering path in the device
1406      * context <code>printDC</code>.
1407      */
1408     protected native void closeFigure(long printDC);
1409 
1410     /**
1411      * Fill a defined Window's rendering path in the device
1412      * context <code>printDC</code>.
1413      */
1414     protected native void fillPath(long printDC);
1415 
1416     /**
1417      * Move the Window's pen position to <code>(x,y)</code>
1418      * in the device context <code>printDC</code>.
1419      */
1420     protected native void moveTo(long printDC, float x, float y);
1421 
1422     /**
1423      * Draw a line from the current pen position to
1424      * <code>(x,y)</code> in the device context <code>printDC</code>.
1425      */
1426     protected native void lineTo(long printDC, float x, float y);
1427 
1428     protected native void polyBezierTo(long printDC,
1429                                        float control1x, float control1y,
1430                                        float control2x, float control2y,
1431                                        float endX, float endY);
1432 
1433     /**
1434      * Set the current polgon fill rule into the device context
1435      * <code>printDC</code>. The <code>fillRule</code> should
1436      * be one of the following Windows constants:
1437      * <code>ALTERNATE</code> or <code>WINDING</code>.
1438      */
1439     protected native void setPolyFillMode(long printDC, int fillRule);
1440 
1441     /**
1442      * Create a Window's solid brush for the color specified
1443      * by <code>(red, green, blue)</code>. Once the brush
1444      * is created, select it in the device
1445      * context <code>printDC</code> and free the old brush.
1446      */
1447     protected native void selectSolidBrush(long printDC,
1448                                            int red, int green, int blue);
1449 
1450     /**
1451      * Return the x coordinate of the current pen
1452      * position in the device context
1453      * <code>printDC</code>.
1454      */
1455     protected native int getPenX(long printDC);
1456 
1457     /**
1458      * Return the y coordinate of the current pen
1459      * position in the device context
1460      * <code>printDC</code>.
1461      */
1462     protected native int getPenY(long printDC);
1463 
1464     /**
1465      * Select the device context's current path
1466      * to be the clipping path.
1467      */
1468     protected native void selectClipPath(long printDC);
1469 
1470     /**
1471      * Draw a rectangle using specified brush.
1472      */
1473     protected native void frameRect(long printDC, float x, float y,
1474                                     float width, float height);
1475 
1476     /**
1477      * Fill a rectangle specified by the coordinates using
1478      * specified brush.
1479      */
1480     protected native void fillRect(long printDC, float x, float y,
1481                                    float width, float height,
1482                                    int red, int green, int blue);
1483 
1484     /**
1485      * Create a solid brush using the RG & B colors and width.
1486      * Select this brush and delete the old one.
1487      */
1488     protected native void selectPen(long printDC, float width,
1489                                     int red, int green, int blue);
1490 
1491     /**
1492      * Create a solid brush using the RG & B colors and specified
1493      * pen styles.  Select this created brush and delete the old one.
1494      */
1495     protected native boolean selectStylePen(long printDC, long cap,
1496                                             long join, float width,
1497                                             int red, int green, int blue);
1498 
1499     /**
1500      * Set a GDI font capable of drawing the java Font
1501      * passed in.
1502      */
1503     protected native boolean setFont(long printDC, String familyName,
1504                                      float fontSize,
1505                                      boolean bold,
1506                                      boolean italic,
1507                                      int rotation,
1508                                      float awScale);
1509 
1510 
1511     /**
1512      * Set the GDI color for text drawing.
1513      */
1514     protected native void setTextColor(long printDC,
1515                                        int red, int green, int blue);
1516 
1517 
1518     /**
1519      * Draw the string <code>text</code> into the device
1520      * context <code>printDC</code> at the specified
1521      * position.
1522      */
1523     protected native void textOut(long printDC, String text,
1524                                   int strlen, boolean glyphs,
1525                                   float x, float y, float[] positions);
1526 
1527 
1528     private native int getGDIAdvance(long printDC, String text);
1529 
1530      /**
1531      * Draw the DIB compatible image buffer represented by
1532      * <code>image</code> to the GDI device context
1533      * <code>printDC</code>. The image is drawn at
1534      * <code>(destX, destY)</code> in device coordinates.
1535      * The image is scaled into a square of size
1536      * specified by <code>destWidth</code> and
1537      * <code>destHeight</code>. The portion of the
1538      * source image copied into that square is specified
1539      * by <code>srcX</code>, <code>srcY</code>,
1540      * <code>srcWidth</code>, and srcHeight.
1541      * Note that the image isn't completely compatible with DIB format.
1542      * At the very least it needs to be padded so each scanline is
1543      * DWORD aligned. Also we "flip" the image to make it a bottom-up DIB.
1544      */
1545     private native void drawDIBImage(long printDC, byte[] image,
1546                                      float destX, float destY,
1547                                      float destWidth, float destHeight,
1548                                      float srcX, float srcY,
1549                                      float srcWidth, float srcHeight,
1550                                      int bitCount, byte[] bmiColors);
1551 
1552 
1553     //** BEGIN Functions called by native code for querying/updating attributes
1554 
1555     private final String getPrinterAttrib() {
1556         // getPrintService will get current print service or default if none
1557         PrintService service = this.getPrintService();
1558         String name = (service != null) ? service.getName() : null;
1559         return name;
1560     }
1561 
1562     /* SheetCollate */
1563     private final int getCollateAttrib() {
1564         // -1 means unset, 0 uncollated, 1 collated.
1565         return mAttCollate;
1566     }
1567 
1568     private void setCollateAttrib(Attribute attr) {
1569         if (attr == SheetCollate.COLLATED) {
1570             mAttCollate = 1; // DMCOLLATE_TRUE
1571         } else {
1572             mAttCollate = 0; // DMCOLLATE_FALSE
1573         }
1574     }
1575 
1576     private void setCollateAttrib(Attribute attr,
1577                                   PrintRequestAttributeSet set) {
1578         setCollateAttrib(attr);
1579         set.add(attr);
1580     }
1581 
1582     /* Orientation */
1583 
1584     private final int getOrientAttrib() {
1585         int orient = PageFormat.PORTRAIT;
1586         OrientationRequested orientReq = (attributes == null) ? null :
1587             (OrientationRequested)attributes.get(OrientationRequested.class);
1588         if (orientReq == null) {
1589             orientReq = (OrientationRequested)
1590                myService.getDefaultAttributeValue(OrientationRequested.class);
1591         }
1592         if (orientReq != null) {
1593             if (orientReq == OrientationRequested.REVERSE_LANDSCAPE) {
1594                 orient = PageFormat.REVERSE_LANDSCAPE;
1595             } else if (orientReq == OrientationRequested.LANDSCAPE) {
1596                 orient = PageFormat.LANDSCAPE;
1597             }
1598         }
1599 
1600         return orient;
1601     }
1602 
1603     private void setOrientAttrib(Attribute attr,
1604                                  PrintRequestAttributeSet set) {
1605         if (set != null) {
1606             set.add(attr);
1607         }
1608     }
1609 
1610     /* Copies and Page Range. */
1611     private final int getCopiesAttrib() {
1612         if (defaultCopies) {
1613             return 0;
1614         } else {
1615             return getCopiesInt();
1616         }
1617      }
1618 
1619     private final void setRangeCopiesAttribute(int from, int to,
1620                                                boolean isRangeSet,
1621                                                int copies) {
1622         if (attributes != null) {
1623             if (isRangeSet) {
1624                 attributes.add(new PageRanges(from, to));
1625                 setPageRange(from, to);
1626             }
1627             defaultCopies = false;
1628             attributes.add(new Copies(copies));
1629             /* Since this is called from native to tell Java to sync
1630              * up with native, we don't call this class's own setCopies()
1631              * method which is mainly to send the value down to native
1632              */
1633             super.setCopies(copies);
1634             mAttCopies = copies;
1635         }
1636     }
1637 
1638 
1639 
1640     private final boolean getDestAttrib() {
1641         return (mDestination != null);
1642     }
1643 
1644     /* Quality */
1645     private final int getQualityAttrib() {
1646         return mAttQuality;
1647     }
1648 
1649     private void setQualityAttrib(Attribute attr) {
1650         if (attr == PrintQuality.HIGH) {
1651             mAttQuality = -4; // DMRES_HIGH
1652         } else if (attr == PrintQuality.NORMAL) {
1653             mAttQuality = -3; // DMRES_MEDIUM
1654         } else {
1655             mAttQuality = -2; // DMRES_LOW
1656         }
1657     }
1658 
1659     private void setQualityAttrib(Attribute attr,
1660                                   PrintRequestAttributeSet set) {
1661         setQualityAttrib(attr);
1662         set.add(attr);
1663     }
1664 
1665     /* Color/Chromaticity */
1666     private final int getColorAttrib() {
1667         return mAttChromaticity;
1668     }
1669 
1670     private void setColorAttrib(Attribute attr) {
1671         if (attr == Chromaticity.COLOR) {
1672             mAttChromaticity = 2; // DMCOLOR_COLOR
1673         } else {
1674             mAttChromaticity = 1; // DMCOLOR_MONOCHROME
1675         }
1676     }
1677 
1678     private void setColorAttrib(Attribute attr,
1679                                   PrintRequestAttributeSet set) {
1680         setColorAttrib(attr);
1681         set.add(attr);
1682     }
1683 
1684     /* Sides */
1685     private final int getSidesAttrib() {
1686         return mAttSides;
1687     }
1688 
1689     private void setSidesAttrib(Attribute attr) {
1690         if (attr == Sides.TWO_SIDED_LONG_EDGE) {
1691             mAttSides = 2; // DMDUP_VERTICAL
1692         } else if (attr == Sides.TWO_SIDED_SHORT_EDGE) {
1693             mAttSides = 3; // DMDUP_HORIZONTAL
1694         } else { // Sides.ONE_SIDED
1695             mAttSides = 1;
1696         }
1697     }
1698 
1699     private void setSidesAttrib(Attribute attr,
1700                                 PrintRequestAttributeSet set) {
1701         setSidesAttrib(attr);
1702         set.add(attr);
1703     }
1704 
1705     /** MediaSizeName / dmPaper */
1706     private final int[] getWin32MediaAttrib() {
1707         int wid_ht[] = {0, 0};
1708         if (attributes != null) {
1709             Media media = (Media)attributes.get(Media.class);
1710             if (media instanceof MediaSizeName) {
1711                 MediaSizeName msn = (MediaSizeName)media;
1712                 MediaSize ms = MediaSize.getMediaSizeForName(msn);
1713                 if (ms != null) {
1714                     wid_ht[0] = (int)(ms.getX(MediaSize.INCH) * 72.0);
1715                     wid_ht[1] = (int)(ms.getY(MediaSize.INCH) * 72.0);
1716                 }
1717             }
1718         }
1719         return wid_ht;
1720     }
1721 
1722     private void setWin32MediaAttrib(Attribute attr) {
1723         if (!(attr instanceof MediaSizeName)) {
1724             return;
1725         }
1726         MediaSizeName msn = (MediaSizeName)attr;
1727         mAttMediaSizeName = ((Win32PrintService)myService).findPaperID(msn);
1728     }
1729 
1730     private void addPaperSize(PrintRequestAttributeSet aset,
1731                               int dmIndex, int width, int length) {
1732 
1733         if (aset == null) {
1734             return;
1735         }
1736         MediaSizeName msn =
1737            ((Win32PrintService)myService).findWin32Media(dmIndex);
1738         if (msn == null) {
1739             msn = ((Win32PrintService)myService).
1740                 findMatchingMediaSizeNameMM((float)width, (float)length);
1741         }
1742 
1743         if (msn != null) {
1744             aset.add(msn);
1745         }
1746     }
1747 
1748     private void setWin32MediaAttrib(int dmIndex, int width, int length) {
1749         addPaperSize(attributes, dmIndex, width, length);
1750         mAttMediaSizeName = dmIndex;
1751     }
1752 
1753     /* MediaTray / dmTray */
1754     private void setMediaTrayAttrib(Attribute attr) {
1755         if (attr == MediaTray.BOTTOM) {
1756             mAttMediaTray = 2;    // DMBIN_LOWER
1757         } else if (attr == MediaTray.ENVELOPE) {
1758             mAttMediaTray = 5;    // DMBIN_ENVELOPE
1759         } else if (attr == MediaTray.LARGE_CAPACITY) {
1760             mAttMediaTray = 11;      // DMBIN_LARGECAPACITY
1761         } else if (attr == MediaTray.MAIN) {
1762             mAttMediaTray =1;               // DMBIN_UPPER
1763         } else if (attr == MediaTray.MANUAL) {
1764             mAttMediaTray = 4;              // DMBIN_MANUAL
1765         } else if (attr == MediaTray.MIDDLE) {
1766             mAttMediaTray = 3;              // DMBIN_MIDDLE
1767         } else if (attr == MediaTray.SIDE) {
1768             // no equivalent predefined value
1769             mAttMediaTray = 7;              // DMBIN_AUTO
1770         } else if (attr == MediaTray.TOP) {
1771             mAttMediaTray = 1;              // DMBIN_UPPER
1772         } else {
1773             if (attr instanceof Win32MediaTray) {
1774                 mAttMediaTray = ((Win32MediaTray)attr).winID;
1775             } else {
1776                 mAttMediaTray = 1;  // default
1777             }
1778         }
1779     }
1780 
1781     private void setMediaTrayAttrib(int dmBinID) {
1782         mAttMediaTray = dmBinID;
1783         MediaTray tray = ((Win32PrintService)myService).findMediaTray(dmBinID);
1784     }
1785 
1786     private int getMediaTrayAttrib() {
1787         return mAttMediaTray;
1788     }
1789 
1790 
1791 
1792     private final boolean getPrintToFileEnabled() {
1793         SecurityManager security = System.getSecurityManager();
1794         if (security != null) {
1795             FilePermission printToFilePermission =
1796                 new FilePermission("<<ALL FILES>>", "read,write");
1797             try {
1798                 security.checkPermission(printToFilePermission);
1799             } catch (SecurityException e) {
1800                 return false;
1801             }
1802         }
1803         return true;
1804     }
1805 
1806     private final void setNativeAttributes(int flags, int fields, int values) {
1807         if (attributes == null) {
1808             return;
1809         }
1810         if ((flags & PD_PRINTTOFILE) != 0) {
1811             Destination destPrn = (Destination)attributes.get(
1812                                                  Destination.class);
1813             if (destPrn == null) {
1814                 try {
1815                     attributes.add(new Destination(
1816                                                new File("./out.prn").toURI()));
1817                 } catch (SecurityException se) {
1818                     try {
1819                         attributes.add(new Destination(
1820                                                 new URI("file:out.prn")));
1821                     } catch (URISyntaxException e) {
1822                     }
1823                 }
1824             }
1825         } else {
1826             attributes.remove(Destination.class);
1827         }
1828 
1829         if ((flags & PD_COLLATE) != 0) {
1830             setCollateAttrib(SheetCollate.COLLATED, attributes);
1831         } else {
1832             setCollateAttrib(SheetCollate.UNCOLLATED, attributes);
1833         }
1834 
1835         if ((flags & PD_PAGENUMS) != 0) {
1836             attributes.add(SunPageSelection.RANGE);
1837         } else if ((flags & PD_SELECTION) != 0) {
1838             attributes.add(SunPageSelection.SELECTION);
1839         } else {
1840             attributes.add(SunPageSelection.ALL);
1841         }
1842 
1843         if ((fields & DM_ORIENTATION) != 0) {
1844             if ((values & SET_ORIENTATION) != 0) {
1845                 setOrientAttrib(OrientationRequested.LANDSCAPE, attributes);
1846             } else {
1847                 setOrientAttrib(OrientationRequested.PORTRAIT, attributes);
1848             }
1849         }
1850 
1851         if ((fields & DM_COLOR) != 0) {
1852             if ((values & SET_COLOR) != 0) {
1853                 setColorAttrib(Chromaticity.COLOR, attributes);
1854             } else {
1855                 setColorAttrib(Chromaticity.MONOCHROME, attributes);
1856             }
1857         }
1858 
1859         if ((fields & DM_PRINTQUALITY) != 0) {
1860             PrintQuality quality;
1861             if ((values & SET_RES_LOW) != 0) {
1862                 quality = PrintQuality.DRAFT;
1863             } else if ((fields & SET_RES_HIGH) != 0) {
1864                 quality = PrintQuality.HIGH;
1865             } else {
1866                 quality = PrintQuality.NORMAL;
1867             }
1868             setQualityAttrib(quality, attributes);
1869         }
1870 
1871         if ((fields & DM_DUPLEX) != 0) {
1872             Sides sides;
1873             if ((values & SET_DUP_VERTICAL) != 0) {
1874                 sides = Sides.TWO_SIDED_LONG_EDGE;
1875             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
1876                 sides = Sides.TWO_SIDED_SHORT_EDGE;
1877             } else {
1878                 sides = Sides.ONE_SIDED;
1879             }
1880             setSidesAttrib(sides, attributes);
1881         }
1882     }
1883 
1884     private static final class DevModeValues {
1885         int dmFields;
1886         short copies;
1887         short collate;
1888         short color;
1889         short duplex;
1890         short orient;
1891         short paper;
1892         short bin;
1893         short xres_quality;
1894         short yres;
1895     }
1896 
1897     private void getDevModeValues(PrintRequestAttributeSet aset,
1898                                   DevModeValues info) {
1899 
1900         Copies c = (Copies)aset.get(Copies.class);
1901         if (c != null) {
1902             info.dmFields |= DM_COPIES;
1903             info.copies = (short)c.getValue();
1904         }
1905 
1906         SheetCollate sc = (SheetCollate)aset.get(SheetCollate.class);
1907         if (sc != null) {
1908             info.dmFields |= DM_COLLATE;
1909             info.collate = (sc == SheetCollate.COLLATED) ?
1910                 DMCOLLATE_TRUE : DMCOLLATE_FALSE;
1911         }
1912 
1913         Chromaticity ch = (Chromaticity)aset.get(Chromaticity.class);
1914         if (ch != null) {
1915             info.dmFields |= DM_COLOR;
1916             if (ch == Chromaticity.COLOR) {
1917                 info.color = DMCOLOR_COLOR;
1918             } else {
1919                 info.color = DMCOLOR_MONOCHROME;
1920             }
1921         }
1922 
1923         Sides s = (Sides)aset.get(Sides.class);
1924         if (s != null) {
1925             info.dmFields |= DM_DUPLEX;
1926             if (s == Sides.TWO_SIDED_LONG_EDGE) {
1927                 info.duplex = DMDUP_VERTICAL;
1928             } else if (s == Sides.TWO_SIDED_SHORT_EDGE) {
1929                 info.duplex = DMDUP_HORIZONTAL;
1930             } else { // Sides.ONE_SIDED
1931                 info.duplex = DMDUP_SIMPLEX;
1932             }
1933         }
1934 
1935         OrientationRequested or =
1936             (OrientationRequested)aset.get(OrientationRequested.class);
1937         if (or != null) {
1938             info.dmFields |= DM_ORIENTATION;
1939             info.orient = (or == OrientationRequested.LANDSCAPE)
1940                 ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
1941         }
1942 
1943         Media m = (Media)aset.get(Media.class);
1944         if (m instanceof MediaSizeName) {
1945             info.dmFields |= DM_PAPERSIZE;
1946             MediaSizeName msn = (MediaSizeName)m;
1947             info.paper =
1948                 (short)((Win32PrintService)myService).findPaperID(msn);
1949         }
1950 
1951         MediaTray mt = null;
1952         if (m instanceof MediaTray) {
1953             mt = (MediaTray)m;
1954         }
1955         if (mt == null) {
1956             SunAlternateMedia sam =
1957                 (SunAlternateMedia)aset.get(SunAlternateMedia.class);
1958             if (sam != null && (sam.getMedia() instanceof MediaTray)) {
1959                 mt = (MediaTray)sam.getMedia();
1960             }
1961         }
1962 
1963         if (mt != null) {
1964             info.dmFields |= DM_DEFAULTSOURCE;
1965             info.bin = (short)(((Win32PrintService)myService).findTrayID(mt));
1966         }
1967 
1968         PrintQuality q = (PrintQuality)aset.get(PrintQuality.class);
1969         if (q != null) {
1970             info.dmFields |= DM_PRINTQUALITY;
1971             if (q == PrintQuality.DRAFT) {
1972                 info.xres_quality = DMRES_DRAFT;
1973             } else if (q == PrintQuality.HIGH) {
1974                 info.xres_quality = DMRES_HIGH;
1975             } else {
1976                 info.xres_quality = DMRES_MEDIUM;
1977             }
1978         }
1979 
1980         PrinterResolution r =
1981             (PrinterResolution)aset.get(PrinterResolution.class);
1982         if (r != null) {
1983             info.dmFields |= DM_PRINTQUALITY | DM_YRESOLUTION;
1984             info.xres_quality =
1985                 (short)r.getCrossFeedResolution(PrinterResolution.DPI);
1986             info.yres = (short)r.getFeedResolution(PrinterResolution.DPI);
1987         }
1988     }
1989 
1990     /* This method is called from native to update the values in the
1991      * attribute set which originates from the cross-platform dialog,
1992      * but updated by the native DocumentPropertiesUI which updates the
1993      * devmode. This syncs the devmode back in to the attributes so that
1994      * we can update the cross-platform dialog.
1995      * The attribute set here is a temporary one installed whilst this
1996      * happens,
1997      */
1998     private final void setJobAttributes(PrintRequestAttributeSet attributes,
1999                                         int fields, int values,
2000                                         short copies,
2001                                         short dmPaperSize,
2002                                         short dmPaperWidth,
2003                                         short dmPaperLength,
2004                                         short dmDefaultSource,
2005                                         short xRes,
2006                                         short yRes) {
2007 
2008         if (attributes == null) {
2009             return;
2010         }
2011 
2012         if ((fields & DM_COPIES) != 0) {
2013             attributes.add(new Copies(copies));
2014         }
2015 
2016         if ((fields & DM_COLLATE) != 0) {
2017             if ((values & SET_COLLATED) != 0) {
2018                 attributes.add(SheetCollate.COLLATED);
2019             } else {
2020                 attributes.add(SheetCollate.UNCOLLATED);
2021             }
2022         }
2023 
2024         if ((fields & DM_ORIENTATION) != 0) {
2025             if ((values & SET_ORIENTATION) != 0) {
2026                 attributes.add(OrientationRequested.LANDSCAPE);
2027             } else {
2028                 attributes.add(OrientationRequested.PORTRAIT);
2029             }
2030         }
2031 
2032         if ((fields & DM_COLOR) != 0) {
2033             if ((values & SET_COLOR) != 0) {
2034                 attributes.add(Chromaticity.COLOR);
2035             } else {
2036                 attributes.add(Chromaticity.MONOCHROME);
2037             }
2038         }
2039 
2040         if ((fields & DM_PRINTQUALITY) != 0) {
2041             /* value < 0 indicates quality setting.
2042              * value > 0 indicates X resolution. In that case
2043              * hopefully we will also find y-resolution specified.
2044              * If its not, assume its the same as x-res.
2045              * Maybe Java code should try to reconcile this against
2046              * the printers claimed set of supported resolutions.
2047              */
2048             if (xRes < 0) {
2049                 PrintQuality quality;
2050                 if ((values & SET_RES_LOW) != 0) {
2051                     quality = PrintQuality.DRAFT;
2052                 } else if ((fields & SET_RES_HIGH) != 0) {
2053                     quality = PrintQuality.HIGH;
2054                 } else {
2055                     quality = PrintQuality.NORMAL;
2056                 }
2057                 attributes.add(quality);
2058             } else if (xRes > 0 && yRes > 0) {
2059                 attributes.add(
2060                     new PrinterResolution(xRes, yRes, PrinterResolution.DPI));
2061             }
2062         }
2063 
2064         if ((fields & DM_DUPLEX) != 0) {
2065             Sides sides;
2066             if ((values & SET_DUP_VERTICAL) != 0) {
2067                 sides = Sides.TWO_SIDED_LONG_EDGE;
2068             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
2069                 sides = Sides.TWO_SIDED_SHORT_EDGE;
2070             } else {
2071                 sides = Sides.ONE_SIDED;
2072             }
2073             attributes.add(sides);
2074         }
2075 
2076         if ((fields & DM_PAPERSIZE) != 0) {
2077             addPaperSize(attributes, dmPaperSize, dmPaperWidth, dmPaperLength);
2078         }
2079 
2080         if ((fields & DM_DEFAULTSOURCE) != 0) {
2081             MediaTray tray =
2082                 ((Win32PrintService)myService).findMediaTray(dmDefaultSource);
2083             attributes.add(new SunAlternateMedia(tray));
2084         }
2085     }
2086 
2087     private native boolean showDocProperties(long hWnd,
2088                                              PrintRequestAttributeSet aset,
2089                                              int dmFields,
2090                                              short copies,
2091                                              short collate,
2092                                              short color,
2093                                              short duplex,
2094                                              short orient,
2095                                              short paper,
2096                                              short bin,
2097                                              short xres_quality,
2098                                              short yres);
2099 
2100     @SuppressWarnings("deprecation")
2101     public PrintRequestAttributeSet
2102         showDocumentProperties(Window owner,
2103                                PrintService service,
2104                                PrintRequestAttributeSet aset)
2105     {
2106         try {
2107             setNativePrintServiceIfNeeded(service.getName());
2108         } catch (PrinterException e) {
2109         }
2110         long hWnd = ((WWindowPeer)(owner.getPeer())).getHWnd();
2111         DevModeValues info = new DevModeValues();
2112         getDevModeValues(aset, info);
2113         boolean ok =
2114             showDocProperties(hWnd, aset,
2115                               info.dmFields,
2116                               info.copies,
2117                               info.collate,
2118                               info.color,
2119                               info.duplex,
2120                               info.orient,
2121                               info.paper,
2122                               info.bin,
2123                               info.xres_quality,
2124                               info.yres);
2125 
2126         if (ok) {
2127             return aset;
2128         } else {
2129             return null;
2130         }
2131     }
2132 
2133     /* Printer Resolution. See also getXRes() and getYRes() */
2134     private final void setResolutionDPI(int xres, int yres) {
2135         if (attributes != null) {
2136             PrinterResolution res =
2137                 new PrinterResolution(xres, yres, PrinterResolution.DPI);
2138             attributes.add(res);
2139         }
2140         mAttXRes = xres;
2141         mAttYRes = yres;
2142     }
2143 
2144     private void setResolutionAttrib(Attribute attr) {
2145         PrinterResolution pr = (PrinterResolution)attr;
2146         mAttXRes = pr.getCrossFeedResolution(PrinterResolution.DPI);
2147         mAttYRes = pr.getFeedResolution(PrinterResolution.DPI);
2148     }
2149 
2150     private void setPrinterNameAttrib(String printerName) {
2151         PrintService service = this.getPrintService();
2152 
2153         if (printerName == null) {
2154             return;
2155         }
2156 
2157         if (service != null && printerName.equals(service.getName())) {
2158             return;
2159         } else {
2160             PrintService []services = PrinterJob.lookupPrintServices();
2161             for (int i=0; i<services.length; i++) {
2162                 if (printerName.equals(services[i].getName())) {
2163 
2164                     try {
2165                         this.setPrintService(services[i]);
2166                     } catch (PrinterException e) {
2167                     }
2168                     return;
2169                 }
2170             }
2171         }
2172     //** END Functions called by native code for querying/updating attributes
2173 
2174     }
2175 
2176 class PrintToFileErrorDialog extends Dialog implements ActionListener{
2177     public PrintToFileErrorDialog(Frame parent, String title, String message,
2178                            String buttonText) {
2179         super(parent, title, true);
2180         init (parent, title, message, buttonText);
2181     }
2182 
2183     public PrintToFileErrorDialog(Dialog parent, String title, String message,
2184                            String buttonText) {
2185         super(parent, title, true);
2186         init (parent, title, message, buttonText);
2187     }
2188 
2189     private void init(Component parent, String  title, String message,
2190                       String buttonText) {
2191         Panel p = new Panel();
2192         add("Center", new Label(message));
2193         Button btn = new Button(buttonText);
2194         btn.addActionListener(this);
2195         p.add(btn);
2196         add("South", p);
2197         pack();
2198 
2199         Dimension dDim = getSize();
2200         if (parent != null) {
2201             Rectangle fRect = parent.getBounds();
2202             setLocation(fRect.x + ((fRect.width - dDim.width) / 2),
2203                         fRect.y + ((fRect.height - dDim.height) / 2));
2204         }
2205     }
2206 
2207     public void actionPerformed(ActionEvent event) {
2208         setVisible(false);
2209         dispose();
2210         return;
2211     }
2212 }
2213 
2214 
2215 
2216 
2217     /**
2218      * Initialize JNI field and method ids
2219      */
2220     private static native void initIDs();
2221 
2222 }