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 ownerFrame = (dlgOwner != null) ? dlgOwner.getOwner() : null;
 482 
 483         WPrintDialog dialog;
 484         if (ownerFrame instanceof Frame) {
 485             dialog = new WPrintDialog((Frame)ownerFrame, this);
 486         } else {
 487             dialog = new WPrintDialog((Dialog)ownerFrame, this);
 488         }
 489         dialog.setRetVal(false);
 490         dialog.setVisible(true);
 491         boolean prv = dialog.getRetVal();
 492         dialog.dispose();
 493 
 494         Destination dest =
 495                 (Destination)attributes.get(Destination.class);
 496         if ((dest == null) || !prv){
 497                 return prv;
 498         } else {
 499             String title = null;
 500             String strBundle = "sun.print.resources.serviceui";
 501             ResourceBundle rb = ResourceBundle.getBundle(strBundle);
 502             try {
 503                 title = rb.getString("dialog.printtofile");
 504             } catch (MissingResourceException e) {
 505             }
 506             FileDialog fileDialog;
 507             if (ownerFrame instanceof Frame) {
 508                 fileDialog = new FileDialog((Frame)ownerFrame, title,
 509                                                    FileDialog.SAVE);
 510             } else {
 511                 fileDialog = new FileDialog((Dialog)ownerFrame, title,
 512                                              FileDialog.SAVE);    
 513             }
 514 
 515             URI destURI = dest.getURI();
 516             // Old code destURI.getPath() would return null for "file:out.prn"
 517             // so we use getSchemeSpecificPart instead.
 518             String pathName = (destURI != null) ?
 519                 destURI.getSchemeSpecificPart() : null;
 520             if (pathName != null) {
 521                File file = new File(pathName);
 522                fileDialog.setFile(file.getName());
 523                File parent = file.getParentFile();
 524                if (parent != null) {
 525                    fileDialog.setDirectory(parent.getPath());
 526                }
 527             } else {
 528                 fileDialog.setFile("out.prn");
 529             }
 530 
 531             fileDialog.setVisible(true);
 532             String fileName = fileDialog.getFile();
 533             if (fileName == null) {
 534                 fileDialog.dispose();
 535                 return false;
 536             }
 537             String fullName = fileDialog.getDirectory() + fileName;
 538             File f = new File(fullName);
 539             File pFile = f.getParentFile();
 540             while ((f.exists() &&
 541                       (!f.isFile() || !f.canWrite())) ||
 542                    ((pFile != null) &&
 543                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
 544 
 545                 if (ownerFrame instanceof Frame) {
 546                     (new PrintToFileErrorDialog((Frame)ownerFrame,
 547                                 ServiceDialog.getMsg("dialog.owtitle"),
 548                                 ServiceDialog.getMsg("dialog.writeerror")+" "+fullName,
 549                                 ServiceDialog.getMsg("button.ok"))).setVisible(true);
 550                 } else {
 551                     (new PrintToFileErrorDialog((Dialog)ownerFrame,
 552                                 ServiceDialog.getMsg("dialog.owtitle"),
 553                                 ServiceDialog.getMsg("dialog.writeerror")+" "+fullName,
 554                                 ServiceDialog.getMsg("button.ok"))).setVisible(true);
 555                 }
 556 
 557                 fileDialog.setVisible(true);
 558                 fileName = fileDialog.getFile();
 559                 if (fileName == null) {
 560                     fileDialog.dispose();
 561                     return false;
 562                 }
 563                 fullName = fileDialog.getDirectory() + fileName;
 564                 f = new File(fullName);
 565                 pFile = f.getParentFile();
 566             }
 567             fileDialog.dispose();
 568             attributes.add(new Destination(f.toURI()));
 569             return true;
 570         }
 571 
 572     }
 573 
 574     /**
 575      * Presents the user a dialog for changing properties of the
 576      * print job interactively.
 577      * @return false if the user cancels the dialog and
 578      *         true otherwise.
 579      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 580      * returns true.
 581      * @see java.awt.GraphicsEnvironment#isHeadless
 582      */
 583     @Override
 584     public boolean printDialog() throws HeadlessException {
 585 
 586         if (GraphicsEnvironment.isHeadless()) {
 587             throw new HeadlessException();
 588         }
 589         // current request attribute set should be reflected to the print dialog.
 590         // If null, create new instance of HashPrintRequestAttributeSet.
 591         if (attributes == null) {
 592             attributes = new HashPrintRequestAttributeSet();
 593         }
 594 
 595         if (!(getPrintService() instanceof Win32PrintService)) {
 596             return super.printDialog(attributes);
 597         }
 598 
 599         if (noDefaultPrinter == true) {
 600             return false;
 601         } else {
 602             return displayNativeDialog();
 603         }
 604     }
 605 
 606      /**
 607      * Associate this PrinterJob with a new PrintService.
 608      *
 609      * Throws {@code PrinterException} if the specified service
 610      * cannot support the {@code Pageable} and
 611      * </code>Printable</code> interfaces necessary to support 2D printing.
 612      * @param service print service which supports 2D printing.
 613      *
 614      * @throws PrinterException if the specified service does not support
 615      * 2D printing.
 616      */
 617     @Override
 618     public void setPrintService(PrintService service)
 619         throws PrinterException {
 620         super.setPrintService(service);
 621         if (!(service instanceof Win32PrintService)) {
 622             return;
 623         }
 624         driverDoesMultipleCopies = false;
 625         driverDoesCollation = false;
 626         setNativePrintServiceIfNeeded(service.getName());
 627     }
 628 
 629     /* associates this job with the specified native service */
 630     private native void setNativePrintService(String name)
 631         throws PrinterException;
 632 
 633     private String lastNativeService = null;
 634     private void setNativePrintServiceIfNeeded(String name)
 635         throws PrinterException {
 636 
 637         if (name != null && !(name.equals(lastNativeService))) {
 638             setNativePrintService(name);
 639             lastNativeService = name;
 640         }
 641     }
 642 
 643     @Override
 644     public PrintService getPrintService() {
 645         if (myService == null) {
 646             String printerName = getNativePrintService();
 647 
 648             if (printerName != null) {
 649                 myService = PrintServiceLookupProvider.getWin32PrintLUS().
 650                     getPrintServiceByName(printerName);
 651                 // no need to call setNativePrintService as this name is
 652                 // currently set in native
 653                 if (myService != null) {
 654                     return myService;
 655                 }
 656             }
 657 
 658             myService = PrintServiceLookup.lookupDefaultPrintService();
 659             if (myService instanceof Win32PrintService) {
 660                 try {
 661                     setNativePrintServiceIfNeeded(myService.getName());
 662                 } catch (Exception e) {
 663                     myService = null;
 664                 }
 665             }
 666 
 667           }
 668           return myService;
 669     }
 670 
 671     private native String getNativePrintService();
 672 
 673     private void initAttributeMembers() {
 674             mAttSides = 0;
 675             mAttChromaticity = 0;
 676             mAttXRes = 0;
 677             mAttYRes = 0;
 678             mAttQuality = 0;
 679             mAttCollate = -1;
 680             mAttCopies = 0;
 681             mAttMediaTray = 0;
 682             mAttMediaSizeName = 0;
 683             mDestination = null;
 684 
 685     }
 686 
 687     /**
 688      * copy the attributes to the native print job
 689      * Note that this method, and hence the re-initialisation
 690      * of the GDI values is done on each entry to the print dialog since
 691      * an app could redisplay the print dialog for the same job and
 692      * 1) the application may have changed attribute settings
 693      * 2) the application may have changed the printer.
 694      * In the event that the user changes the printer using the
 695       dialog, then it is up to GDI to report back all changed values.
 696      */
 697     @Override
 698     protected void setAttributes(PrintRequestAttributeSet attributes)
 699         throws PrinterException {
 700 
 701         // initialize attribute values
 702         initAttributeMembers();
 703         super.setAttributes(attributes);
 704 
 705         mAttCopies = getCopiesInt();
 706         mDestination = destinationAttr;
 707 
 708         if (attributes == null) {
 709             return; // now always use attributes, so this shouldn't happen.
 710         }
 711         Attribute[] attrs = attributes.toArray();
 712         for (int i=0; i< attrs.length; i++) {
 713             Attribute attr = attrs[i];
 714             try {
 715                  if (attr.getCategory() == Sides.class) {
 716                     setSidesAttrib(attr);
 717                 }
 718                 else if (attr.getCategory() == Chromaticity.class) {
 719                     setColorAttrib(attr);
 720                 }
 721                 else if (attr.getCategory() == PrinterResolution.class) {
 722                     if (myService.isAttributeValueSupported(attr, null, null)) {
 723                         setResolutionAttrib(attr);
 724                     }
 725                 }
 726                 else if (attr.getCategory() == PrintQuality.class) {
 727                     setQualityAttrib(attr);
 728                 }
 729                 else if (attr.getCategory() == SheetCollate.class) {
 730                     setCollateAttrib(attr);
 731                 }  else if (attr.getCategory() == Media.class ||
 732                             attr.getCategory() == SunAlternateMedia.class) {
 733                     /* SunAlternateMedia is used if its a tray, and
 734                      * any Media that is specified is not a tray.
 735                      */
 736                     if (attr.getCategory() == SunAlternateMedia.class) {
 737                         Media media = (Media)attributes.get(Media.class);
 738                         if (media == null ||
 739                             !(media instanceof MediaTray)) {
 740                             attr = ((SunAlternateMedia)attr).getMedia();
 741                         }
 742                     }
 743                     if (attr instanceof MediaSizeName) {
 744                         setWin32MediaAttrib(attr);
 745                     }
 746                     if (attr instanceof MediaTray) {
 747                         setMediaTrayAttrib(attr);
 748                     }
 749                 }
 750 
 751             } catch (ClassCastException e) {
 752             }
 753         }
 754     }
 755 
 756     /**
 757      * Alters the orientation and Paper to match defaults obtained
 758      * from a printer.
 759      */
 760     private native void getDefaultPage(PageFormat page);
 761 
 762     /**
 763      * The passed in PageFormat will be copied and altered to describe
 764      * the default page size and orientation of the PrinterJob's
 765      * current printer.
 766      * Note: PageFormat.getPaper() returns a clone and getDefaultPage()
 767      * gets that clone so it won't overwrite the original paper.
 768      */
 769     @Override
 770     public PageFormat defaultPage(PageFormat page) {
 771         PageFormat newPage = (PageFormat)page.clone();
 772         getDefaultPage(newPage);
 773         return newPage;
 774     }
 775 
 776     /**
 777      * validate the paper size against the current printer.
 778      */
 779     @Override
 780     protected native void validatePaper(Paper origPaper, Paper newPaper );
 781 
 782     /**
 783      * Examine the metrics captured by the
 784      * {@code PeekGraphics} instance and
 785      * if capable of directly converting this
 786      * print job to the printer's control language
 787      * or the native OS's graphics primitives, then
 788      * return a {@code PathGraphics} to perform
 789      * that conversion. If there is not an object
 790      * capable of the conversion then return
 791      * {@code null}. Returning {@code null}
 792      * causes the print job to be rasterized.
 793      */
 794 
 795     @Override
 796     protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
 797                                             PrinterJob printerJob,
 798                                             Printable painter,
 799                                             PageFormat pageFormat,
 800                                             int pageIndex) {
 801 
 802         WPathGraphics pathGraphics;
 803         PeekMetrics metrics = peekGraphics.getMetrics();
 804 
 805         /* If the application has drawn anything that
 806          * out PathGraphics class can not handle then
 807          * return a null PathGraphics. If the property
 808          * to force the raster pipeline has been set then
 809          * we also want to avoid the path (pdl) pipeline
 810          * and return null.
 811          */
 812        if (forcePDL == false && (forceRaster == true
 813                                   || metrics.hasNonSolidColors()
 814                                   || metrics.hasCompositing()
 815                                   )) {
 816             pathGraphics = null;
 817         } else {
 818             BufferedImage bufferedImage = new BufferedImage(8, 8,
 819                                             BufferedImage.TYPE_INT_RGB);
 820             Graphics2D bufferedGraphics = bufferedImage.createGraphics();
 821 
 822             boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
 823             pathGraphics =  new WPathGraphics(bufferedGraphics, printerJob,
 824                                               painter, pageFormat, pageIndex,
 825                                               canRedraw);
 826         }
 827 
 828         return pathGraphics;
 829     }
 830 
 831 
 832     @Override
 833     protected double getXRes() {
 834         if (mAttXRes != 0) {
 835             return mAttXRes;
 836         } else {
 837             return mPrintXRes;
 838         }
 839     }
 840 
 841     @Override
 842     protected double getYRes() {
 843         if (mAttYRes != 0) {
 844             return mAttYRes;
 845         } else {
 846             return mPrintYRes;
 847         }
 848     }
 849 
 850     @Override
 851     protected double getPhysicalPrintableX(Paper p) {
 852         return mPrintPhysX;
 853     }
 854 
 855     @Override
 856     protected double getPhysicalPrintableY(Paper p) {
 857         return mPrintPhysY;
 858     }
 859 
 860     @Override
 861     protected double getPhysicalPrintableWidth(Paper p) {
 862         return mPrintWidth;
 863     }
 864 
 865     @Override
 866     protected double getPhysicalPrintableHeight(Paper p) {
 867         return mPrintHeight;
 868     }
 869 
 870     @Override
 871     protected double getPhysicalPageWidth(Paper p) {
 872         return mPageWidth;
 873     }
 874 
 875     @Override
 876     protected double getPhysicalPageHeight(Paper p) {
 877         return mPageHeight;
 878     }
 879 
 880     /**
 881      * We don't (yet) provide API to support collation, and
 882      * when we do the logic here will require adjustment, but
 883      * this method is currently necessary to honour user-originated
 884      * collation requests - which can only originate from the print dialog.
 885      * REMIND: check if this can be deleted already.
 886      */
 887     @Override
 888     protected boolean isCollated() {
 889         return userRequestedCollation;
 890     }
 891 
 892     /**
 893      * Returns how many times the entire book should
 894      * be printed by the PrintJob. If the printer
 895      * itself supports collation then this method
 896      * should return 1 indicating that the entire
 897      * book need only be printed once and the copies
 898      * will be collated and made in the printer.
 899      */
 900     @Override
 901     protected int getCollatedCopies() {
 902         debug_println("driverDoesMultipleCopies="+driverDoesMultipleCopies
 903                       +" driverDoesCollation="+driverDoesCollation);
 904         if  (super.isCollated() && !driverDoesCollation) {
 905             // we will do our own collation so we need to
 906             // tell the printer to not collate
 907             mAttCollate = 0;
 908             mAttCopies = 1;
 909             return getCopies();
 910         }
 911 
 912         return 1;
 913     }
 914 
 915     /**
 916      * Returns how many times each page in the book
 917      * should be consecutively printed by PrinterJob.
 918      * If the underlying Window's driver will
 919      * generate the copies, rather than having RasterPrinterJob
 920      * iterate over the number of copies, this method always returns
 921      * 1.
 922      */
 923     @Override
 924     protected int getNoncollatedCopies() {
 925         if (driverDoesMultipleCopies || super.isCollated()) {
 926             return 1;
 927         } else {
 928             return getCopies();
 929         }
 930     }
 931 
 932     /* These getter/setters are called from native code */
 933 
 934     /**
 935      * Return the Window's device context that we are printing
 936      * into.
 937      */
 938     private long getPrintDC() {
 939         return handleRecord.mPrintDC;
 940     }
 941 
 942     private void setPrintDC(long mPrintDC) {
 943         handleRecord.mPrintDC = mPrintDC;
 944     }
 945 
 946     private long getDevMode() {
 947         return handleRecord.mPrintHDevMode;
 948     }
 949 
 950     private void setDevMode(long mPrintHDevMode) {
 951         handleRecord.mPrintHDevMode = mPrintHDevMode;
 952     }
 953 
 954     private long getDevNames() {
 955         return handleRecord.mPrintHDevNames;
 956     }
 957 
 958     private void setDevNames(long mPrintHDevNames) {
 959         handleRecord.mPrintHDevNames = mPrintHDevNames;
 960     }
 961 
 962     protected void beginPath() {
 963         beginPath(getPrintDC());
 964     }
 965 
 966     protected void endPath() {
 967         endPath(getPrintDC());
 968     }
 969 
 970     protected void closeFigure() {
 971         closeFigure(getPrintDC());
 972     }
 973 
 974     protected void fillPath() {
 975         fillPath(getPrintDC());
 976     }
 977 
 978     protected void moveTo(float x, float y) {
 979         moveTo(getPrintDC(), x, y);
 980     }
 981 
 982     protected void lineTo(float x, float y) {
 983         lineTo(getPrintDC(), x, y);
 984     }
 985 
 986     protected void polyBezierTo(float control1x, float control1y,
 987                                 float control2x, float control2y,
 988                                 float endX, float endY) {
 989 
 990         polyBezierTo(getPrintDC(), control1x, control1y,
 991                                control2x, control2y,
 992                                endX, endY);
 993     }
 994 
 995     /**
 996      * Set the current polgon fill rule into the printer device context.
 997      * The {@code fillRule} should
 998      * be one of the following Windows constants:
 999      * {@code ALTERNATE} or {@code WINDING}.
1000      */
1001     protected void setPolyFillMode(int fillRule) {
1002         setPolyFillMode(getPrintDC(), fillRule);
1003     }
1004 
1005     /*
1006      * Create a Window's solid brush for the color specified
1007      * by {@code (red, green, blue)}. Once the brush
1008      * is created, select it in the current printing device
1009      * context and free the old brush.
1010      */
1011     protected void selectSolidBrush(Color color) {
1012 
1013         /* We only need to select a brush if the color has changed.
1014         */
1015         if (color.equals(mLastColor) == false) {
1016             mLastColor = color;
1017             float[] rgb = color.getRGBColorComponents(null);
1018 
1019             selectSolidBrush(getPrintDC(),
1020                              (int) (rgb[0] * MAX_WCOLOR),
1021                              (int) (rgb[1] * MAX_WCOLOR),
1022                              (int) (rgb[2] * MAX_WCOLOR));
1023         }
1024     }
1025 
1026     /**
1027      * Return the x coordinate of the current pen
1028      * position in the print device context.
1029      */
1030     protected int getPenX() {
1031 
1032         return getPenX(getPrintDC());
1033     }
1034 
1035 
1036     /**
1037      * Return the y coordinate of the current pen
1038      * position in the print device context.
1039      */
1040     protected int getPenY() {
1041 
1042         return getPenY(getPrintDC());
1043     }
1044 
1045     /**
1046      * Set the current path in the printer device's
1047      * context to be clipping path.
1048      */
1049     protected void selectClipPath() {
1050         selectClipPath(getPrintDC());
1051     }
1052 
1053 
1054     protected void frameRect(float x, float y, float width, float height) {
1055         frameRect(getPrintDC(), x, y, width, height);
1056     }
1057 
1058     protected void fillRect(float x, float y, float width, float height,
1059                             Color color) {
1060         float[] rgb = color.getRGBColorComponents(null);
1061 
1062         fillRect(getPrintDC(), x, y, width, height,
1063                  (int) (rgb[0] * MAX_WCOLOR),
1064                  (int) (rgb[1] * MAX_WCOLOR),
1065                  (int) (rgb[2] * MAX_WCOLOR));
1066     }
1067 
1068 
1069     protected void selectPen(float width, Color color) {
1070 
1071         float[] rgb = color.getRGBColorComponents(null);
1072 
1073         selectPen(getPrintDC(), width,
1074                   (int) (rgb[0] * MAX_WCOLOR),
1075                   (int) (rgb[1] * MAX_WCOLOR),
1076                   (int) (rgb[2] * MAX_WCOLOR));
1077     }
1078 
1079 
1080     protected boolean selectStylePen(int cap, int join, float width,
1081                                      Color color) {
1082 
1083         long endCap;
1084         long lineJoin;
1085 
1086         float[] rgb = color.getRGBColorComponents(null);
1087 
1088         switch(cap) {
1089         case BasicStroke.CAP_BUTT: endCap = PS_ENDCAP_FLAT; break;
1090         case BasicStroke.CAP_ROUND: endCap = PS_ENDCAP_ROUND; break;
1091         default:
1092         case BasicStroke.CAP_SQUARE: endCap = PS_ENDCAP_SQUARE; break;
1093         }
1094 
1095         switch(join) {
1096         case BasicStroke.JOIN_BEVEL:lineJoin = PS_JOIN_BEVEL; break;
1097         default:
1098         case BasicStroke.JOIN_MITER:lineJoin = PS_JOIN_MITER; break;
1099         case BasicStroke.JOIN_ROUND:lineJoin = PS_JOIN_ROUND; break;
1100         }
1101 
1102         return (selectStylePen(getPrintDC(), endCap, lineJoin, width,
1103                                (int) (rgb[0] * MAX_WCOLOR),
1104                                (int) (rgb[1] * MAX_WCOLOR),
1105                                (int) (rgb[2] * MAX_WCOLOR)));
1106     }
1107 
1108     /**
1109      * Set a GDI font capable of drawing the java Font
1110      * passed in.
1111      */
1112     protected boolean setFont(String family, float size, int style,
1113                               int rotation, float awScale) {
1114 
1115         boolean didSetFont = true;
1116 
1117         if (!family.equals(mLastFontFamily) ||
1118             size     != mLastFontSize       ||
1119             style    != mLastFontStyle      ||
1120             rotation != mLastRotation       ||
1121             awScale  != mLastAwScale) {
1122 
1123             didSetFont = setFont(getPrintDC(),
1124                                  family,
1125                                  size,
1126                                  (style & Font.BOLD) != 0,
1127                                  (style & Font.ITALIC) != 0,
1128                                  rotation, awScale);
1129             if (didSetFont) {
1130                 mLastFontFamily   = family;
1131                 mLastFontSize     = size;
1132                 mLastFontStyle    = style;
1133                 mLastRotation     = rotation;
1134                 mLastAwScale      = awScale;
1135             }
1136         }
1137         return didSetFont;
1138     }
1139 
1140     /**
1141      * Set the GDI color for text drawing.
1142      */
1143     protected void setTextColor(Color color) {
1144 
1145         /* We only need to select a brush if the color has changed.
1146         */
1147         if (color.equals(mLastTextColor) == false) {
1148             mLastTextColor = color;
1149             float[] rgb = color.getRGBColorComponents(null);
1150 
1151             setTextColor(getPrintDC(),
1152                          (int) (rgb[0] * MAX_WCOLOR),
1153                          (int) (rgb[1] * MAX_WCOLOR),
1154                          (int) (rgb[2] * MAX_WCOLOR));
1155         }
1156     }
1157 
1158     /**
1159      * Remove control characters.
1160      */
1161     @Override
1162     protected String removeControlChars(String str) {
1163         return super.removeControlChars(str);
1164     }
1165 
1166     /**
1167      * Draw the string {@code text} to the printer's
1168      * device context at the specified position.
1169      */
1170     protected void textOut(String str, float x, float y,
1171                            float[] positions) {
1172         /* Don't leave handling of control chars to GDI.
1173          * If control chars are removed,  'positions' isn't valid.
1174          * This means the caller needs to be aware of this and remove
1175          * control chars up front if supplying positions. Since the
1176          * caller is tightly integrated here, that's acceptable.
1177          */
1178         String text = removeControlChars(str);
1179         assert (positions == null) || (text.length() == str.length());
1180         if (text.length() == 0) {
1181             return;
1182         }
1183         textOut(getPrintDC(), text, text.length(), false, x, y, positions);
1184     }
1185 
1186    /**
1187      * Draw the glyphs {@code glyphs} to the printer's
1188      * device context at the specified position.
1189      */
1190     protected void glyphsOut(int []glyphs, float x, float y,
1191                              float[] positions) {
1192 
1193         /* TrueType glyph codes are 16 bit values, so can be packed
1194          * in a unicode string, and that's how GDI expects them.
1195          * A flag bit is set to indicate to GDI that these are glyphs,
1196          * not characters. The positions array must always be non-null
1197          * here for our purposes, although if not supplied, GDI should
1198          * just use the default advances for the glyphs.
1199          * Mask out upper 16 bits to remove any slot from a composite.
1200          */
1201         char[] glyphCharArray = new char[glyphs.length];
1202         for (int i=0;i<glyphs.length;i++) {
1203             glyphCharArray[i] = (char)(glyphs[i] & 0xffff);
1204         }
1205         String glyphStr = new String(glyphCharArray);
1206         textOut(getPrintDC(), glyphStr, glyphs.length, true, x, y, positions);
1207     }
1208 
1209 
1210     /**
1211      * Get the advance of this text that GDI returns for the
1212      * font currently selected into the GDI device context for
1213      * this job. Note that the removed control characters are
1214      * interpreted as zero-width by JDK and we remove them for
1215      * rendering so also remove them for measurement so that
1216      * this measurement can be properly compared with JDK measurement.
1217      */
1218     protected int getGDIAdvance(String text) {
1219         /* Don't leave handling of control chars to GDI. */
1220         text = removeControlChars(text);
1221         if (text.length() == 0) {
1222             return 0;
1223         }
1224         return getGDIAdvance(getPrintDC(), text);
1225     }
1226 
1227      /**
1228      * Draw the 24 bit BGR image buffer represented by
1229      * {@code image} to the GDI device context
1230      * {@code printDC}. The image is drawn at
1231      * {@code (destX, destY)} in device coordinates.
1232      * The image is scaled into a square of size
1233      * specified by {@code destWidth} and
1234      * {@code destHeight}. The portion of the
1235      * source image copied into that square is specified
1236      * by {@code srcX}, {@code srcY},
1237      * {@code srcWidth}, and srcHeight.
1238      */
1239     protected void drawImage3ByteBGR(byte[] image,
1240                                      float destX, float destY,
1241                                      float destWidth, float destHeight,
1242                                      float srcX, float srcY,
1243                                      float srcWidth, float srcHeight) {
1244 
1245 
1246         drawDIBImage(getPrintDC(), image,
1247                      destX, destY,
1248                      destWidth, destHeight,
1249                      srcX, srcY,
1250                      srcWidth, srcHeight,
1251                      24, null);
1252 
1253     }
1254 
1255     /* If 'icm' is null we expect its 24 bit (ie 3BYTE_BGR).
1256      * If 'icm' is non-null we expect its no more than 8 bpp and
1257      * specifically must be a valid DIB sizes : 1, 4 or 8 bpp.
1258      * Then we need to extract the colours into a byte array of the
1259      * format required by GDI which is an array of 'RGBQUAD'
1260      * RGBQUAD looks like :
1261      * typedef struct tagRGBQUAD {
1262      *    BYTE    rgbBlue;
1263      *    BYTE    rgbGreen;
1264      *    BYTE    rgbRed;
1265      *    BYTE    rgbReserved; // must be zero.
1266      * } RGBQUAD;
1267      * There's no alignment problem as GDI expects this to be packed
1268      * and each struct will start on a 4 byte boundary anyway.
1269      */
1270     protected void drawDIBImage(byte[] image,
1271                                 float destX, float destY,
1272                                 float destWidth, float destHeight,
1273                                 float srcX, float srcY,
1274                                 float srcWidth, float srcHeight,
1275                                 int sampleBitsPerPixel,
1276                                 IndexColorModel icm) {
1277         int bitCount = 24;
1278         byte[] bmiColors = null;
1279 
1280         if (icm != null) {
1281             bitCount = sampleBitsPerPixel;
1282             bmiColors = new byte[(1<<icm.getPixelSize())*4];
1283             for (int i=0;i<icm.getMapSize(); i++) {
1284                 bmiColors[i*4+0]=(byte)(icm.getBlue(i)&0xff);
1285                 bmiColors[i*4+1]=(byte)(icm.getGreen(i)&0xff);
1286                 bmiColors[i*4+2]=(byte)(icm.getRed(i)&0xff);
1287             }
1288         }
1289 
1290         drawDIBImage(getPrintDC(), image,
1291                      destX, destY,
1292                      destWidth, destHeight,
1293                      srcX, srcY,
1294                      srcWidth, srcHeight,
1295                      bitCount, bmiColors);
1296     }
1297 
1298     /**
1299      * Begin a new page.
1300      */
1301     @Override
1302     protected void startPage(PageFormat format, Printable painter,
1303                              int index, boolean paperChanged) {
1304 
1305         /* Invalidate any device state caches we are
1306          * maintaining. Win95/98 resets the device
1307          * context attributes to default values at
1308          * the start of each page.
1309          */
1310         invalidateCachedState();
1311 
1312         deviceStartPage(format, painter, index, paperChanged);
1313     }
1314 
1315     /**
1316      * End a page.
1317      */
1318     @Override
1319     protected void endPage(PageFormat format, Printable painter,
1320                            int index) {
1321 
1322         deviceEndPage(format, painter, index);
1323     }
1324 
1325     /**
1326      * Forget any device state we may have cached.
1327      */
1328     private void invalidateCachedState() {
1329         mLastColor = null;
1330         mLastTextColor = null;
1331         mLastFontFamily = null;
1332     }
1333 
1334     private boolean defaultCopies = true;
1335     /**
1336      * Set the number of copies to be printed.
1337      */
1338     @Override
1339     public void setCopies(int copies) {
1340         super.setCopies(copies);
1341         defaultCopies = false;
1342         mAttCopies = copies;
1343         setNativeCopies(copies);
1344     }
1345 
1346 
1347  /* Native Methods */
1348 
1349     /**
1350      * Set copies in device.
1351      */
1352     private native void setNativeCopies(int copies);
1353 
1354     /**
1355      * Displays the print dialog and records the user's settings
1356      * into this object. Return false if the user cancels the
1357      * dialog.
1358      * If the dialog is to use a set of attributes, useAttributes is true.
1359      */
1360     private native boolean jobSetup(Pageable doc, boolean allowPrintToFile);
1361 
1362     /* Make sure printer DC is intialised and that info about the printer
1363      * is reflected back up to Java code
1364      */
1365     @Override
1366     protected native void initPrinter();
1367 
1368     /**
1369      * Call Window's StartDoc routine to begin a
1370      * print job. The DC from the print dialog is
1371      * used. If the print dialog was not displayed
1372      * then a DC for the default printer is created.
1373      * The native StartDoc returns false if the end-user cancelled
1374      * printing. This is possible if the printer is connected to FILE:
1375      * in which case windows queries the user for a destination and the
1376      * user may cancel out of it. Note that the implementation of
1377      * cancel() throws PrinterAbortException to indicate the user cancelled.
1378      */
1379     private native boolean _startDoc(String dest, String jobName)
1380                                      throws PrinterException;
1381     @Override
1382     protected void startDoc() throws PrinterException {
1383         if (!_startDoc(mDestination, getJobName())) {
1384             cancel();
1385         }
1386     }
1387 
1388     /**
1389      * Call Window's EndDoc routine to end a
1390      * print job.
1391      */
1392     @Override
1393     protected native void endDoc();
1394 
1395     /**
1396      * Call Window's AbortDoc routine to abort a
1397      * print job.
1398      */
1399     @Override
1400     protected native void abortDoc();
1401 
1402     /**
1403      * Call Windows native resource freeing APIs
1404      */
1405     private static native void deleteDC(long dc, long devmode, long devnames);
1406 
1407     /**
1408      * Begin a new page. This call's Window's
1409      * StartPage routine.
1410      */
1411     protected native void deviceStartPage(PageFormat format, Printable painter,
1412                                           int index, boolean paperChanged);
1413     /**
1414      * End a page. This call's Window's EndPage
1415      * routine.
1416      */
1417     protected native void deviceEndPage(PageFormat format, Printable painter,
1418                                         int index);
1419 
1420     /**
1421      * Prints the contents of the array of ints, 'data'
1422      * to the current page. The band is placed at the
1423      * location (x, y) in device coordinates on the
1424      * page. The width and height of the band is
1425      * specified by the caller.
1426      */
1427     @Override
1428     protected native void printBand(byte[] data, int x, int y,
1429                                     int width, int height);
1430 
1431     /**
1432      * Begin a Window's rendering path in the device
1433      * context {@code printDC}.
1434      */
1435     protected native void beginPath(long printDC);
1436 
1437     /**
1438      * End a Window's rendering path in the device
1439      * context {@code printDC}.
1440      */
1441     protected native void endPath(long printDC);
1442 
1443     /**
1444      * Close a subpath in a Window's rendering path in the device
1445      * context {@code printDC}.
1446      */
1447     protected native void closeFigure(long printDC);
1448 
1449     /**
1450      * Fill a defined Window's rendering path in the device
1451      * context {@code printDC}.
1452      */
1453     protected native void fillPath(long printDC);
1454 
1455     /**
1456      * Move the Window's pen position to {@code (x,y)}
1457      * in the device context {@code printDC}.
1458      */
1459     protected native void moveTo(long printDC, float x, float y);
1460 
1461     /**
1462      * Draw a line from the current pen position to
1463      * {@code (x,y)} in the device context {@code printDC}.
1464      */
1465     protected native void lineTo(long printDC, float x, float y);
1466 
1467     protected native void polyBezierTo(long printDC,
1468                                        float control1x, float control1y,
1469                                        float control2x, float control2y,
1470                                        float endX, float endY);
1471 
1472     /**
1473      * Set the current polgon fill rule into the device context
1474      * {@code printDC}. The {@code fillRule} should
1475      * be one of the following Windows constants:
1476      * {@code ALTERNATE} or {@code WINDING}.
1477      */
1478     protected native void setPolyFillMode(long printDC, int fillRule);
1479 
1480     /**
1481      * Create a Window's solid brush for the color specified
1482      * by {@code (red, green, blue)}. Once the brush
1483      * is created, select it in the device
1484      * context {@code printDC} and free the old brush.
1485      */
1486     protected native void selectSolidBrush(long printDC,
1487                                            int red, int green, int blue);
1488 
1489     /**
1490      * Return the x coordinate of the current pen
1491      * position in the device context
1492      * {@code printDC}.
1493      */
1494     protected native int getPenX(long printDC);
1495 
1496     /**
1497      * Return the y coordinate of the current pen
1498      * position in the device context
1499      * {@code printDC}.
1500      */
1501     protected native int getPenY(long printDC);
1502 
1503     /**
1504      * Select the device context's current path
1505      * to be the clipping path.
1506      */
1507     protected native void selectClipPath(long printDC);
1508 
1509     /**
1510      * Draw a rectangle using specified brush.
1511      */
1512     protected native void frameRect(long printDC, float x, float y,
1513                                     float width, float height);
1514 
1515     /**
1516      * Fill a rectangle specified by the coordinates using
1517      * specified brush.
1518      */
1519     protected native void fillRect(long printDC, float x, float y,
1520                                    float width, float height,
1521                                    int red, int green, int blue);
1522 
1523     /**
1524      * Create a solid brush using the RG & B colors and width.
1525      * Select this brush and delete the old one.
1526      */
1527     protected native void selectPen(long printDC, float width,
1528                                     int red, int green, int blue);
1529 
1530     /**
1531      * Create a solid brush using the RG & B colors and specified
1532      * pen styles.  Select this created brush and delete the old one.
1533      */
1534     protected native boolean selectStylePen(long printDC, long cap,
1535                                             long join, float width,
1536                                             int red, int green, int blue);
1537 
1538     /**
1539      * Set a GDI font capable of drawing the java Font
1540      * passed in.
1541      */
1542     protected native boolean setFont(long printDC, String familyName,
1543                                      float fontSize,
1544                                      boolean bold,
1545                                      boolean italic,
1546                                      int rotation,
1547                                      float awScale);
1548 
1549 
1550     /**
1551      * Set the GDI color for text drawing.
1552      */
1553     protected native void setTextColor(long printDC,
1554                                        int red, int green, int blue);
1555 
1556 
1557     /**
1558      * Draw the string {@code text} into the device
1559      * context {@code printDC} at the specified
1560      * position.
1561      */
1562     protected native void textOut(long printDC, String text,
1563                                   int strlen, boolean glyphs,
1564                                   float x, float y, float[] positions);
1565 
1566 
1567     private native int getGDIAdvance(long printDC, String text);
1568 
1569      /**
1570      * Draw the DIB compatible image buffer represented by
1571      * {@code image} to the GDI device context
1572      * {@code printDC}. The image is drawn at
1573      * {@code (destX, destY)} in device coordinates.
1574      * The image is scaled into a square of size
1575      * specified by {@code destWidth} and
1576      * {@code destHeight}. The portion of the
1577      * source image copied into that square is specified
1578      * by {@code srcX}, {@code srcY},
1579      * {@code srcWidth}, and srcHeight.
1580      * Note that the image isn't completely compatible with DIB format.
1581      * At the very least it needs to be padded so each scanline is
1582      * DWORD aligned. Also we "flip" the image to make it a bottom-up DIB.
1583      */
1584     private native void drawDIBImage(long printDC, byte[] image,
1585                                      float destX, float destY,
1586                                      float destWidth, float destHeight,
1587                                      float srcX, float srcY,
1588                                      float srcWidth, float srcHeight,
1589                                      int bitCount, byte[] bmiColors);
1590 
1591 
1592     //** BEGIN Functions called by native code for querying/updating attributes
1593 
1594     private String getPrinterAttrib() {
1595         // getPrintService will get current print service or default if none
1596         PrintService service = this.getPrintService();
1597         String name = (service != null) ? service.getName() : null;
1598         return name;
1599     }
1600 
1601     /* SheetCollate */
1602     private int getCollateAttrib() {
1603         // -1 means unset, 0 uncollated, 1 collated.
1604         return mAttCollate;
1605     }
1606 
1607     private void setCollateAttrib(Attribute attr) {
1608         if (attr == SheetCollate.COLLATED) {
1609             mAttCollate = 1; // DMCOLLATE_TRUE
1610         } else {
1611             mAttCollate = 0; // DMCOLLATE_FALSE
1612         }
1613     }
1614 
1615     private void setCollateAttrib(Attribute attr,
1616                                   PrintRequestAttributeSet set) {
1617         setCollateAttrib(attr);
1618         set.add(attr);
1619     }
1620 
1621     /* Orientation */
1622 
1623     private int getOrientAttrib() {
1624         int orient = PageFormat.PORTRAIT;
1625         OrientationRequested orientReq = (attributes == null) ? null :
1626             (OrientationRequested)attributes.get(OrientationRequested.class);
1627         if (orientReq == null) {
1628             orientReq = (OrientationRequested)
1629                myService.getDefaultAttributeValue(OrientationRequested.class);
1630         }
1631         if (orientReq != null) {
1632             if (orientReq == OrientationRequested.REVERSE_LANDSCAPE) {
1633                 orient = PageFormat.REVERSE_LANDSCAPE;
1634             } else if (orientReq == OrientationRequested.LANDSCAPE) {
1635                 orient = PageFormat.LANDSCAPE;
1636             }
1637         }
1638 
1639         return orient;
1640     }
1641 
1642     private void setOrientAttrib(Attribute attr,
1643                                  PrintRequestAttributeSet set) {
1644         if (set != null) {
1645             set.add(attr);
1646         }
1647     }
1648 
1649     /* Copies and Page Range. */
1650     private int getCopiesAttrib() {
1651         if (defaultCopies) {
1652             return 0;
1653         } else {
1654             return getCopiesInt();
1655         }
1656      }
1657 
1658     private void setRangeCopiesAttribute(int from, int to, boolean isRangeSet,
1659                                          int copies) {
1660         if (attributes != null) {
1661             if (isRangeSet) {
1662                 attributes.add(new PageRanges(from, to));
1663                 setPageRange(from, to);
1664             }
1665             defaultCopies = false;
1666             attributes.add(new Copies(copies));
1667             /* Since this is called from native to tell Java to sync
1668              * up with native, we don't call this class's own setCopies()
1669              * method which is mainly to send the value down to native
1670              */
1671             super.setCopies(copies);
1672             mAttCopies = copies;
1673         }
1674     }
1675 
1676 
1677 
1678     private boolean getDestAttrib() {
1679         return (mDestination != null);
1680     }
1681 
1682     /* Quality */
1683     private int getQualityAttrib() {
1684         return mAttQuality;
1685     }
1686 
1687     private void setQualityAttrib(Attribute attr) {
1688         if (attr == PrintQuality.HIGH) {
1689             mAttQuality = -4; // DMRES_HIGH
1690         } else if (attr == PrintQuality.NORMAL) {
1691             mAttQuality = -3; // DMRES_MEDIUM
1692         } else {
1693             mAttQuality = -2; // DMRES_LOW
1694         }
1695     }
1696 
1697     private void setQualityAttrib(Attribute attr,
1698                                   PrintRequestAttributeSet set) {
1699         setQualityAttrib(attr);
1700         set.add(attr);
1701     }
1702 
1703     /* Color/Chromaticity */
1704     private int getColorAttrib() {
1705         return mAttChromaticity;
1706     }
1707 
1708     private void setColorAttrib(Attribute attr) {
1709         if (attr == Chromaticity.COLOR) {
1710             mAttChromaticity = 2; // DMCOLOR_COLOR
1711         } else {
1712             mAttChromaticity = 1; // DMCOLOR_MONOCHROME
1713         }
1714     }
1715 
1716     private void setColorAttrib(Attribute attr,
1717                                   PrintRequestAttributeSet set) {
1718         setColorAttrib(attr);
1719         set.add(attr);
1720     }
1721 
1722     /* Sides */
1723     private int getSidesAttrib() {
1724         return mAttSides;
1725     }
1726 
1727     private void setSidesAttrib(Attribute attr) {
1728         if (attr == Sides.TWO_SIDED_LONG_EDGE) {
1729             mAttSides = 2; // DMDUP_VERTICAL
1730         } else if (attr == Sides.TWO_SIDED_SHORT_EDGE) {
1731             mAttSides = 3; // DMDUP_HORIZONTAL
1732         } else { // Sides.ONE_SIDED
1733             mAttSides = 1;
1734         }
1735     }
1736 
1737     private void setSidesAttrib(Attribute attr,
1738                                 PrintRequestAttributeSet set) {
1739         setSidesAttrib(attr);
1740         set.add(attr);
1741     }
1742 
1743     /** MediaSizeName / dmPaper */
1744     private int[] getWin32MediaAttrib() {
1745         int wid_ht[] = {0, 0};
1746         if (attributes != null) {
1747             Media media = (Media)attributes.get(Media.class);
1748             if (media instanceof MediaSizeName) {
1749                 MediaSizeName msn = (MediaSizeName)media;
1750                 MediaSize ms = MediaSize.getMediaSizeForName(msn);
1751                 if (ms != null) {
1752                     wid_ht[0] = (int)(ms.getX(MediaSize.INCH) * 72.0);
1753                     wid_ht[1] = (int)(ms.getY(MediaSize.INCH) * 72.0);
1754                 }
1755             }
1756         }
1757         return wid_ht;
1758     }
1759 
1760     private void setWin32MediaAttrib(Attribute attr) {
1761         if (!(attr instanceof MediaSizeName)) {
1762             return;
1763         }
1764         MediaSizeName msn = (MediaSizeName)attr;
1765         mAttMediaSizeName = ((Win32PrintService)myService).findPaperID(msn);
1766     }
1767 
1768     private void addPaperSize(PrintRequestAttributeSet aset,
1769                               int dmIndex, int width, int length) {
1770 
1771         if (aset == null) {
1772             return;
1773         }
1774         MediaSizeName msn =
1775            ((Win32PrintService)myService).findWin32Media(dmIndex);
1776         if (msn == null) {
1777             msn = ((Win32PrintService)myService).
1778                 findMatchingMediaSizeNameMM((float)width, (float)length);
1779         }
1780 
1781         if (msn != null) {
1782             aset.add(msn);
1783         }
1784     }
1785 
1786     private void setWin32MediaAttrib(int dmIndex, int width, int length) {
1787         addPaperSize(attributes, dmIndex, width, length);
1788         mAttMediaSizeName = dmIndex;
1789     }
1790 
1791     /* MediaTray / dmTray */
1792     private void setMediaTrayAttrib(Attribute attr) {
1793         if (attr == MediaTray.BOTTOM) {
1794             mAttMediaTray = 2;    // DMBIN_LOWER
1795         } else if (attr == MediaTray.ENVELOPE) {
1796             mAttMediaTray = 5;    // DMBIN_ENVELOPE
1797         } else if (attr == MediaTray.LARGE_CAPACITY) {
1798             mAttMediaTray = 11;      // DMBIN_LARGECAPACITY
1799         } else if (attr == MediaTray.MAIN) {
1800             mAttMediaTray =1;               // DMBIN_UPPER
1801         } else if (attr == MediaTray.MANUAL) {
1802             mAttMediaTray = 4;              // DMBIN_MANUAL
1803         } else if (attr == MediaTray.MIDDLE) {
1804             mAttMediaTray = 3;              // DMBIN_MIDDLE
1805         } else if (attr == MediaTray.SIDE) {
1806             // no equivalent predefined value
1807             mAttMediaTray = 7;              // DMBIN_AUTO
1808         } else if (attr == MediaTray.TOP) {
1809             mAttMediaTray = 1;              // DMBIN_UPPER
1810         } else {
1811             if (attr instanceof Win32MediaTray) {
1812                 mAttMediaTray = ((Win32MediaTray)attr).winID;
1813             } else {
1814                 mAttMediaTray = 1;  // default
1815             }
1816         }
1817     }
1818 
1819     private void setMediaTrayAttrib(int dmBinID) {
1820         mAttMediaTray = dmBinID;
1821         MediaTray tray = ((Win32PrintService)myService).findMediaTray(dmBinID);
1822     }
1823 
1824     private int getMediaTrayAttrib() {
1825         return mAttMediaTray;
1826     }
1827 
1828 
1829 
1830     private boolean getPrintToFileEnabled() {
1831         SecurityManager security = System.getSecurityManager();
1832         if (security != null) {
1833             FilePermission printToFilePermission =
1834                 new FilePermission("<<ALL FILES>>", "read,write");
1835             try {
1836                 security.checkPermission(printToFilePermission);
1837             } catch (SecurityException e) {
1838                 return false;
1839             }
1840         }
1841         return true;
1842     }
1843 
1844     private void setNativeAttributes(int flags, int fields, int values) {
1845         if (attributes == null) {
1846             return;
1847         }
1848         if ((flags & PD_PRINTTOFILE) != 0) {
1849             Destination destPrn = (Destination)attributes.get(
1850                                                  Destination.class);
1851             if (destPrn == null) {
1852                 try {
1853                     attributes.add(new Destination(
1854                                                new File("./out.prn").toURI()));
1855                 } catch (SecurityException se) {
1856                     try {
1857                         attributes.add(new Destination(
1858                                                 new URI("file:out.prn")));
1859                     } catch (URISyntaxException e) {
1860                     }
1861                 }
1862             }
1863         } else {
1864             attributes.remove(Destination.class);
1865         }
1866 
1867         if ((flags & PD_COLLATE) != 0) {
1868             setCollateAttrib(SheetCollate.COLLATED, attributes);
1869         } else {
1870             setCollateAttrib(SheetCollate.UNCOLLATED, attributes);
1871         }
1872 
1873         if ((flags & PD_NOSELECTION) != PD_NOSELECTION) {
1874             if ((flags & PD_PAGENUMS) != 0) {
1875                 attributes.add(SunPageSelection.RANGE);
1876             } else if ((flags & PD_SELECTION) != 0) {
1877                 attributes.add(SunPageSelection.SELECTION);
1878             } else {
1879                 attributes.add(SunPageSelection.ALL);
1880             }
1881         }
1882 
1883         if ((fields & DM_ORIENTATION) != 0) {
1884             if ((values & SET_ORIENTATION) != 0) {
1885                 setOrientAttrib(OrientationRequested.LANDSCAPE, attributes);
1886             } else {
1887                 setOrientAttrib(OrientationRequested.PORTRAIT, attributes);
1888             }
1889         }
1890 
1891         if ((fields & DM_COLOR) != 0) {
1892             if ((values & SET_COLOR) != 0) {
1893                 setColorAttrib(Chromaticity.COLOR, attributes);
1894             } else {
1895                 setColorAttrib(Chromaticity.MONOCHROME, attributes);
1896             }
1897         }
1898 
1899         if ((fields & DM_PRINTQUALITY) != 0) {
1900             PrintQuality quality;
1901             if ((values & SET_RES_LOW) != 0) {
1902                 quality = PrintQuality.DRAFT;
1903             } else if ((fields & SET_RES_HIGH) != 0) {
1904                 quality = PrintQuality.HIGH;
1905             } else {
1906                 quality = PrintQuality.NORMAL;
1907             }
1908             setQualityAttrib(quality, attributes);
1909         }
1910 
1911         if ((fields & DM_DUPLEX) != 0) {
1912             Sides sides;
1913             if ((values & SET_DUP_VERTICAL) != 0) {
1914                 sides = Sides.TWO_SIDED_LONG_EDGE;
1915             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
1916                 sides = Sides.TWO_SIDED_SHORT_EDGE;
1917             } else {
1918                 sides = Sides.ONE_SIDED;
1919             }
1920             setSidesAttrib(sides, attributes);
1921         }
1922     }
1923 
1924     private static final class DevModeValues {
1925         int dmFields;
1926         short copies;
1927         short collate;
1928         short color;
1929         short duplex;
1930         short orient;
1931         short paper;
1932         short bin;
1933         short xres_quality;
1934         short yres;
1935     }
1936 
1937     private void getDevModeValues(PrintRequestAttributeSet aset,
1938                                   DevModeValues info) {
1939 
1940         Copies c = (Copies)aset.get(Copies.class);
1941         if (c != null) {
1942             info.dmFields |= DM_COPIES;
1943             info.copies = (short)c.getValue();
1944         }
1945 
1946         SheetCollate sc = (SheetCollate)aset.get(SheetCollate.class);
1947         if (sc != null) {
1948             info.dmFields |= DM_COLLATE;
1949             info.collate = (sc == SheetCollate.COLLATED) ?
1950                 DMCOLLATE_TRUE : DMCOLLATE_FALSE;
1951         }
1952 
1953         Chromaticity ch = (Chromaticity)aset.get(Chromaticity.class);
1954         if (ch != null) {
1955             info.dmFields |= DM_COLOR;
1956             if (ch == Chromaticity.COLOR) {
1957                 info.color = DMCOLOR_COLOR;
1958             } else {
1959                 info.color = DMCOLOR_MONOCHROME;
1960             }
1961         }
1962 
1963         Sides s = (Sides)aset.get(Sides.class);
1964         if (s != null) {
1965             info.dmFields |= DM_DUPLEX;
1966             if (s == Sides.TWO_SIDED_LONG_EDGE) {
1967                 info.duplex = DMDUP_VERTICAL;
1968             } else if (s == Sides.TWO_SIDED_SHORT_EDGE) {
1969                 info.duplex = DMDUP_HORIZONTAL;
1970             } else { // Sides.ONE_SIDED
1971                 info.duplex = DMDUP_SIMPLEX;
1972             }
1973         }
1974 
1975         OrientationRequested or =
1976             (OrientationRequested)aset.get(OrientationRequested.class);
1977         if (or != null) {
1978             info.dmFields |= DM_ORIENTATION;
1979             info.orient = (or == OrientationRequested.LANDSCAPE)
1980                 ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
1981         }
1982 
1983         Media m = (Media)aset.get(Media.class);
1984         if (m instanceof MediaSizeName) {
1985             info.dmFields |= DM_PAPERSIZE;
1986             MediaSizeName msn = (MediaSizeName)m;
1987             info.paper =
1988                 (short)((Win32PrintService)myService).findPaperID(msn);
1989         }
1990 
1991         MediaTray mt = null;
1992         if (m instanceof MediaTray) {
1993             mt = (MediaTray)m;
1994         }
1995         if (mt == null) {
1996             SunAlternateMedia sam =
1997                 (SunAlternateMedia)aset.get(SunAlternateMedia.class);
1998             if (sam != null && (sam.getMedia() instanceof MediaTray)) {
1999                 mt = (MediaTray)sam.getMedia();
2000             }
2001         }
2002 
2003         if (mt != null) {
2004             info.dmFields |= DM_DEFAULTSOURCE;
2005             info.bin = (short)(((Win32PrintService)myService).findTrayID(mt));
2006         }
2007 
2008         PrintQuality q = (PrintQuality)aset.get(PrintQuality.class);
2009         if (q != null) {
2010             info.dmFields |= DM_PRINTQUALITY;
2011             if (q == PrintQuality.DRAFT) {
2012                 info.xres_quality = DMRES_DRAFT;
2013             } else if (q == PrintQuality.HIGH) {
2014                 info.xres_quality = DMRES_HIGH;
2015             } else {
2016                 info.xres_quality = DMRES_MEDIUM;
2017             }
2018         }
2019 
2020         PrinterResolution r =
2021             (PrinterResolution)aset.get(PrinterResolution.class);
2022         if (r != null) {
2023             info.dmFields |= DM_PRINTQUALITY | DM_YRESOLUTION;
2024             info.xres_quality =
2025                 (short)r.getCrossFeedResolution(PrinterResolution.DPI);
2026             info.yres = (short)r.getFeedResolution(PrinterResolution.DPI);
2027         }
2028     }
2029 
2030     /* This method is called from native to update the values in the
2031      * attribute set which originates from the cross-platform dialog,
2032      * but updated by the native DocumentPropertiesUI which updates the
2033      * devmode. This syncs the devmode back in to the attributes so that
2034      * we can update the cross-platform dialog.
2035      * The attribute set here is a temporary one installed whilst this
2036      * happens,
2037      */
2038     private void setJobAttributes(PrintRequestAttributeSet attributes,
2039                                         int fields, int values,
2040                                         short copies,
2041                                         short dmPaperSize,
2042                                         short dmPaperWidth,
2043                                         short dmPaperLength,
2044                                         short dmDefaultSource,
2045                                         short xRes,
2046                                         short yRes) {
2047 
2048         if (attributes == null) {
2049             return;
2050         }
2051 
2052         if ((fields & DM_COPIES) != 0) {
2053             attributes.add(new Copies(copies));
2054         }
2055 
2056         if ((fields & DM_COLLATE) != 0) {
2057             if ((values & SET_COLLATED) != 0) {
2058                 attributes.add(SheetCollate.COLLATED);
2059             } else {
2060                 attributes.add(SheetCollate.UNCOLLATED);
2061             }
2062         }
2063 
2064         if ((fields & DM_ORIENTATION) != 0) {
2065             if ((values & SET_ORIENTATION) != 0) {
2066                 attributes.add(OrientationRequested.LANDSCAPE);
2067             } else {
2068                 attributes.add(OrientationRequested.PORTRAIT);
2069             }
2070         }
2071 
2072         if ((fields & DM_COLOR) != 0) {
2073             if ((values & SET_COLOR) != 0) {
2074                 attributes.add(Chromaticity.COLOR);
2075             } else {
2076                 attributes.add(Chromaticity.MONOCHROME);
2077             }
2078         }
2079 
2080         if ((fields & DM_PRINTQUALITY) != 0) {
2081             /* value < 0 indicates quality setting.
2082              * value > 0 indicates X resolution. In that case
2083              * hopefully we will also find y-resolution specified.
2084              * If its not, assume its the same as x-res.
2085              * Maybe Java code should try to reconcile this against
2086              * the printers claimed set of supported resolutions.
2087              */
2088             if (xRes < 0) {
2089                 PrintQuality quality;
2090                 if ((values & SET_RES_LOW) != 0) {
2091                     quality = PrintQuality.DRAFT;
2092                 } else if ((fields & SET_RES_HIGH) != 0) {
2093                     quality = PrintQuality.HIGH;
2094                 } else {
2095                     quality = PrintQuality.NORMAL;
2096                 }
2097                 attributes.add(quality);
2098             } else if (xRes > 0 && yRes > 0) {
2099                 attributes.add(
2100                     new PrinterResolution(xRes, yRes, PrinterResolution.DPI));
2101             }
2102         }
2103 
2104         if ((fields & DM_DUPLEX) != 0) {
2105             Sides sides;
2106             if ((values & SET_DUP_VERTICAL) != 0) {
2107                 sides = Sides.TWO_SIDED_LONG_EDGE;
2108             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
2109                 sides = Sides.TWO_SIDED_SHORT_EDGE;
2110             } else {
2111                 sides = Sides.ONE_SIDED;
2112             }
2113             attributes.add(sides);
2114         }
2115 
2116         if ((fields & DM_PAPERSIZE) != 0) {
2117             addPaperSize(attributes, dmPaperSize, dmPaperWidth, dmPaperLength);
2118         }
2119 
2120         if ((fields & DM_DEFAULTSOURCE) != 0) {
2121             MediaTray tray =
2122                 ((Win32PrintService)myService).findMediaTray(dmDefaultSource);
2123             attributes.add(new SunAlternateMedia(tray));
2124         }
2125     }
2126 
2127     private native boolean showDocProperties(long hWnd,
2128                                              PrintRequestAttributeSet aset,
2129                                              int dmFields,
2130                                              short copies,
2131                                              short collate,
2132                                              short color,
2133                                              short duplex,
2134                                              short orient,
2135                                              short paper,
2136                                              short bin,
2137                                              short xres_quality,
2138                                              short yres);
2139 
2140     public PrintRequestAttributeSet
2141         showDocumentProperties(Window owner,
2142                                PrintService service,
2143                                PrintRequestAttributeSet aset)
2144     {
2145         try {
2146             setNativePrintServiceIfNeeded(service.getName());
2147         } catch (PrinterException e) {
2148         }
2149         final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
2150         long hWnd = acc.<WComponentPeer>getPeer(owner).getHWnd();
2151         DevModeValues info = new DevModeValues();
2152         getDevModeValues(aset, info);
2153         boolean ok =
2154             showDocProperties(hWnd, aset,
2155                               info.dmFields,
2156                               info.copies,
2157                               info.collate,
2158                               info.color,
2159                               info.duplex,
2160                               info.orient,
2161                               info.paper,
2162                               info.bin,
2163                               info.xres_quality,
2164                               info.yres);
2165 
2166         if (ok) {
2167             return aset;
2168         } else {
2169             return null;
2170         }
2171     }
2172 
2173     /* Printer Resolution. See also getXRes() and getYRes() */
2174     private void setResolutionDPI(int xres, int yres) {
2175         if (attributes != null) {
2176             PrinterResolution res =
2177                 new PrinterResolution(xres, yres, PrinterResolution.DPI);
2178             attributes.add(res);
2179         }
2180         mAttXRes = xres;
2181         mAttYRes = yres;
2182     }
2183 
2184     private void setResolutionAttrib(Attribute attr) {
2185         PrinterResolution pr = (PrinterResolution)attr;
2186         mAttXRes = pr.getCrossFeedResolution(PrinterResolution.DPI);
2187         mAttYRes = pr.getFeedResolution(PrinterResolution.DPI);
2188     }
2189 
2190     private void setPrinterNameAttrib(String printerName) {
2191         PrintService service = this.getPrintService();
2192 
2193         if (printerName == null) {
2194             return;
2195         }
2196 
2197         if (service != null && printerName.equals(service.getName())) {
2198             return;
2199         } else {
2200             PrintService []services = PrinterJob.lookupPrintServices();
2201             for (int i=0; i<services.length; i++) {
2202                 if (printerName.equals(services[i].getName())) {
2203 
2204                     try {
2205                         this.setPrintService(services[i]);
2206                     } catch (PrinterException e) {
2207                     }
2208                     return;
2209                 }
2210             }
2211         }
2212     //** END Functions called by native code for querying/updating attributes
2213 
2214     }
2215 
2216 @SuppressWarnings("serial") // JDK-implementation class
2217 class PrintToFileErrorDialog extends Dialog implements ActionListener{
2218     public PrintToFileErrorDialog(Frame parent, String title, String message,
2219                            String buttonText) {
2220         super(parent, title, true);
2221         init (parent, title, message, buttonText);
2222     }
2223 
2224     public PrintToFileErrorDialog(Dialog parent, String title, String message,
2225                            String buttonText) {
2226         super(parent, title, true);
2227         init (parent, title, message, buttonText);
2228     }
2229 
2230     private void init(Component parent, String  title, String message,
2231                       String buttonText) {
2232         Panel p = new Panel();
2233         add("Center", new Label(message));
2234         Button btn = new Button(buttonText);
2235         btn.addActionListener(this);
2236         p.add(btn);
2237         add("South", p);
2238         pack();
2239 
2240         Dimension dDim = getSize();
2241         if (parent != null) {
2242             Rectangle fRect = parent.getBounds();
2243             setLocation(fRect.x + ((fRect.width - dDim.width) / 2),
2244                         fRect.y + ((fRect.height - dDim.height) / 2));
2245         }
2246     }
2247 
2248     @Override
2249     public void actionPerformed(ActionEvent event) {
2250         setVisible(false);
2251         dispose();
2252         return;
2253     }
2254 }
2255 
2256 
2257 
2258 
2259     /**
2260      * Initialize JNI field and method ids
2261      */
2262     private static native void initIDs();
2263 
2264 }