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 
  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.Toolkit;
  34 import java.awt.BasicStroke;
  35 import java.awt.Button;
  36 import java.awt.Component;
  37 import java.awt.Dimension;
  38 import java.awt.event.ActionEvent;
  39 import java.awt.event.ActionListener;
  40 import java.awt.FileDialog;
  41 import java.awt.Dialog;
  42 import java.awt.Label;
  43 import java.awt.Panel;
  44 import java.awt.Rectangle;
  45 import java.awt.Window;
  46 
  47 import java.awt.image.BufferedImage;
  48 import java.awt.image.IndexColorModel;
  49 
  50 import java.awt.print.Pageable;
  51 import java.awt.print.PageFormat;
  52 import java.awt.print.Paper;
  53 import java.awt.print.Printable;
  54 import java.awt.print.PrinterJob;
  55 import java.awt.print.PrinterException;
  56 import javax.print.PrintService;
  57 
  58 import java.io.File;
  59 
  60 import java.util.MissingResourceException;
  61 import java.util.ResourceBundle;
  62 
  63 import sun.awt.AWTAccessor;
  64 import sun.awt.AWTAccessor.ComponentAccessor;
  65 import sun.print.PeekGraphics;
  66 import sun.print.PeekMetrics;
  67 
  68 import java.net.URI;
  69 import java.net.URISyntaxException;
  70 
  71 import javax.print.PrintServiceLookup;
  72 import javax.print.attribute.PrintRequestAttributeSet;
  73 import javax.print.attribute.HashPrintRequestAttributeSet;
  74 import javax.print.attribute.Attribute;
  75 import javax.print.attribute.standard.Sides;
  76 import javax.print.attribute.standard.Chromaticity;
  77 import javax.print.attribute.standard.PrintQuality;
  78 import javax.print.attribute.standard.PrinterResolution;
  79 import javax.print.attribute.standard.SheetCollate;
  80 import javax.print.attribute.standard.Copies;
  81 import javax.print.attribute.standard.Destination;
  82 import javax.print.attribute.standard.OrientationRequested;
  83 import javax.print.attribute.standard.Media;
  84 import javax.print.attribute.standard.MediaSizeName;
  85 import javax.print.attribute.standard.MediaSize;
  86 import javax.print.attribute.standard.MediaTray;
  87 import javax.print.attribute.standard.PageRanges;
  88 
  89 import sun.awt.Win32FontManager;
  90 
  91 import sun.print.RasterPrinterJob;
  92 import sun.print.SunAlternateMedia;
  93 import sun.print.SunPageSelection;
  94 import sun.print.Win32MediaTray;
  95 import sun.print.Win32PrintService;
  96 import sun.print.PrintServiceLookupProvider;
  97 import sun.print.ServiceDialog;
  98 import sun.print.DialogOwner;
  99 
 100 import java.awt.Frame;
 101 import java.io.FilePermission;
 102 
 103 import sun.java2d.Disposer;
 104 import sun.java2d.DisposerRecord;
 105 import sun.java2d.DisposerTarget;
 106 
 107 /**
 108  * A class which initiates and executes a Win32 printer job.
 109  *
 110  * @author Richard Blanchard
 111  */
 112 public 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
2210 class 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 }