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 }