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