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