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