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 }