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