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