1 /*
   2  * Copyright 1997-2007 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any 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                                 IndexColorModel icm) {
1216         int bitCount = 24;
1217         byte[] bmiColors = null;
1218 
1219         if (icm != null) {
1220             bitCount = icm.getPixelSize();
1221             bmiColors = new byte[(1<<bitCount)*4];
1222             for (int i=0;i<icm.getMapSize(); i++) {
1223                 bmiColors[i*4+0]=(byte)(icm.getBlue(i)&0xff);
1224                 bmiColors[i*4+1]=(byte)(icm.getGreen(i)&0xff);
1225                 bmiColors[i*4+2]=(byte)(icm.getRed(i)&0xff);
1226             }
1227         }
1228 
1229         drawDIBImage(getPrintDC(), image,
1230                      destX, destY,
1231                      destWidth, destHeight,
1232                      srcX, srcY,
1233                      srcWidth, srcHeight,
1234                      bitCount, bmiColors);
1235     }
1236 
1237     /**
1238      * Begin a new page.
1239      */
1240     protected void startPage(PageFormat format, Printable painter,
1241                              int index, boolean paperChanged) {
1242 
1243         /* Invalidate any device state caches we are
1244          * maintaining. Win95/98 resets the device
1245          * context attributes to default values at
1246          * the start of each page.
1247          */
1248         invalidateCachedState();
1249 
1250         deviceStartPage(format, painter, index, paperChanged);
1251     }
1252 
1253     /**
1254      * End a page.
1255      */
1256     protected void endPage(PageFormat format, Printable painter,
1257                            int index) {
1258 
1259         deviceEndPage(format, painter, index);
1260     }
1261 
1262     /**
1263      * Forget any device state we may have cached.
1264      */
1265     private void invalidateCachedState() {
1266         mLastColor = null;
1267         mLastTextColor = null;
1268         mLastFontFamily = null;
1269     }
1270 
1271     /**
1272      * Set the number of copies to be printed.
1273      */
1274     public void setCopies(int copies) {
1275         super.setCopies(copies);
1276         mAttCopies = copies;
1277         setNativeCopies(copies);
1278     }
1279 
1280 
1281  /* Native Methods */
1282 
1283     /**
1284      * Set copies in device.
1285      */
1286     public native void setNativeCopies(int copies);
1287 
1288     /**
1289      * Displays the print dialog and records the user's settings
1290      * into this object. Return false if the user cancels the
1291      * dialog.
1292      * If the dialog is to use a set of attributes, useAttributes is true.
1293      */
1294     private native boolean jobSetup(Pageable doc, boolean allowPrintToFile);
1295 
1296     /* Make sure printer DC is intialised and that info about the printer
1297      * is reflected back up to Java code
1298      */
1299     protected native void initPrinter();
1300 
1301     /**
1302      * Call Window's StartDoc routine to begin a
1303      * print job. The DC from the print dialog is
1304      * used. If the print dialog was not displayed
1305      * then a DC for the default printer is created.
1306      * The native StartDoc returns false if the end-user cancelled
1307      * printing. This is possible if the printer is connected to FILE:
1308      * in which case windows queries the user for a destination and the
1309      * user may cancel out of it. Note that the implementation of
1310      * cancel() throws PrinterAbortException to indicate the user cancelled.
1311      */
1312     private native boolean _startDoc(String dest, String jobName)
1313                                      throws PrinterException;
1314     protected void startDoc() throws PrinterException {
1315         if (!_startDoc(mDestination, getJobName())) {
1316             cancel();
1317         }
1318     }
1319 
1320     /**
1321      * Call Window's EndDoc routine to end a
1322      * print job.
1323      */
1324     protected native void endDoc();
1325 
1326     /**
1327      * Call Window's AbortDoc routine to abort a
1328      * print job.
1329      */
1330     protected native void abortDoc();
1331 
1332     /**
1333      * Call Windows native resource freeing APIs
1334      */
1335     private static native void deleteDC(long dc, long devmode, long devnames);
1336 
1337     /**
1338      * Begin a new page. This call's Window's
1339      * StartPage routine.
1340      */
1341     protected native void deviceStartPage(PageFormat format, Printable painter,
1342                                           int index, boolean paperChanged);
1343     /**
1344      * End a page. This call's Window's EndPage
1345      * routine.
1346      */
1347     protected native void deviceEndPage(PageFormat format, Printable painter,
1348                                         int index);
1349 
1350     /**
1351      * Prints the contents of the array of ints, 'data'
1352      * to the current page. The band is placed at the
1353      * location (x, y) in device coordinates on the
1354      * page. The width and height of the band is
1355      * specified by the caller.
1356      */
1357     protected native void printBand(byte[] data, int x, int y,
1358                                     int width, int height);
1359 
1360     /**
1361      * Begin a Window's rendering path in the device
1362      * context <code>printDC</code>.
1363      */
1364     protected native void beginPath(long printDC);
1365 
1366     /**
1367      * End a Window's rendering path in the device
1368      * context <code>printDC</code>.
1369      */
1370     protected native void endPath(long printDC);
1371 
1372     /**
1373      * Close a subpath in a Window's rendering path in the device
1374      * context <code>printDC</code>.
1375      */
1376     protected native void closeFigure(long printDC);
1377 
1378     /**
1379      * Fill a defined Window's rendering path in the device
1380      * context <code>printDC</code>.
1381      */
1382     protected native void fillPath(long printDC);
1383 
1384     /**
1385      * Move the Window's pen position to <code>(x,y)</code>
1386      * in the device context <code>printDC</code>.
1387      */
1388     protected native void moveTo(long printDC, float x, float y);
1389 
1390     /**
1391      * Draw a line from the current pen position to
1392      * <code>(x,y)</code> in the device context <code>printDC</code>.
1393      */
1394     protected native void lineTo(long printDC, float x, float y);
1395 
1396     protected native void polyBezierTo(long printDC,
1397                                        float control1x, float control1y,
1398                                        float control2x, float control2y,
1399                                        float endX, float endY);
1400 
1401     /**
1402      * Set the current polgon fill rule into the device context
1403      * <code>printDC</code>. The <code>fillRule</code> should
1404      * be one of the following Windows constants:
1405      * <code>ALTERNATE</code> or <code>WINDING</code>.
1406      */
1407     protected native void setPolyFillMode(long printDC, int fillRule);
1408 
1409     /**
1410      * Create a Window's solid brush for the color specified
1411      * by <code>(red, green, blue)</code>. Once the brush
1412      * is created, select it in the device
1413      * context <code>printDC</code> and free the old brush.
1414      */
1415     protected native void selectSolidBrush(long printDC,
1416                                            int red, int green, int blue);
1417 
1418     /**
1419      * Return the x coordinate of the current pen
1420      * position in the device context
1421      * <code>printDC</code>.
1422      */
1423     protected native int getPenX(long printDC);
1424 
1425     /**
1426      * Return the y coordinate of the current pen
1427      * position in the device context
1428      * <code>printDC</code>.
1429      */
1430     protected native int getPenY(long printDC);
1431 
1432     /**
1433      * Select the device context's current path
1434      * to be the clipping path.
1435      */
1436     protected native void selectClipPath(long printDC);
1437 
1438     /**
1439      * Draw a rectangle using specified brush.
1440      */
1441     protected native void frameRect(long printDC, float x, float y,
1442                                     float width, float height);
1443 
1444     /**
1445      * Fill a rectangle specified by the coordinates using
1446      * specified brush.
1447      */
1448     protected native void fillRect(long printDC, float x, float y,
1449                                    float width, float height,
1450                                    int red, int green, int blue);
1451 
1452     /**
1453      * Create a solid brush using the RG & B colors and width.
1454      * Select this brush and delete the old one.
1455      */
1456     protected native void selectPen(long printDC, float width,
1457                                     int red, int green, int blue);
1458 
1459     /**
1460      * Create a solid brush using the RG & B colors and specified
1461      * pen styles.  Select this created brush and delete the old one.
1462      */
1463     protected native boolean selectStylePen(long printDC, long cap,
1464                                             long join, float width,
1465                                             int red, int green, int blue);
1466 
1467     /**
1468      * Set a GDI font capable of drawing the java Font
1469      * passed in.
1470      */
1471     protected native boolean setFont(long printDC, String familyName,
1472                                      float fontSize,
1473                                      boolean bold,
1474                                      boolean italic,
1475                                      int rotation,
1476                                      float awScale);
1477 
1478 
1479     /**
1480      * Set the GDI color for text drawing.
1481      */
1482     protected native void setTextColor(long printDC,
1483                                        int red, int green, int blue);
1484 
1485 
1486     /**
1487      * Draw the string <code>text</code> into the device
1488      * context <code>printDC</code> at the specified
1489      * position.
1490      */
1491     protected native void textOut(long printDC, String text,
1492                                   int strlen, boolean glyphs,
1493                                   float x, float y, float[] positions);
1494 
1495 
1496     private native int getGDIAdvance(long printDC, String text);
1497 
1498      /**
1499      * Draw the DIB compatible image buffer represented by
1500      * <code>image</code> to the GDI device context
1501      * <code>printDC</code>. The image is drawn at
1502      * <code>(destX, destY)</code> in device coordinates.
1503      * The image is scaled into a square of size
1504      * specified by <code>destWidth</code> and
1505      * <code>destHeight</code>. The portion of the
1506      * source image copied into that square is specified
1507      * by <code>srcX</code>, <code>srcY</code>,
1508      * <code>srcWidth</code>, and srcHeight.
1509      * Note that the image isn't completely compatible with DIB format.
1510      * At the very least it needs to be padded so each scanline is
1511      * DWORD aligned. Also we "flip" the image to make it a bottom-up DIB.
1512      */
1513     private native void drawDIBImage(long printDC, byte[] image,
1514                                      float destX, float destY,
1515                                      float destWidth, float destHeight,
1516                                      float srcX, float srcY,
1517                                      float srcWidth, float srcHeight,
1518                                      int bitCount, byte[] bmiColors);
1519 
1520 
1521     //** BEGIN Functions called by native code for querying/updating attributes
1522 
1523     private final String getPrinterAttrib() {
1524         // getPrintService will get current print service or default if none
1525         PrintService service = this.getPrintService();
1526         String name = (service != null) ? service.getName() : null;
1527         return name;
1528     }
1529 
1530     /* SheetCollate */
1531     private final boolean getCollateAttrib() {
1532         return (mAttCollate == 1);
1533     }
1534 
1535     private void setCollateAttrib(Attribute attr) {
1536         if (attr == SheetCollate.COLLATED) {
1537             mAttCollate = 1; // DMCOLLATE_TRUE
1538         } else {
1539             mAttCollate = 0; // DMCOLLATE_FALSE
1540         }
1541     }
1542 
1543     private void setCollateAttrib(Attribute attr,
1544                                   PrintRequestAttributeSet set) {
1545         setCollateAttrib(attr);
1546         set.add(attr);
1547     }
1548 
1549     /* Orientation */
1550 
1551     private final int getOrientAttrib() {
1552         int orient = PageFormat.PORTRAIT;
1553         OrientationRequested orientReq = (attributes == null) ? null :
1554             (OrientationRequested)attributes.get(OrientationRequested.class);
1555         if (orientReq != null) {
1556             if (orientReq == OrientationRequested.REVERSE_LANDSCAPE) {
1557                 orient = PageFormat.REVERSE_LANDSCAPE;
1558             } else if (orientReq == OrientationRequested.LANDSCAPE) {
1559                 orient = PageFormat.LANDSCAPE;
1560             }
1561         }
1562 
1563         return orient;
1564     }
1565 
1566     private void setOrientAttrib(Attribute attr,
1567                                  PrintRequestAttributeSet set) {
1568         if (set != null) {
1569             set.add(attr);
1570         }
1571     }
1572 
1573     /* Copies and Page Range. */
1574     private final int getCopiesAttrib() {
1575         return getCopiesInt();
1576      }
1577 
1578     private final void setRangeCopiesAttribute(int from, int to,
1579                                                boolean isRangeSet,
1580                                                int copies) {
1581         if (attributes != null) {
1582             if (isRangeSet) {
1583                 attributes.add(new PageRanges(from, to));
1584                 setPageRange(from, to);
1585             }
1586             attributes.add(new Copies(copies));
1587             /* Since this is called from native to tell Java to sync
1588              * up with native, we don't call this class's own setCopies()
1589              * method which is mainly to send the value down to native
1590              */
1591             super.setCopies(copies);
1592             mAttCopies = copies;
1593         }
1594     }
1595 
1596     //returns 1-based index for "From" page
1597     private final int getFromPageAttrib() {
1598         if (attributes != null) {
1599             PageRanges pageRangesAttr =
1600                 (PageRanges)attributes.get(PageRanges.class);
1601             if (pageRangesAttr != null) {
1602                 int[][] range = pageRangesAttr.getMembers();
1603                 return range[0][0];
1604             }
1605         }
1606         return getMinPageAttrib();
1607     }
1608 
1609     //returns 1-based index for "To" page
1610     private final int getToPageAttrib() {
1611         if (attributes != null) {
1612             PageRanges pageRangesAttr =
1613                 (PageRanges)attributes.get(PageRanges.class);
1614             if (pageRangesAttr != null) {
1615                 int[][] range = pageRangesAttr.getMembers();
1616                 return range[range.length-1][1];
1617             }
1618         }
1619         return getMaxPageAttrib();
1620     }
1621 
1622     private final int getMinPageAttrib() {
1623         if (attributes != null) {
1624             SunMinMaxPage s =
1625                 (SunMinMaxPage)attributes.get(SunMinMaxPage.class);
1626             if (s != null) {
1627                 return s.getMin();
1628             }
1629         }
1630         return 1;
1631     }
1632 
1633     private final int getMaxPageAttrib() {
1634         if (attributes != null) {
1635             SunMinMaxPage s =
1636                 (SunMinMaxPage)attributes.get(SunMinMaxPage.class);
1637             if (s != null) {
1638                 return s.getMax();
1639             }
1640         }
1641 
1642         Pageable pageable = getPageable();
1643         if (pageable != null) {
1644             int numPages = pageable.getNumberOfPages();
1645             if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) {
1646                 numPages = MAX_UNKNOWN_PAGES;
1647             }
1648             return  ((numPages == 0) ? 1 : numPages);
1649         }
1650 
1651         return Integer.MAX_VALUE;
1652     }
1653 
1654     private final boolean getDestAttrib() {
1655         return (mDestination != null);
1656     }
1657 
1658     /* Quality */
1659     private final int getQualityAttrib() {
1660         return mAttQuality;
1661     }
1662 
1663     private void setQualityAttrib(Attribute attr) {
1664         if (attr == PrintQuality.HIGH) {
1665             mAttQuality = -4; // DMRES_HIGH
1666         } else if (attr == PrintQuality.NORMAL) {
1667             mAttQuality = -3; // DMRES_MEDIUM
1668         } else {
1669             mAttQuality = -2; // DMRES_LOW
1670         }
1671     }
1672 
1673     private void setQualityAttrib(Attribute attr,
1674                                   PrintRequestAttributeSet set) {
1675         setQualityAttrib(attr);
1676         set.add(attr);
1677     }
1678 
1679     /* Color/Chromaticity */
1680     private final int getColorAttrib() {
1681         return mAttChromaticity;
1682     }
1683 
1684     private void setColorAttrib(Attribute attr) {
1685         if (attr == Chromaticity.COLOR) {
1686             mAttChromaticity = 2; // DMCOLOR_COLOR
1687         } else {
1688             mAttChromaticity = 1; // DMCOLOR_MONOCHROME
1689         }
1690     }
1691 
1692     private void setColorAttrib(Attribute attr,
1693                                   PrintRequestAttributeSet set) {
1694         setColorAttrib(attr);
1695         set.add(attr);
1696     }
1697 
1698     /* Sides */
1699     private final int getSidesAttrib() {
1700         return mAttSides;
1701     }
1702 
1703     private void setSidesAttrib(Attribute attr) {
1704         if (attr == Sides.TWO_SIDED_LONG_EDGE) {
1705             mAttSides = 2; // DMDUP_VERTICAL
1706         } else if (attr == Sides.TWO_SIDED_SHORT_EDGE) {
1707             mAttSides = 3; // DMDUP_HORIZONTAL
1708         } else { // Sides.ONE_SIDED
1709             mAttSides = 1;
1710         }
1711     }
1712 
1713     private void setSidesAttrib(Attribute attr,
1714                                 PrintRequestAttributeSet set) {
1715         setSidesAttrib(attr);
1716         set.add(attr);
1717     }
1718 
1719     /** MediaSizeName / dmPaper */
1720     private final int[] getWin32MediaAttrib() {
1721         int wid_ht[] = {0, 0};
1722         if (attributes != null) {
1723             Media media = (Media)attributes.get(Media.class);
1724             if (media instanceof MediaSizeName) {
1725                 MediaSizeName msn = (MediaSizeName)media;
1726                 MediaSize ms = MediaSize.getMediaSizeForName(msn);
1727                 if (ms != null) {
1728                     wid_ht[0] = (int)(ms.getX(MediaSize.INCH) * 72.0);
1729                     wid_ht[1] = (int)(ms.getY(MediaSize.INCH) * 72.0);
1730                 }
1731             }
1732         }
1733         return wid_ht;
1734     }
1735 
1736     private void setWin32MediaAttrib(Attribute attr) {
1737         if (!(attr instanceof MediaSizeName)) {
1738             return;
1739         }
1740         MediaSizeName msn = (MediaSizeName)attr;
1741         mAttMediaSizeName = ((Win32PrintService)myService).findPaperID(msn);
1742     }
1743 
1744     private void setWin32MediaAttrib(int dmIndex, int width, int length) {
1745        MediaSizeName msn =
1746            ((Win32PrintService)myService).findWin32Media(dmIndex);
1747         if (msn == null) {
1748             msn = ((Win32PrintService)myService).
1749                 findMatchingMediaSizeNameMM((float)width, (float)length);
1750         }
1751 
1752         if (msn != null) {
1753             if (attributes != null) {
1754                 attributes.add(msn);
1755             }
1756         }
1757         mAttMediaSizeName = dmIndex;
1758     }
1759 
1760     /* MediaTray / dmTray */
1761     private void setMediaTrayAttrib(Attribute attr) {
1762         if (attr == MediaTray.BOTTOM) {
1763             mAttMediaTray = 2;    // DMBIN_LOWER
1764         } else if (attr == MediaTray.ENVELOPE) {
1765             mAttMediaTray = 5;    // DMBIN_ENVELOPE
1766         } else if (attr == MediaTray.LARGE_CAPACITY) {
1767             mAttMediaTray = 11;      // DMBIN_LARGECAPACITY
1768         } else if (attr == MediaTray.MAIN) {
1769             mAttMediaTray =1;               // DMBIN_UPPER
1770         } else if (attr == MediaTray.MANUAL) {
1771             mAttMediaTray = 4;              // DMBIN_MANUAL
1772         } else if (attr == MediaTray.MIDDLE) {
1773             mAttMediaTray = 3;              // DMBIN_MIDDLE
1774         } else if (attr == MediaTray.SIDE) {
1775             // no equivalent predefined value
1776             mAttMediaTray = 7;              // DMBIN_AUTO
1777         } else if (attr == MediaTray.TOP) {
1778             mAttMediaTray =1;               // DMBIN_UPPER
1779         } else {
1780             if (attr instanceof Win32MediaTray) {
1781                 mAttMediaTray = ((Win32MediaTray)attr).winID;
1782             } else {
1783                 mAttMediaTray = 1;  // default
1784             }
1785         }
1786     }
1787 
1788     private void setMediaTrayAttrib(int dmBinID) {
1789         mAttMediaTray = dmBinID;
1790         MediaTray tray = ((Win32PrintService)myService).findMediaTray(dmBinID);
1791     }
1792 
1793     private int getMediaTrayAttrib() {
1794         return mAttMediaTray;
1795     }
1796 
1797     private final int getSelectAttrib() {
1798         if (attributes != null) {
1799             SunPageSelection pages =
1800                 (SunPageSelection)attributes.get(SunPageSelection.class);
1801             if (pages == SunPageSelection.RANGE) {
1802                 return PD_PAGENUMS;
1803             } else if (pages == SunPageSelection.SELECTION) {
1804                 return PD_SELECTION;
1805             } else if (pages ==  SunPageSelection.ALL) {
1806                 return PD_ALLPAGES;
1807             }
1808         }
1809         return PD_NOSELECTION;
1810     }
1811 
1812     private final 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 final 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 
1905     /* Printer Resolution. See also getXRes() and getYRes() */
1906     private final void setResolutionDPI(int xres, int yres) {
1907         if (attributes != null) {
1908             PrinterResolution res =
1909                 new PrinterResolution(xres, yres, PrinterResolution.DPI);
1910             attributes.add(res);
1911         }
1912         mAttXRes = xres;
1913         mAttYRes = yres;
1914     }
1915 
1916     private void setResolutionAttrib(Attribute attr) {
1917         PrinterResolution pr = (PrinterResolution)attr;
1918         mAttXRes = pr.getCrossFeedResolution(PrinterResolution.DPI);
1919         mAttYRes = pr.getFeedResolution(PrinterResolution.DPI);
1920     }
1921 
1922     private void setPrinterNameAttrib(String printerName) {
1923         PrintService service = this.getPrintService();
1924 
1925         if (printerName == null) {
1926             return;
1927         }
1928 
1929         if (service != null && printerName.equals(service.getName())) {
1930             return;
1931         } else {
1932             PrintService []services = PrinterJob.lookupPrintServices();
1933             for (int i=0; i<services.length; i++) {
1934                 if (printerName.equals(services[i].getName())) {
1935 
1936                     try {
1937                         this.setPrintService(services[i]);
1938                     } catch (PrinterException e) {
1939                     }
1940                     return;
1941                 }
1942             }
1943         }
1944     //** END Functions called by native code for querying/updating attributes
1945 
1946    }
1947 
1948 class PrintToFileErrorDialog extends Dialog implements ActionListener{
1949     public PrintToFileErrorDialog(Frame parent, String title, String message,
1950                            String buttonText) {
1951         super(parent, title, true);
1952         init (parent, title, message, buttonText);
1953     }
1954 
1955     public PrintToFileErrorDialog(Dialog parent, String title, String message,
1956                            String buttonText) {
1957         super(parent, title, true);
1958         init (parent, title, message, buttonText);
1959     }
1960 
1961     private void init(Component parent, String  title, String message,
1962                       String buttonText) {
1963         Panel p = new Panel();
1964         add("Center", new Label(message));
1965         Button btn = new Button(buttonText);
1966         btn.addActionListener(this);
1967         p.add(btn);
1968         add("South", p);
1969         pack();
1970 
1971         Dimension dDim = getSize();
1972         if (parent != null) {
1973             Rectangle fRect = parent.getBounds();
1974             setLocation(fRect.x + ((fRect.width - dDim.width) / 2),
1975                         fRect.y + ((fRect.height - dDim.height) / 2));
1976         }
1977     }
1978 
1979     public void actionPerformed(ActionEvent event) {
1980         setVisible(false);
1981         dispose();
1982         return;
1983     }
1984 }
1985 
1986 
1987 
1988 
1989     /**
1990      * Initialize JNI field and method ids
1991      */
1992     private static native void initIDs();
1993 
1994 }