1 /* 2 * Copyright (c) 1998, 2012, 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.print; 27 28 import java.io.FilePermission; 29 30 import java.awt.Color; 31 import java.awt.Dialog; 32 import java.awt.Frame; 33 import java.awt.Graphics; 34 import java.awt.Graphics2D; 35 import java.awt.GraphicsConfiguration; 36 import java.awt.GraphicsEnvironment; 37 import java.awt.HeadlessException; 38 import java.awt.KeyboardFocusManager; 39 import java.awt.Rectangle; 40 import java.awt.Shape; 41 import java.awt.geom.AffineTransform; 42 import java.awt.geom.Area; 43 import java.awt.geom.Point2D; 44 import java.awt.geom.Rectangle2D; 45 import java.awt.image.BufferedImage; 46 import java.awt.print.Book; 47 import java.awt.print.Pageable; 48 import java.awt.print.PageFormat; 49 import java.awt.print.Paper; 50 import java.awt.print.Printable; 51 import java.awt.print.PrinterAbortException; 52 import java.awt.print.PrinterException; 53 import java.awt.print.PrinterJob; 54 import java.awt.Window; 55 import java.io.File; 56 import java.io.IOException; 57 import java.util.ArrayList; 58 import java.util.Enumeration; 59 import java.util.Locale; 60 import sun.awt.image.ByteInterleavedRaster; 61 62 import javax.print.Doc; 63 import javax.print.DocFlavor; 64 import javax.print.DocPrintJob; 65 import javax.print.PrintException; 66 import javax.print.PrintService; 67 import javax.print.PrintServiceLookup; 68 import javax.print.ServiceUI; 69 import javax.print.StreamPrintService; 70 import javax.print.StreamPrintServiceFactory; 71 import javax.print.attribute.Attribute; 72 import javax.print.attribute.AttributeSet; 73 import javax.print.attribute.HashPrintRequestAttributeSet; 74 import javax.print.attribute.PrintRequestAttributeSet; 75 import javax.print.attribute.Size2DSyntax; 76 import javax.print.attribute.standard.Chromaticity; 77 import javax.print.attribute.standard.Copies; 78 import javax.print.attribute.standard.Destination; 79 import javax.print.attribute.standard.DialogTypeSelection; 80 import javax.print.attribute.standard.Fidelity; 81 import javax.print.attribute.standard.JobName; 82 import javax.print.attribute.standard.JobSheets; 83 import javax.print.attribute.standard.Media; 84 import javax.print.attribute.standard.MediaPrintableArea; 85 import javax.print.attribute.standard.MediaSize; 86 import javax.print.attribute.standard.MediaSizeName; 87 import javax.print.attribute.standard.OrientationRequested; 88 import javax.print.attribute.standard.PageRanges; 89 import javax.print.attribute.standard.PrinterState; 90 import javax.print.attribute.standard.PrinterStateReason; 91 import javax.print.attribute.standard.PrinterStateReasons; 92 import javax.print.attribute.standard.PrinterIsAcceptingJobs; 93 import javax.print.attribute.standard.RequestingUserName; 94 import javax.print.attribute.standard.SheetCollate; 95 import javax.print.attribute.standard.Sides; 96 97 import sun.print.PageableDoc; 98 import sun.print.ServiceDialog; 99 import sun.print.SunPrinterJobService; 100 import sun.print.SunPageSelection; 101 102 /** 103 * A class which rasterizes a printer job. 104 * 105 * @author Richard Blanchard 106 */ 107 public abstract class RasterPrinterJob extends PrinterJob { 108 109 /* Class Constants */ 110 111 /* Printer destination type. */ 112 protected static final int PRINTER = 0; 113 114 /* File destination type. */ 115 protected static final int FILE = 1; 116 117 /* Stream destination type. */ 118 protected static final int STREAM = 2; 119 120 /** 121 * Pageable MAX pages 122 */ 123 protected static final int MAX_UNKNOWN_PAGES = 9999; 124 125 protected static final int PD_ALLPAGES = 0x00000000; 126 protected static final int PD_SELECTION = 0x00000001; 127 protected static final int PD_PAGENUMS = 0x00000002; 128 protected static final int PD_NOSELECTION = 0x00000004; 129 130 /** 131 * Maximum amount of memory in bytes to use for the 132 * buffered image "band". 4Mb is a compromise between 133 * limiting the number of bands on hi-res printers and 134 * not using too much of the Java heap or causing paging 135 * on systems with little RAM. 136 */ 137 private static final int MAX_BAND_SIZE = (1024 * 1024 * 4); 138 139 /* Dots Per Inch */ 140 private static final float DPI = 72.0f; 141 142 /** 143 * Useful mainly for debugging, this system property 144 * can be used to force the printing code to print 145 * using a particular pipeline. The two currently 146 * supported values are FORCE_RASTER and FORCE_PDL. 147 */ 148 private static final String FORCE_PIPE_PROP = "sun.java2d.print.pipeline"; 149 150 /** 151 * When the system property FORCE_PIPE_PROP has this value 152 * then each page of a print job will be rendered through 153 * the raster pipeline. 154 */ 155 private static final String FORCE_RASTER = "raster"; 156 157 /** 158 * When the system property FORCE_PIPE_PROP has this value 159 * then each page of a print job will be rendered through 160 * the PDL pipeline. 161 */ 162 private static final String FORCE_PDL = "pdl"; 163 164 /** 165 * When the system property SHAPE_TEXT_PROP has this value 166 * then text is always rendered as a shape, and no attempt is made 167 * to match the font through GDI 168 */ 169 private static final String SHAPE_TEXT_PROP = "sun.java2d.print.shapetext"; 170 171 /** 172 * values obtained from System properties in static initialiser block 173 */ 174 public static boolean forcePDL = false; 175 public static boolean forceRaster = false; 176 public static boolean shapeTextProp = false; 177 178 static { 179 /* The system property FORCE_PIPE_PROP 180 * can be used to force the printing code to 181 * use a particular pipeline. Either the raster 182 * pipeline or the pdl pipeline can be forced. 183 */ 184 String forceStr = 185 (String)java.security.AccessController.doPrivileged( 186 new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP)); 187 188 if (forceStr != null) { 189 if (forceStr.equalsIgnoreCase(FORCE_PDL)) { 190 forcePDL = true; 191 } else if (forceStr.equalsIgnoreCase(FORCE_RASTER)) { 192 forceRaster = true; 193 } 194 } 195 196 String shapeTextStr = 197 (String)java.security.AccessController.doPrivileged( 198 new sun.security.action.GetPropertyAction(SHAPE_TEXT_PROP)); 199 200 if (shapeTextStr != null) { 201 shapeTextProp = true; 202 } 203 } 204 205 /* Instance Variables */ 206 207 /** 208 * Used to minimize GC & reallocation of band when printing 209 */ 210 private int cachedBandWidth = 0; 211 private int cachedBandHeight = 0; 212 private BufferedImage cachedBand = null; 213 214 /** 215 * The number of book copies to be printed. 216 */ 217 private int mNumCopies = 1; 218 219 /** 220 * Collation effects the order of the pages printed 221 * when multiple copies are requested. For two copies 222 * of a three page document the page order is: 223 * mCollate true: 1, 2, 3, 1, 2, 3 224 * mCollate false: 1, 1, 2, 2, 3, 3 225 */ 226 private boolean mCollate = false; 227 228 /** 229 * The zero based indices of the first and last 230 * pages to be printed. If 'mFirstPage' is 231 * UNDEFINED_PAGE_NUM then the first page to 232 * be printed is page 0. If 'mLastPage' is 233 * UNDEFINED_PAGE_NUM then the last page to 234 * be printed is the last one in the book. 235 */ 236 private int mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; 237 private int mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; 238 239 /** 240 * The previous print stream Paper 241 * Used to check if the paper size has changed such that the 242 * implementation needs to emit the new paper size information 243 * into the print stream. 244 * Since we do our own rotation, and the margins aren't relevant, 245 * Its strictly the dimensions of the paper that we will check. 246 */ 247 private Paper previousPaper; 248 249 /** 250 * The document to be printed. It is initialized to an 251 * empty (zero pages) book. 252 */ 253 // MacOSX - made protected so subclasses can reference it. 254 protected Pageable mDocument = new Book(); 255 256 /** 257 * The name of the job being printed. 258 */ 259 private String mDocName = "Java Printing"; 260 261 262 /** 263 * Printing cancellation flags 264 */ 265 // MacOSX - made protected so subclasses can reference it. 266 protected boolean performingPrinting = false; 267 // MacOSX - made protected so subclasses can reference it. 268 protected boolean userCancelled = false; 269 270 /** 271 * Print to file permission variables. 272 */ 273 private FilePermission printToFilePermission; 274 275 /** 276 * List of areas & the graphics state for redrawing 277 */ 278 private ArrayList redrawList = new ArrayList(); 279 280 281 /* variables representing values extracted from an attribute set. 282 * These take precedence over values set on a printer job 283 */ 284 private int copiesAttr; 285 private String jobNameAttr; 286 private String userNameAttr; 287 private PageRanges pageRangesAttr; 288 protected Sides sidesAttr; 289 protected String destinationAttr; 290 protected boolean noJobSheet = false; 291 protected int mDestType = RasterPrinterJob.FILE; 292 protected String mDestination = ""; 293 protected boolean collateAttReq = false; 294 295 /** 296 * Device rotation flag, if it support 270, this is set to true; 297 */ 298 protected boolean landscapeRotates270 = false; 299 300 /** 301 * attributes used by no-args page and print dialog and print method to 302 * communicate state 303 */ 304 protected PrintRequestAttributeSet attributes = null; 305 306 /** 307 * Class to keep state information for redrawing areas 308 * "region" is an area at as a high a resolution as possible. 309 * The redrawing code needs to look at sx, sy to calculate the scale 310 * to device resolution. 311 */ 312 private class GraphicsState { 313 Rectangle2D region; // Area of page to repaint 314 Shape theClip; // image drawing clip. 315 AffineTransform theTransform; // to transform clip to dev coords. 316 double sx; // X scale from region to device resolution 317 double sy; // Y scale from region to device resolution 318 } 319 320 /** 321 * Service for this job 322 */ 323 protected PrintService myService; 324 325 /* Constructors */ 326 327 public RasterPrinterJob() 328 { 329 } 330 331 /* Abstract Methods */ 332 333 /** 334 * Returns the resolution in dots per inch across the width 335 * of the page. 336 */ 337 abstract protected double getXRes(); 338 339 /** 340 * Returns the resolution in dots per inch down the height 341 * of the page. 342 */ 343 abstract protected double getYRes(); 344 345 /** 346 * Must be obtained from the current printer. 347 * Value is in device pixels. 348 * Not adjusted for orientation of the paper. 349 */ 350 abstract protected double getPhysicalPrintableX(Paper p); 351 352 /** 353 * Must be obtained from the current printer. 354 * Value is in device pixels. 355 * Not adjusted for orientation of the paper. 356 */ 357 abstract protected double getPhysicalPrintableY(Paper p); 358 359 /** 360 * Must be obtained from the current printer. 361 * Value is in device pixels. 362 * Not adjusted for orientation of the paper. 363 */ 364 abstract protected double getPhysicalPrintableWidth(Paper p); 365 366 /** 367 * Must be obtained from the current printer. 368 * Value is in device pixels. 369 * Not adjusted for orientation of the paper. 370 */ 371 abstract protected double getPhysicalPrintableHeight(Paper p); 372 373 /** 374 * Must be obtained from the current printer. 375 * Value is in device pixels. 376 * Not adjusted for orientation of the paper. 377 */ 378 abstract protected double getPhysicalPageWidth(Paper p); 379 380 /** 381 * Must be obtained from the current printer. 382 * Value is in device pixels. 383 * Not adjusted for orientation of the paper. 384 */ 385 abstract protected double getPhysicalPageHeight(Paper p); 386 387 /** 388 * Begin a new page. 389 */ 390 abstract protected void startPage(PageFormat format, Printable painter, 391 int index, boolean paperChanged) 392 throws PrinterException; 393 394 /** 395 * End a page. 396 */ 397 abstract protected void endPage(PageFormat format, Printable painter, 398 int index) 399 throws PrinterException; 400 401 /** 402 * Prints the contents of the array of ints, 'data' 403 * to the current page. The band is placed at the 404 * location (x, y) in device coordinates on the 405 * page. The width and height of the band is 406 * specified by the caller. 407 */ 408 abstract protected void printBand(byte[] data, int x, int y, 409 int width, int height) 410 throws PrinterException; 411 412 /* Instance Methods */ 413 414 /** 415 * save graphics state of a PathGraphics for later redrawing 416 * of part of page represented by the region in that state 417 */ 418 419 public void saveState(AffineTransform at, Shape clip, 420 Rectangle2D region, double sx, double sy) { 421 GraphicsState gstate = new GraphicsState(); 422 gstate.theTransform = at; 423 gstate.theClip = clip; 424 gstate.region = region; 425 gstate.sx = sx; 426 gstate.sy = sy; 427 redrawList.add(gstate); 428 } 429 430 431 /* 432 * A convenience method which returns the default service 433 * for 2D <code>PrinterJob</code>s. 434 * May return null if there is no suitable default (although there 435 * may still be 2D services available). 436 * @return default 2D print service, or null. 437 * @since 1.4 438 */ 439 protected static PrintService lookupDefaultPrintService() { 440 PrintService service = PrintServiceLookup.lookupDefaultPrintService(); 441 442 /* Pageable implies Printable so checking both isn't strictly needed */ 443 if (service != null && 444 service.isDocFlavorSupported( 445 DocFlavor.SERVICE_FORMATTED.PAGEABLE) && 446 service.isDocFlavorSupported( 447 DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 448 return service; 449 } else { 450 PrintService []services = 451 PrintServiceLookup.lookupPrintServices( 452 DocFlavor.SERVICE_FORMATTED.PAGEABLE, null); 453 if (services.length > 0) { 454 return services[0]; 455 } 456 } 457 return null; 458 } 459 460 /** 461 * Returns the service (printer) for this printer job. 462 * Implementations of this class which do not support print services 463 * may return null; 464 * @return the service for this printer job. 465 * 466 */ 467 public PrintService getPrintService() { 468 if (myService == null) { 469 PrintService svc = PrintServiceLookup.lookupDefaultPrintService(); 470 if (svc != null && 471 svc.isDocFlavorSupported( 472 DocFlavor.SERVICE_FORMATTED.PAGEABLE)) { 473 try { 474 setPrintService(svc); 475 myService = svc; 476 } catch (PrinterException e) { 477 } 478 } 479 if (myService == null) { 480 PrintService[] svcs = PrintServiceLookup.lookupPrintServices( 481 DocFlavor.SERVICE_FORMATTED.PAGEABLE, null); 482 if (svcs.length > 0) { 483 try { 484 setPrintService(svcs[0]); 485 myService = svcs[0]; 486 } catch (PrinterException e) { 487 } 488 } 489 } 490 } 491 return myService; 492 } 493 494 /** 495 * Associate this PrinterJob with a new PrintService. 496 * 497 * Throws <code>PrinterException</code> if the specified service 498 * cannot support the <code>Pageable</code> and 499 * <code>Printable</code> interfaces necessary to support 2D printing. 500 * @param a print service which supports 2D printing. 501 * 502 * @throws PrinterException if the specified service does not support 503 * 2D printing or no longer available. 504 */ 505 public void setPrintService(PrintService service) 506 throws PrinterException { 507 if (service == null) { 508 throw new PrinterException("Service cannot be null"); 509 } else if (!(service instanceof StreamPrintService) && 510 service.getName() == null) { 511 throw new PrinterException("Null PrintService name."); 512 } else { 513 // Check the list of services. This service may have been 514 // deleted already 515 PrinterState prnState = (PrinterState)service.getAttribute( 516 PrinterState.class); 517 if (prnState == PrinterState.STOPPED) { 518 PrinterStateReasons prnStateReasons = 519 (PrinterStateReasons)service.getAttribute( 520 PrinterStateReasons.class); 521 if ((prnStateReasons != null) && 522 (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN))) 523 { 524 throw new PrinterException("PrintService is no longer available."); 525 } 526 } 527 528 529 if (service.isDocFlavorSupported( 530 DocFlavor.SERVICE_FORMATTED.PAGEABLE) && 531 service.isDocFlavorSupported( 532 DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 533 myService = service; 534 } else { 535 throw new PrinterException("Not a 2D print service: " + service); 536 } 537 } 538 } 539 540 private PageFormat attributeToPageFormat(PrintService service, 541 PrintRequestAttributeSet attSet) { 542 PageFormat page = defaultPage(); 543 544 if (service == null) { 545 return page; 546 } 547 548 OrientationRequested orient = (OrientationRequested) 549 attSet.get(OrientationRequested.class); 550 if (orient == null) { 551 orient = (OrientationRequested) 552 service.getDefaultAttributeValue(OrientationRequested.class); 553 } 554 if (orient == OrientationRequested.REVERSE_LANDSCAPE) { 555 page.setOrientation(PageFormat.REVERSE_LANDSCAPE); 556 } else if (orient == OrientationRequested.LANDSCAPE) { 557 page.setOrientation(PageFormat.LANDSCAPE); 558 } else { 559 page.setOrientation(PageFormat.PORTRAIT); 560 } 561 562 Media media = (Media)attSet.get(Media.class); 563 if (media == null) { 564 media = 565 (Media)service.getDefaultAttributeValue(Media.class); 566 } 567 if (!(media instanceof MediaSizeName)) { 568 media = MediaSizeName.NA_LETTER; 569 } 570 MediaSize size = 571 MediaSize.getMediaSizeForName((MediaSizeName)media); 572 if (size == null) { 573 size = MediaSize.NA.LETTER; 574 } 575 Paper paper = new Paper(); 576 float dim[] = size.getSize(1); //units == 1 to avoid FP error 577 double w = Math.rint((dim[0]*72.0)/Size2DSyntax.INCH); 578 double h = Math.rint((dim[1]*72.0)/Size2DSyntax.INCH); 579 paper.setSize(w, h); 580 MediaPrintableArea area = 581 (MediaPrintableArea) 582 attSet.get(MediaPrintableArea.class); 583 double ix, iw, iy, ih; 584 585 if (area != null) { 586 // Should pass in same unit as updatePageAttributes 587 // to avoid rounding off errors. 588 ix = Math.rint( 589 area.getX(MediaPrintableArea.INCH) * DPI); 590 iy = Math.rint( 591 area.getY(MediaPrintableArea.INCH) * DPI); 592 iw = Math.rint( 593 area.getWidth(MediaPrintableArea.INCH) * DPI); 594 ih = Math.rint( 595 area.getHeight(MediaPrintableArea.INCH) * DPI); 596 } 597 else { 598 if (w >= 72.0 * 6.0) { 599 ix = 72.0; 600 iw = w - 2 * 72.0; 601 } else { 602 ix = w / 6.0; 603 iw = w * 0.75; 604 } 605 if (h >= 72.0 * 6.0) { 606 iy = 72.0; 607 ih = h - 2 * 72.0; 608 } else { 609 iy = h / 6.0; 610 ih = h * 0.75; 611 } 612 } 613 paper.setImageableArea(ix, iy, iw, ih); 614 page.setPaper(paper); 615 return page; 616 } 617 618 protected void updatePageAttributes(PrintService service, 619 PageFormat page) { 620 if (this.attributes == null) { 621 this.attributes = new HashPrintRequestAttributeSet(); 622 } 623 624 updateAttributesWithPageFormat(service, page, this.attributes); 625 } 626 627 protected void updateAttributesWithPageFormat(PrintService service, 628 PageFormat page, 629 PrintRequestAttributeSet pageAttributes) { 630 if (service == null || page == null || pageAttributes == null) { 631 return; 632 } 633 634 float x = (float)Math.rint( 635 (page.getPaper().getWidth()*Size2DSyntax.INCH)/ 636 (72.0))/(float)Size2DSyntax.INCH; 637 float y = (float)Math.rint( 638 (page.getPaper().getHeight()*Size2DSyntax.INCH)/ 639 (72.0))/(float)Size2DSyntax.INCH; 640 641 // We should limit the list where we search the matching 642 // media, this will prevent mapping to wrong media ex. Ledger 643 // can be mapped to B. Especially useful when creating 644 // custom MediaSize. 645 Media[] mediaList = (Media[])service.getSupportedAttributeValues( 646 Media.class, null, null); 647 Media media = null; 648 try { 649 media = CustomMediaSizeName.findMedia(mediaList, x, y, 650 Size2DSyntax.INCH); 651 } catch (IllegalArgumentException iae) { 652 } 653 if ((media == null) || 654 !(service.isAttributeValueSupported(media, null, null))) { 655 media = (Media)service.getDefaultAttributeValue(Media.class); 656 } 657 658 OrientationRequested orient; 659 switch (page.getOrientation()) { 660 case PageFormat.LANDSCAPE : 661 orient = OrientationRequested.LANDSCAPE; 662 break; 663 case PageFormat.REVERSE_LANDSCAPE: 664 orient = OrientationRequested.REVERSE_LANDSCAPE; 665 break; 666 default: 667 orient = OrientationRequested.PORTRAIT; 668 } 669 670 if (media != null) { 671 pageAttributes.add(media); 672 } 673 pageAttributes.add(orient); 674 675 float ix = (float)(page.getPaper().getImageableX()/DPI); 676 float iw = (float)(page.getPaper().getImageableWidth()/DPI); 677 float iy = (float)(page.getPaper().getImageableY()/DPI); 678 float ih = (float)(page.getPaper().getImageableHeight()/DPI); 679 if (ix < 0) ix = 0f; if (iy < 0) iy = 0f; 680 try { 681 pageAttributes.add(new MediaPrintableArea(ix, iy, iw, ih, 682 MediaPrintableArea.INCH)); 683 } catch (IllegalArgumentException iae) { 684 } 685 } 686 687 /** 688 * Display a dialog to the user allowing the modification of a 689 * PageFormat instance. 690 * The <code>page</code> argument is used to initialize controls 691 * in the page setup dialog. 692 * If the user cancels the dialog, then the method returns the 693 * original <code>page</code> object unmodified. 694 * If the user okays the dialog then the method returns a new 695 * PageFormat object with the indicated changes. 696 * In either case the original <code>page</code> object will 697 * not be modified. 698 * @param page the default PageFormat presented to the user 699 * for modification 700 * @return the original <code>page</code> object if the dialog 701 * is cancelled, or a new PageFormat object containing 702 * the format indicated by the user if the dialog is 703 * acknowledged 704 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 705 * returns true. 706 * @see java.awt.GraphicsEnvironment#isHeadless 707 * @since 1.2 708 */ 709 public PageFormat pageDialog(PageFormat page) 710 throws HeadlessException { 711 if (GraphicsEnvironment.isHeadless()) { 712 throw new HeadlessException(); 713 } 714 715 final GraphicsConfiguration gc = 716 GraphicsEnvironment.getLocalGraphicsEnvironment(). 717 getDefaultScreenDevice().getDefaultConfiguration(); 718 719 PrintService service = 720 (PrintService)java.security.AccessController.doPrivileged( 721 new java.security.PrivilegedAction() { 722 public Object run() { 723 PrintService service = getPrintService(); 724 if (service == null) { 725 ServiceDialog.showNoPrintService(gc); 726 return null; 727 } 728 return service; 729 } 730 }); 731 732 if (service == null) { 733 return page; 734 } 735 updatePageAttributes(service, page); 736 737 PageFormat newPage = pageDialog(attributes); 738 739 if (newPage == null) { 740 return page; 741 } else { 742 return newPage; 743 } 744 } 745 746 /** 747 * return a PageFormat corresponding to the updated attributes, 748 * or null if the user cancelled the dialog. 749 */ 750 public PageFormat pageDialog(final PrintRequestAttributeSet attributes) 751 throws HeadlessException { 752 if (GraphicsEnvironment.isHeadless()) { 753 throw new HeadlessException(); 754 } 755 756 DialogTypeSelection dlg = 757 (DialogTypeSelection)attributes.get(DialogTypeSelection.class); 758 759 // Check for native, note that default dialog is COMMON. 760 if (dlg == DialogTypeSelection.NATIVE) { 761 PrintService pservice = getPrintService(); 762 PageFormat page = pageDialog(attributeToPageFormat(pservice, 763 attributes)); 764 updateAttributesWithPageFormat(pservice, page, attributes); 765 return page; 766 } 767 768 final GraphicsConfiguration gc = 769 GraphicsEnvironment.getLocalGraphicsEnvironment(). 770 getDefaultScreenDevice().getDefaultConfiguration(); 771 Rectangle bounds = gc.getBounds(); 772 int x = bounds.x+bounds.width/3; 773 int y = bounds.y+bounds.height/3; 774 775 PrintService service = 776 (PrintService)java.security.AccessController.doPrivileged( 777 new java.security.PrivilegedAction() { 778 public Object run() { 779 PrintService service = getPrintService(); 780 if (service == null) { 781 ServiceDialog.showNoPrintService(gc); 782 return null; 783 } 784 return service; 785 } 786 }); 787 788 if (service == null) { 789 return null; 790 } 791 792 ServiceDialog pageDialog = new ServiceDialog(gc, x, y, service, 793 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 794 attributes, (Frame)null); 795 pageDialog.show(); 796 797 if (pageDialog.getStatus() == ServiceDialog.APPROVE) { 798 PrintRequestAttributeSet newas = 799 pageDialog.getAttributes(); 800 Class amCategory = SunAlternateMedia.class; 801 802 if (attributes.containsKey(amCategory) && 803 !newas.containsKey(amCategory)) { 804 attributes.remove(amCategory); 805 } 806 attributes.addAll(newas); 807 return attributeToPageFormat(service, attributes); 808 } else { 809 return null; 810 } 811 } 812 813 protected PageFormat getPageFormatFromAttributes() { 814 if (attributes == null) { 815 return null; 816 } 817 return attributeToPageFormat(getPrintService(), this.attributes); 818 } 819 820 821 /** 822 * Presents the user a dialog for changing properties of the 823 * print job interactively. 824 * The services browsable here are determined by the type of 825 * service currently installed. 826 * If the application installed a StreamPrintService on this 827 * PrinterJob, only the available StreamPrintService (factories) are 828 * browsable. 829 * 830 * @param attributes to store changed properties. 831 * @return false if the user cancels the dialog and true otherwise. 832 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 833 * returns true. 834 * @see java.awt.GraphicsEnvironment#isHeadless 835 */ 836 public boolean printDialog(final PrintRequestAttributeSet attributes) 837 throws HeadlessException { 838 if (GraphicsEnvironment.isHeadless()) { 839 throw new HeadlessException(); 840 } 841 842 DialogTypeSelection dlg = 843 (DialogTypeSelection)attributes.get(DialogTypeSelection.class); 844 845 // Check for native, note that default dialog is COMMON. 846 if (dlg == DialogTypeSelection.NATIVE) { 847 this.attributes = attributes; 848 try { 849 debug_println("calling setAttributes in printDialog"); 850 setAttributes(attributes); 851 852 } catch (PrinterException e) { 853 854 } 855 856 boolean ret = printDialog(); 857 this.attributes = attributes; 858 return ret; 859 860 } 861 862 /* A security check has already been performed in the 863 * java.awt.print.printerJob.getPrinterJob method. 864 * So by the time we get here, it is OK for the current thread 865 * to print either to a file (from a Dialog we control!) or 866 * to a chosen printer. 867 * 868 * We raise privilege when we put up the dialog, to avoid 869 * the "warning applet window" banner. 870 */ 871 final GraphicsConfiguration gc = 872 GraphicsEnvironment.getLocalGraphicsEnvironment(). 873 getDefaultScreenDevice().getDefaultConfiguration(); 874 875 PrintService service = 876 (PrintService)java.security.AccessController.doPrivileged( 877 new java.security.PrivilegedAction() { 878 public Object run() { 879 PrintService service = getPrintService(); 880 if (service == null) { 881 ServiceDialog.showNoPrintService(gc); 882 return null; 883 } 884 return service; 885 } 886 }); 887 888 if (service == null) { 889 return false; 890 } 891 892 PrintService[] services; 893 StreamPrintServiceFactory[] spsFactories = null; 894 if (service instanceof StreamPrintService) { 895 spsFactories = lookupStreamPrintServices(null); 896 services = new StreamPrintService[spsFactories.length]; 897 for (int i=0; i<spsFactories.length; i++) { 898 services[i] = spsFactories[i].getPrintService(null); 899 } 900 } else { 901 services = 902 (PrintService[])java.security.AccessController.doPrivileged( 903 new java.security.PrivilegedAction() { 904 public Object run() { 905 PrintService[] services = PrinterJob.lookupPrintServices(); 906 return services; 907 } 908 }); 909 910 if ((services == null) || (services.length == 0)) { 911 /* 912 * No services but default PrintService exists? 913 * Create services using defaultService. 914 */ 915 services = new PrintService[1]; 916 services[0] = service; 917 } 918 } 919 920 Rectangle bounds = gc.getBounds(); 921 int x = bounds.x+bounds.width/3; 922 int y = bounds.y+bounds.height/3; 923 PrintService newService; 924 // temporarily add an attribute pointing back to this job. 925 PrinterJobWrapper jobWrapper = new PrinterJobWrapper(this); 926 attributes.add(jobWrapper); 927 try { 928 newService = 929 ServiceUI.printDialog(gc, x, y, 930 services, service, 931 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 932 attributes); 933 } catch (IllegalArgumentException iae) { 934 newService = ServiceUI.printDialog(gc, x, y, 935 services, services[0], 936 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 937 attributes); 938 } 939 attributes.remove(PrinterJobWrapper.class); 940 941 if (newService == null) { 942 return false; 943 } 944 945 if (!service.equals(newService)) { 946 try { 947 setPrintService(newService); 948 } catch (PrinterException e) { 949 /* 950 * The only time it would throw an exception is when 951 * newService is no longer available but we should still 952 * select this printer. 953 */ 954 myService = newService; 955 } 956 } 957 return true; 958 } 959 960 /** 961 * Presents the user a dialog for changing properties of the 962 * print job interactively. 963 * @returns false if the user cancels the dialog and 964 * true otherwise. 965 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 966 * returns true. 967 * @see java.awt.GraphicsEnvironment#isHeadless 968 */ 969 public boolean printDialog() throws HeadlessException { 970 971 if (GraphicsEnvironment.isHeadless()) { 972 throw new HeadlessException(); 973 } 974 975 PrintRequestAttributeSet attributes = 976 new HashPrintRequestAttributeSet(); 977 attributes.add(new Copies(getCopies())); 978 attributes.add(new JobName(getJobName(), null)); 979 boolean doPrint = printDialog(attributes); 980 if (doPrint) { 981 JobName jobName = (JobName)attributes.get(JobName.class); 982 if (jobName != null) { 983 setJobName(jobName.getValue()); 984 } 985 Copies copies = (Copies)attributes.get(Copies.class); 986 if (copies != null) { 987 setCopies(copies.getValue()); 988 } 989 990 Destination dest = (Destination)attributes.get(Destination.class); 991 992 if (dest != null) { 993 try { 994 mDestType = RasterPrinterJob.FILE; 995 mDestination = (new File(dest.getURI())).getPath(); 996 } catch (Exception e) { 997 mDestination = "out.prn"; 998 PrintService ps = getPrintService(); 999 if (ps != null) { 1000 Destination defaultDest = (Destination)ps. 1001 getDefaultAttributeValue(Destination.class); 1002 if (defaultDest != null) { 1003 mDestination = (new File(defaultDest.getURI())).getPath(); 1004 } 1005 } 1006 } 1007 } else { 1008 mDestType = RasterPrinterJob.PRINTER; 1009 PrintService ps = getPrintService(); 1010 if (ps != null) { 1011 mDestination = ps.getName(); 1012 } 1013 } 1014 } 1015 1016 return doPrint; 1017 } 1018 1019 /** 1020 * The pages in the document to be printed by this PrinterJob 1021 * are drawn by the Printable object 'painter'. The PageFormat 1022 * for each page is the default page format. 1023 * @param Printable Called to render each page of the document. 1024 */ 1025 public void setPrintable(Printable painter) { 1026 setPageable(new OpenBook(defaultPage(new PageFormat()), painter)); 1027 } 1028 1029 /** 1030 * The pages in the document to be printed by this PrinterJob 1031 * are drawn by the Printable object 'painter'. The PageFormat 1032 * of each page is 'format'. 1033 * @param Printable Called to render each page of the document. 1034 * @param PageFormat The size and orientation of each page to 1035 * be printed. 1036 */ 1037 public void setPrintable(Printable painter, PageFormat format) { 1038 setPageable(new OpenBook(format, painter)); 1039 updatePageAttributes(getPrintService(), format); 1040 } 1041 1042 /** 1043 * The pages in the document to be printed are held by the 1044 * Pageable instance 'document'. 'document' will be queried 1045 * for the number of pages as well as the PageFormat and 1046 * Printable for each page. 1047 * @param Pageable The document to be printed. It may not be null. 1048 * @exception NullPointerException the Pageable passed in was null. 1049 * @see PageFormat 1050 * @see Printable 1051 */ 1052 public void setPageable(Pageable document) throws NullPointerException { 1053 if (document != null) { 1054 mDocument = document; 1055 1056 } else { 1057 throw new NullPointerException(); 1058 } 1059 } 1060 1061 protected void initPrinter() { 1062 return; 1063 } 1064 1065 protected boolean isSupportedValue(Attribute attrval, 1066 PrintRequestAttributeSet attrset) { 1067 PrintService ps = getPrintService(); 1068 return 1069 (attrval != null && ps != null && 1070 ps.isAttributeValueSupported(attrval, 1071 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 1072 attrset)); 1073 } 1074 1075 /* subclasses may need to pull extra information out of the attribute set 1076 * They can override this method & call super.setAttributes() 1077 */ 1078 protected void setAttributes(PrintRequestAttributeSet attributes) 1079 throws PrinterException { 1080 /* reset all values to defaults */ 1081 setCollated(false); 1082 sidesAttr = null; 1083 pageRangesAttr = null; 1084 copiesAttr = 0; 1085 jobNameAttr = null; 1086 userNameAttr = null; 1087 destinationAttr = null; 1088 collateAttReq = false; 1089 1090 PrintService service = getPrintService(); 1091 if (attributes == null || service == null) { 1092 return; 1093 } 1094 1095 boolean fidelity = false; 1096 Fidelity attrFidelity = (Fidelity)attributes.get(Fidelity.class); 1097 if (attrFidelity != null && attrFidelity == Fidelity.FIDELITY_TRUE) { 1098 fidelity = true; 1099 } 1100 1101 if (fidelity == true) { 1102 AttributeSet unsupported = 1103 service.getUnsupportedAttributes( 1104 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 1105 attributes); 1106 if (unsupported != null) { 1107 throw new PrinterException("Fidelity cannot be satisfied"); 1108 } 1109 } 1110 1111 /* 1112 * Since we have verified supported values if fidelity is true, 1113 * we can either ignore unsupported values, or substitute a 1114 * reasonable alternative 1115 */ 1116 1117 SheetCollate collateAttr = 1118 (SheetCollate)attributes.get(SheetCollate.class); 1119 if (isSupportedValue(collateAttr, attributes)) { 1120 setCollated(collateAttr == SheetCollate.COLLATED); 1121 } 1122 1123 sidesAttr = (Sides)attributes.get(Sides.class); 1124 if (!isSupportedValue(sidesAttr, attributes)) { 1125 sidesAttr = Sides.ONE_SIDED; 1126 } 1127 1128 pageRangesAttr = (PageRanges)attributes.get(PageRanges.class); 1129 if (!isSupportedValue(pageRangesAttr, attributes)) { 1130 pageRangesAttr = null; 1131 } else { 1132 if ((SunPageSelection)attributes.get(SunPageSelection.class) 1133 == SunPageSelection.RANGE) { 1134 // get to, from, min, max page ranges 1135 int[][] range = pageRangesAttr.getMembers(); 1136 // setPageRanges uses 0-based indexing so we subtract 1 1137 setPageRange(range[0][0] - 1, range[0][1] - 1); 1138 } else { 1139 setPageRange(-1, - 1); 1140 } 1141 } 1142 1143 Copies copies = (Copies)attributes.get(Copies.class); 1144 if (isSupportedValue(copies, attributes) || 1145 (!fidelity && copies != null)) { 1146 copiesAttr = copies.getValue(); 1147 setCopies(copiesAttr); 1148 } else { 1149 copiesAttr = getCopies(); 1150 } 1151 1152 Destination destination = 1153 (Destination)attributes.get(Destination.class); 1154 1155 if (isSupportedValue(destination, attributes)) { 1156 try { 1157 // Old code (new File(destination.getURI())).getPath() 1158 // would generate a "URI is not hierarchical" IAE 1159 // for "file:out.prn" so we use getSchemeSpecificPart instead 1160 destinationAttr = "" + new File(destination.getURI(). 1161 getSchemeSpecificPart()); 1162 } catch (Exception e) { // paranoid exception 1163 Destination defaultDest = (Destination)service. 1164 getDefaultAttributeValue(Destination.class); 1165 if (defaultDest != null) { 1166 destinationAttr = "" + new File(defaultDest.getURI(). 1167 getSchemeSpecificPart()); 1168 } 1169 } 1170 } 1171 1172 JobSheets jobSheets = (JobSheets)attributes.get(JobSheets.class); 1173 if (jobSheets != null) { 1174 noJobSheet = jobSheets == JobSheets.NONE; 1175 } 1176 1177 JobName jobName = (JobName)attributes.get(JobName.class); 1178 if (isSupportedValue(jobName, attributes) || 1179 (!fidelity && jobName != null)) { 1180 jobNameAttr = jobName.getValue(); 1181 setJobName(jobNameAttr); 1182 } else { 1183 jobNameAttr = getJobName(); 1184 } 1185 1186 RequestingUserName userName = 1187 (RequestingUserName)attributes.get(RequestingUserName.class); 1188 if (isSupportedValue(userName, attributes) || 1189 (!fidelity && userName != null)) { 1190 userNameAttr = userName.getValue(); 1191 } else { 1192 try { 1193 userNameAttr = getUserName(); 1194 } catch (SecurityException e) { 1195 userNameAttr = ""; 1196 } 1197 } 1198 1199 /* OpenBook is used internally only when app uses Printable. 1200 * This is the case when we use the values from the attribute set. 1201 */ 1202 Media media = (Media)attributes.get(Media.class); 1203 OrientationRequested orientReq = 1204 (OrientationRequested)attributes.get(OrientationRequested.class); 1205 MediaPrintableArea mpa = 1206 (MediaPrintableArea)attributes.get(MediaPrintableArea.class); 1207 1208 if ((orientReq != null || media != null || mpa != null) && 1209 getPageable() instanceof OpenBook) { 1210 1211 /* We could almost(!) use PrinterJob.getPageFormat() except 1212 * here we need to start with the PageFormat from the OpenBook : 1213 */ 1214 Pageable pageable = getPageable(); 1215 Printable printable = pageable.getPrintable(0); 1216 PageFormat pf = (PageFormat)pageable.getPageFormat(0).clone(); 1217 Paper paper = pf.getPaper(); 1218 1219 /* If there's a media but no media printable area, we can try 1220 * to retrieve the default value for mpa and use that. 1221 */ 1222 if (mpa == null && media != null && 1223 service. 1224 isAttributeCategorySupported(MediaPrintableArea.class)) { 1225 Object mpaVals = service. 1226 getSupportedAttributeValues(MediaPrintableArea.class, 1227 null, attributes); 1228 if (mpaVals instanceof MediaPrintableArea[] && 1229 ((MediaPrintableArea[])mpaVals).length > 0) { 1230 mpa = ((MediaPrintableArea[])mpaVals)[0]; 1231 } 1232 } 1233 1234 if (isSupportedValue(orientReq, attributes) || 1235 (!fidelity && orientReq != null)) { 1236 int orient; 1237 if (orientReq.equals(OrientationRequested.REVERSE_LANDSCAPE)) { 1238 orient = PageFormat.REVERSE_LANDSCAPE; 1239 } else if (orientReq.equals(OrientationRequested.LANDSCAPE)) { 1240 orient = PageFormat.LANDSCAPE; 1241 } else { 1242 orient = PageFormat.PORTRAIT; 1243 } 1244 pf.setOrientation(orient); 1245 } 1246 1247 if (isSupportedValue(media, attributes) || 1248 (!fidelity && media != null)) { 1249 if (media instanceof MediaSizeName) { 1250 MediaSizeName msn = (MediaSizeName)media; 1251 MediaSize msz = MediaSize.getMediaSizeForName(msn); 1252 if (msz != null) { 1253 float paperWid = msz.getX(MediaSize.INCH) * 72.0f; 1254 float paperHgt = msz.getY(MediaSize.INCH) * 72.0f; 1255 paper.setSize(paperWid, paperHgt); 1256 if (mpa == null) { 1257 paper.setImageableArea(72.0, 72.0, 1258 paperWid-144.0, 1259 paperHgt-144.0); 1260 } 1261 } 1262 } 1263 } 1264 1265 if (isSupportedValue(mpa, attributes) || 1266 (!fidelity && mpa != null)) { 1267 float [] printableArea = 1268 mpa.getPrintableArea(MediaPrintableArea.INCH); 1269 for (int i=0; i < printableArea.length; i++) { 1270 printableArea[i] = printableArea[i]*72.0f; 1271 } 1272 paper.setImageableArea(printableArea[0], printableArea[1], 1273 printableArea[2], printableArea[3]); 1274 } 1275 1276 pf.setPaper(paper); 1277 pf = validatePage(pf); 1278 setPrintable(printable, pf); 1279 } else { 1280 // for AWT where pageable is not an instance of OpenBook, 1281 // we need to save paper info 1282 this.attributes = attributes; 1283 } 1284 1285 } 1286 1287 /* 1288 * Services we don't recognize as built-in services can't be 1289 * implemented as subclasses of PrinterJob, therefore we create 1290 * a DocPrintJob from their service and pass a Doc representing 1291 * the application's printjob 1292 */ 1293 // MacOSX - made protected so subclasses can reference it. 1294 protected void spoolToService(PrintService psvc, 1295 PrintRequestAttributeSet attributes) 1296 throws PrinterException { 1297 1298 if (psvc == null) { 1299 throw new PrinterException("No print service found."); 1300 } 1301 1302 DocPrintJob job = psvc.createPrintJob(); 1303 Doc doc = new PageableDoc(getPageable()); 1304 if (attributes == null) { 1305 attributes = new HashPrintRequestAttributeSet(); 1306 } 1307 try { 1308 job.print(doc, attributes); 1309 } catch (PrintException e) { 1310 throw new PrinterException(e.toString()); 1311 } 1312 } 1313 1314 /** 1315 * Prints a set of pages. 1316 * @exception java.awt.print.PrinterException an error in the print system 1317 * caused the job to be aborted 1318 * @see java.awt.print.Book 1319 * @see java.awt.print.Pageable 1320 * @see java.awt.print.Printable 1321 */ 1322 public void print() throws PrinterException { 1323 print(attributes); 1324 } 1325 1326 public static boolean debugPrint = false; 1327 protected void debug_println(String str) { 1328 if (debugPrint) { 1329 System.out.println("RasterPrinterJob "+str+" "+this); 1330 } 1331 } 1332 1333 public void print(PrintRequestAttributeSet attributes) 1334 throws PrinterException { 1335 1336 /* 1337 * In the future PrinterJob will probably always dispatch 1338 * the print job to the PrintService. 1339 * This is how third party 2D Print Services will be invoked 1340 * when applications use the PrinterJob API. 1341 * However the JRE's concrete PrinterJob implementations have 1342 * not yet been re-worked to be implemented as standalone 1343 * services, and are implemented only as subclasses of PrinterJob. 1344 * So here we dispatch only those services we do not recognize 1345 * as implemented through platform subclasses of PrinterJob 1346 * (and this class). 1347 */ 1348 PrintService psvc = getPrintService(); 1349 debug_println("psvc = "+psvc); 1350 if (psvc == null) { 1351 throw new PrinterException("No print service found."); 1352 } 1353 1354 // Check the list of services. This service may have been 1355 // deleted already 1356 PrinterState prnState = (PrinterState)psvc.getAttribute( 1357 PrinterState.class); 1358 if (prnState == PrinterState.STOPPED) { 1359 PrinterStateReasons prnStateReasons = 1360 (PrinterStateReasons)psvc.getAttribute( 1361 PrinterStateReasons.class); 1362 if ((prnStateReasons != null) && 1363 (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN))) 1364 { 1365 throw new PrinterException("PrintService is no longer available."); 1366 } 1367 } 1368 1369 if ((PrinterIsAcceptingJobs)(psvc.getAttribute( 1370 PrinterIsAcceptingJobs.class)) == 1371 PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) { 1372 throw new PrinterException("Printer is not accepting job."); 1373 } 1374 1375 if ((psvc instanceof SunPrinterJobService) && 1376 ((SunPrinterJobService)psvc).usesClass(getClass())) { 1377 setAttributes(attributes); 1378 // throw exception for invalid destination 1379 if (destinationAttr != null) { 1380 validateDestination(destinationAttr); 1381 } 1382 } else { 1383 spoolToService(psvc, attributes); 1384 return; 1385 } 1386 /* We need to make sure that the collation and copies 1387 * settings are initialised */ 1388 initPrinter(); 1389 1390 int numCollatedCopies = getCollatedCopies(); 1391 int numNonCollatedCopies = getNoncollatedCopies(); 1392 debug_println("getCollatedCopies() "+numCollatedCopies 1393 + " getNoncollatedCopies() "+ numNonCollatedCopies); 1394 1395 /* Get the range of pages we are to print. If the 1396 * last page to print is unknown, then we print to 1397 * the end of the document. Note that firstPage 1398 * and lastPage are 0 based page indices. 1399 */ 1400 int numPages = mDocument.getNumberOfPages(); 1401 if (numPages == 0) { 1402 return; 1403 } 1404 1405 int firstPage = getFirstPage(); 1406 int lastPage = getLastPage(); 1407 if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES){ 1408 int totalPages = mDocument.getNumberOfPages(); 1409 if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) { 1410 lastPage = mDocument.getNumberOfPages() - 1; 1411 } 1412 } 1413 1414 try { 1415 synchronized (this) { 1416 performingPrinting = true; 1417 userCancelled = false; 1418 } 1419 1420 startDoc(); 1421 if (isCancelled()) { 1422 cancelDoc(); 1423 } 1424 1425 // PageRanges can be set even if RANGE is not selected 1426 // so we need to check if it is selected. 1427 boolean rangeIsSelected = true; 1428 if (attributes != null) { 1429 SunPageSelection pages = 1430 (SunPageSelection)attributes.get(SunPageSelection.class); 1431 if ((pages != null) && (pages != SunPageSelection.RANGE)) { 1432 rangeIsSelected = false; 1433 } 1434 } 1435 1436 1437 debug_println("after startDoc rangeSelected? "+rangeIsSelected 1438 + " numNonCollatedCopies "+ numNonCollatedCopies); 1439 1440 1441 /* Three nested loops iterate over the document. The outer loop 1442 * counts the number of collated copies while the inner loop 1443 * counts the number of nonCollated copies. Normally, one of 1444 * these two loops will only execute once; that is we will 1445 * either print collated copies or noncollated copies. The 1446 * middle loop iterates over the pages. 1447 * If a PageRanges attribute is used, it constrains the pages 1448 * that are imaged. If a platform subclass (though a user dialog) 1449 * requests a page range via setPageRange(). it too can 1450 * constrain the page ranges that are imaged. 1451 * It is expected that only one of these will be used in a 1452 * job but both should be able to co-exist. 1453 */ 1454 for(int collated = 0; collated < numCollatedCopies; collated++) { 1455 for(int i = firstPage, pageResult = Printable.PAGE_EXISTS; 1456 (i <= lastPage || 1457 lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) 1458 && pageResult == Printable.PAGE_EXISTS; 1459 i++) 1460 { 1461 1462 if ((pageRangesAttr != null) && rangeIsSelected ){ 1463 int nexti = pageRangesAttr.next(i); 1464 if (nexti == -1) { 1465 break; 1466 } else if (nexti != i+1) { 1467 continue; 1468 } 1469 } 1470 1471 for(int nonCollated = 0; 1472 nonCollated < numNonCollatedCopies 1473 && pageResult == Printable.PAGE_EXISTS; 1474 nonCollated++) 1475 { 1476 if (isCancelled()) { 1477 cancelDoc(); 1478 } 1479 debug_println("printPage "+i); 1480 pageResult = printPage(mDocument, i); 1481 1482 } 1483 } 1484 } 1485 1486 if (isCancelled()) { 1487 cancelDoc(); 1488 } 1489 1490 } finally { 1491 // reset previousPaper in case this job is invoked again. 1492 previousPaper = null; 1493 synchronized (this) { 1494 if (performingPrinting) { 1495 endDoc(); 1496 } 1497 performingPrinting = false; 1498 notify(); 1499 } 1500 } 1501 } 1502 1503 protected void validateDestination(String dest) throws PrinterException { 1504 if (dest == null) { 1505 return; 1506 } 1507 // dest is null for Destination(new URI("")) 1508 // because isAttributeValueSupported returns false in setAttributes 1509 1510 // Destination(new URI(" ")) throws URISyntaxException 1511 File f = new File(dest); 1512 try { 1513 // check if this is a new file and if filename chars are valid 1514 if (f.createNewFile()) { 1515 f.delete(); 1516 } 1517 } catch (IOException ioe) { 1518 throw new PrinterException("Cannot write to file:"+ 1519 dest); 1520 } catch (SecurityException se) { 1521 //There is already file read/write access so at this point 1522 // only delete access is denied. Just ignore it because in 1523 // most cases the file created in createNewFile gets overwritten 1524 // anyway. 1525 } 1526 1527 File pFile = f.getParentFile(); 1528 if ((f.exists() && 1529 (!f.isFile() || !f.canWrite())) || 1530 ((pFile != null) && 1531 (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) { 1532 throw new PrinterException("Cannot write to file:"+ 1533 dest); 1534 } 1535 } 1536 1537 /** 1538 * updates a Paper object to reflect the current printer's selected 1539 * paper size and imageable area for that paper size. 1540 * Default implementation copies settings from the original, applies 1541 * applies some validity checks, changes them only if they are 1542 * clearly unreasonable, then sets them into the new Paper. 1543 * Subclasses are expected to override this method to make more 1544 * informed decisons. 1545 */ 1546 protected void validatePaper(Paper origPaper, Paper newPaper) { 1547 if (origPaper == null || newPaper == null) { 1548 return; 1549 } else { 1550 double wid = origPaper.getWidth(); 1551 double hgt = origPaper.getHeight(); 1552 double ix = origPaper.getImageableX(); 1553 double iy = origPaper.getImageableY(); 1554 double iw = origPaper.getImageableWidth(); 1555 double ih = origPaper.getImageableHeight(); 1556 1557 /* Assume any +ve values are legal. Overall paper dimensions 1558 * take precedence. Make sure imageable area fits on the paper. 1559 */ 1560 Paper defaultPaper = new Paper(); 1561 wid = ((wid > 0.0) ? wid : defaultPaper.getWidth()); 1562 hgt = ((hgt > 0.0) ? hgt : defaultPaper.getHeight()); 1563 ix = ((ix > 0.0) ? ix : defaultPaper.getImageableX()); 1564 iy = ((iy > 0.0) ? iy : defaultPaper.getImageableY()); 1565 iw = ((iw > 0.0) ? iw : defaultPaper.getImageableWidth()); 1566 ih = ((ih > 0.0) ? ih : defaultPaper.getImageableHeight()); 1567 /* full width/height is not likely to be imageable, but since we 1568 * don't know the limits we have to allow it 1569 */ 1570 if (iw > wid) { 1571 iw = wid; 1572 } 1573 if (ih > hgt) { 1574 ih = hgt; 1575 } 1576 if ((ix + iw) > wid) { 1577 ix = wid - iw; 1578 } 1579 if ((iy + ih) > hgt) { 1580 iy = hgt - ih; 1581 } 1582 newPaper.setSize(wid, hgt); 1583 newPaper.setImageableArea(ix, iy, iw, ih); 1584 } 1585 } 1586 1587 /** 1588 * The passed in PageFormat will be copied and altered to describe 1589 * the default page size and orientation of the PrinterJob's 1590 * current printer. 1591 * Platform subclasses which can access the actual default paper size 1592 * for a printer may override this method. 1593 */ 1594 public PageFormat defaultPage(PageFormat page) { 1595 PageFormat newPage = (PageFormat)page.clone(); 1596 newPage.setOrientation(PageFormat.PORTRAIT); 1597 Paper newPaper = new Paper(); 1598 double ptsPerInch = 72.0; 1599 double w, h; 1600 Media media = null; 1601 1602 PrintService service = getPrintService(); 1603 if (service != null) { 1604 MediaSize size; 1605 media = 1606 (Media)service.getDefaultAttributeValue(Media.class); 1607 1608 if (media instanceof MediaSizeName && 1609 ((size = MediaSize.getMediaSizeForName((MediaSizeName)media)) != 1610 null)) { 1611 w = size.getX(MediaSize.INCH) * ptsPerInch; 1612 h = size.getY(MediaSize.INCH) * ptsPerInch; 1613 newPaper.setSize(w, h); 1614 newPaper.setImageableArea(ptsPerInch, ptsPerInch, 1615 w - 2.0*ptsPerInch, 1616 h - 2.0*ptsPerInch); 1617 newPage.setPaper(newPaper); 1618 return newPage; 1619 1620 } 1621 } 1622 1623 /* Default to A4 paper outside North America. 1624 */ 1625 String defaultCountry = Locale.getDefault().getCountry(); 1626 if (!Locale.getDefault().equals(Locale.ENGLISH) && // ie "C" 1627 defaultCountry != null && 1628 !defaultCountry.equals(Locale.US.getCountry()) && 1629 !defaultCountry.equals(Locale.CANADA.getCountry())) { 1630 1631 double mmPerInch = 25.4; 1632 w = Math.rint((210.0*ptsPerInch)/mmPerInch); 1633 h = Math.rint((297.0*ptsPerInch)/mmPerInch); 1634 newPaper.setSize(w, h); 1635 newPaper.setImageableArea(ptsPerInch, ptsPerInch, 1636 w - 2.0*ptsPerInch, 1637 h - 2.0*ptsPerInch); 1638 } 1639 1640 newPage.setPaper(newPaper); 1641 1642 return newPage; 1643 } 1644 1645 /** 1646 * The passed in PageFormat is cloned and altered to be usable on 1647 * the PrinterJob's current printer. 1648 */ 1649 public PageFormat validatePage(PageFormat page) { 1650 PageFormat newPage = (PageFormat)page.clone(); 1651 Paper newPaper = new Paper(); 1652 validatePaper(newPage.getPaper(), newPaper); 1653 newPage.setPaper(newPaper); 1654 1655 return newPage; 1656 } 1657 1658 /** 1659 * Set the number of copies to be printed. 1660 */ 1661 public void setCopies(int copies) { 1662 mNumCopies = copies; 1663 } 1664 1665 /** 1666 * Get the number of copies to be printed. 1667 */ 1668 public int getCopies() { 1669 return mNumCopies; 1670 } 1671 1672 /* Used when executing a print job where an attribute set may 1673 * over ride API values. 1674 */ 1675 protected int getCopiesInt() { 1676 return (copiesAttr > 0) ? copiesAttr : getCopies(); 1677 } 1678 1679 /** 1680 * Get the name of the printing user. 1681 * The caller must have security permission to read system properties. 1682 */ 1683 public String getUserName() { 1684 return System.getProperty("user.name"); 1685 } 1686 1687 /* Used when executing a print job where an attribute set may 1688 * over ride API values. 1689 */ 1690 protected String getUserNameInt() { 1691 if (userNameAttr != null) { 1692 return userNameAttr; 1693 } else { 1694 try { 1695 return getUserName(); 1696 } catch (SecurityException e) { 1697 return ""; 1698 } 1699 } 1700 } 1701 1702 /** 1703 * Set the name of the document to be printed. 1704 * The document name can not be null. 1705 */ 1706 public void setJobName(String jobName) { 1707 if (jobName != null) { 1708 mDocName = jobName; 1709 } else { 1710 throw new NullPointerException(); 1711 } 1712 } 1713 1714 /** 1715 * Get the name of the document to be printed. 1716 */ 1717 public String getJobName() { 1718 return mDocName; 1719 } 1720 1721 /* Used when executing a print job where an attribute set may 1722 * over ride API values. 1723 */ 1724 protected String getJobNameInt() { 1725 return (jobNameAttr != null) ? jobNameAttr : getJobName(); 1726 } 1727 1728 /** 1729 * Set the range of pages from a Book to be printed. 1730 * Both 'firstPage' and 'lastPage' are zero based 1731 * page indices. If either parameter is less than 1732 * zero then the page range is set to be from the 1733 * first page to the last. 1734 */ 1735 protected void setPageRange(int firstPage, int lastPage) { 1736 if(firstPage >= 0 && lastPage >= 0) { 1737 mFirstPage = firstPage; 1738 mLastPage = lastPage; 1739 if(mLastPage < mFirstPage) mLastPage = mFirstPage; 1740 } else { 1741 mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; 1742 mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; 1743 } 1744 } 1745 1746 /** 1747 * Return the zero based index of the first page to 1748 * be printed in this job. 1749 */ 1750 protected int getFirstPage() { 1751 return mFirstPage == Book.UNKNOWN_NUMBER_OF_PAGES ? 0 : mFirstPage; 1752 } 1753 1754 /** 1755 * Return the zero based index of the last page to 1756 * be printed in this job. 1757 */ 1758 protected int getLastPage() { 1759 return mLastPage; 1760 } 1761 1762 /** 1763 * Set whether copies should be collated or not. 1764 * Two collated copies of a three page document 1765 * print in this order: 1, 2, 3, 1, 2, 3 while 1766 * uncollated copies print in this order: 1767 * 1, 1, 2, 2, 3, 3. 1768 * This is set when request is using an attribute set. 1769 */ 1770 protected void setCollated(boolean collate) { 1771 mCollate = collate; 1772 collateAttReq = true; 1773 } 1774 1775 /** 1776 * Return true if collated copies will be printed as determined 1777 * in an attribute set. 1778 */ 1779 protected boolean isCollated() { 1780 return mCollate; 1781 } 1782 1783 protected final int getSelectAttrib() { 1784 if (attributes != null) { 1785 SunPageSelection pages = 1786 (SunPageSelection)attributes.get(SunPageSelection.class); 1787 if (pages == SunPageSelection.RANGE) { 1788 return PD_PAGENUMS; 1789 } else if (pages == SunPageSelection.SELECTION) { 1790 return PD_SELECTION; 1791 } else if (pages == SunPageSelection.ALL) { 1792 return PD_ALLPAGES; 1793 } 1794 } 1795 return PD_NOSELECTION; 1796 } 1797 1798 //returns 1-based index for "From" page 1799 protected final int getFromPageAttrib() { 1800 if (attributes != null) { 1801 PageRanges pageRangesAttr = 1802 (PageRanges)attributes.get(PageRanges.class); 1803 if (pageRangesAttr != null) { 1804 int[][] range = pageRangesAttr.getMembers(); 1805 return range[0][0]; 1806 } 1807 } 1808 return getMinPageAttrib(); 1809 } 1810 1811 //returns 1-based index for "To" page 1812 protected final int getToPageAttrib() { 1813 if (attributes != null) { 1814 PageRanges pageRangesAttr = 1815 (PageRanges)attributes.get(PageRanges.class); 1816 if (pageRangesAttr != null) { 1817 int[][] range = pageRangesAttr.getMembers(); 1818 return range[range.length-1][1]; 1819 } 1820 } 1821 return getMaxPageAttrib(); 1822 } 1823 1824 protected final int getMinPageAttrib() { 1825 if (attributes != null) { 1826 SunMinMaxPage s = 1827 (SunMinMaxPage)attributes.get(SunMinMaxPage.class); 1828 if (s != null) { 1829 return s.getMin(); 1830 } 1831 } 1832 return 1; 1833 } 1834 1835 protected final int getMaxPageAttrib() { 1836 if (attributes != null) { 1837 SunMinMaxPage s = 1838 (SunMinMaxPage)attributes.get(SunMinMaxPage.class); 1839 if (s != null) { 1840 return s.getMax(); 1841 } 1842 } 1843 1844 Pageable pageable = getPageable(); 1845 if (pageable != null) { 1846 int numPages = pageable.getNumberOfPages(); 1847 if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) { 1848 numPages = MAX_UNKNOWN_PAGES; 1849 } 1850 return ((numPages == 0) ? 1 : numPages); 1851 } 1852 1853 return Integer.MAX_VALUE; 1854 } 1855 /** 1856 * Called by the print() method at the start of 1857 * a print job. 1858 */ 1859 protected abstract void startDoc() throws PrinterException; 1860 1861 /** 1862 * Called by the print() method at the end of 1863 * a print job. 1864 */ 1865 protected abstract void endDoc() throws PrinterException; 1866 1867 /* Called by cancelDoc */ 1868 protected abstract void abortDoc(); 1869 1870 // MacOSX - made protected so subclasses can reference it. 1871 protected void cancelDoc() throws PrinterAbortException { 1872 abortDoc(); 1873 synchronized (this) { 1874 userCancelled = false; 1875 performingPrinting = false; 1876 notify(); 1877 } 1878 throw new PrinterAbortException(); 1879 } 1880 1881 /** 1882 * Returns how many times the entire book should 1883 * be printed by the PrintJob. If the printer 1884 * itself supports collation then this method 1885 * should return 1 indicating that the entire 1886 * book need only be printed once and the copies 1887 * will be collated and made in the printer. 1888 */ 1889 protected int getCollatedCopies() { 1890 return isCollated() ? getCopiesInt() : 1; 1891 } 1892 1893 /** 1894 * Returns how many times each page in the book 1895 * should be consecutively printed by PrintJob. 1896 * If the printer makes copies itself then this 1897 * method should return 1. 1898 */ 1899 protected int getNoncollatedCopies() { 1900 return isCollated() ? 1 : getCopiesInt(); 1901 } 1902 1903 1904 /* The printer graphics config is cached on the job, so that it can 1905 * be created once, and updated only as needed (for now only to change 1906 * the bounds if when using a Pageable the page sizes changes). 1907 */ 1908 1909 private int deviceWidth, deviceHeight; 1910 private AffineTransform defaultDeviceTransform; 1911 private PrinterGraphicsConfig pgConfig; 1912 1913 synchronized void setGraphicsConfigInfo(AffineTransform at, 1914 double pw, double ph) { 1915 Point2D.Double pt = new Point2D.Double(pw, ph); 1916 at.transform(pt, pt); 1917 1918 if (pgConfig == null || 1919 defaultDeviceTransform == null || 1920 !at.equals(defaultDeviceTransform) || 1921 deviceWidth != (int)pt.getX() || 1922 deviceHeight != (int)pt.getY()) { 1923 1924 deviceWidth = (int)pt.getX(); 1925 deviceHeight = (int)pt.getY(); 1926 defaultDeviceTransform = at; 1927 pgConfig = null; 1928 } 1929 } 1930 1931 synchronized PrinterGraphicsConfig getPrinterGraphicsConfig() { 1932 if (pgConfig != null) { 1933 return pgConfig; 1934 } 1935 String deviceID = "Printer Device"; 1936 PrintService service = getPrintService(); 1937 if (service != null) { 1938 deviceID = service.toString(); 1939 } 1940 pgConfig = new PrinterGraphicsConfig(deviceID, 1941 defaultDeviceTransform, 1942 deviceWidth, deviceHeight); 1943 return pgConfig; 1944 } 1945 1946 /** 1947 * Print a page from the provided document. 1948 * @return int Printable.PAGE_EXISTS if the page existed and was drawn and 1949 * Printable.NO_SUCH_PAGE if the page did not exist. 1950 * @see java.awt.print.Printable 1951 */ 1952 protected int printPage(Pageable document, int pageIndex) 1953 throws PrinterException 1954 { 1955 PageFormat page; 1956 PageFormat origPage; 1957 Printable painter; 1958 try { 1959 origPage = document.getPageFormat(pageIndex); 1960 page = (PageFormat)origPage.clone(); 1961 painter = document.getPrintable(pageIndex); 1962 } catch (Exception e) { 1963 PrinterException pe = 1964 new PrinterException("Error getting page or printable.[ " + 1965 e +" ]"); 1966 pe.initCause(e); 1967 throw pe; 1968 } 1969 1970 /* Get the imageable area from Paper instead of PageFormat 1971 * because we do not want it adjusted by the page orientation. 1972 */ 1973 Paper paper = page.getPaper(); 1974 // if non-portrait and 270 degree landscape rotation 1975 if (page.getOrientation() != PageFormat.PORTRAIT && 1976 landscapeRotates270) { 1977 1978 double left = paper.getImageableX(); 1979 double top = paper.getImageableY(); 1980 double width = paper.getImageableWidth(); 1981 double height = paper.getImageableHeight(); 1982 paper.setImageableArea(paper.getWidth()-left-width, 1983 paper.getHeight()-top-height, 1984 width, height); 1985 page.setPaper(paper); 1986 if (page.getOrientation() == PageFormat.LANDSCAPE) { 1987 page.setOrientation(PageFormat.REVERSE_LANDSCAPE); 1988 } else { 1989 page.setOrientation(PageFormat.LANDSCAPE); 1990 } 1991 } 1992 1993 double xScale = getXRes() / 72.0; 1994 double yScale = getYRes() / 72.0; 1995 1996 /* The deviceArea is the imageable area in the printer's 1997 * resolution. 1998 */ 1999 Rectangle2D deviceArea = 2000 new Rectangle2D.Double(paper.getImageableX() * xScale, 2001 paper.getImageableY() * yScale, 2002 paper.getImageableWidth() * xScale, 2003 paper.getImageableHeight() * yScale); 2004 2005 /* Build and hold on to a uniform transform so that 2006 * we can get back to device space at the beginning 2007 * of each band. 2008 */ 2009 AffineTransform uniformTransform = new AffineTransform(); 2010 2011 /* The scale transform is used to switch from the 2012 * device space to the user's 72 dpi space. 2013 */ 2014 AffineTransform scaleTransform = new AffineTransform(); 2015 scaleTransform.scale(xScale, yScale); 2016 2017 /* bandwidth is multiple of 4 as the data is used in a win32 DIB and 2018 * some drivers behave badly if scanlines aren't multiples of 4 bytes. 2019 */ 2020 int bandWidth = (int) deviceArea.getWidth(); 2021 if (bandWidth % 4 != 0) { 2022 bandWidth += (4 - (bandWidth % 4)); 2023 } 2024 if (bandWidth <= 0) { 2025 throw new PrinterException("Paper's imageable width is too small."); 2026 } 2027 2028 int deviceAreaHeight = (int)deviceArea.getHeight(); 2029 if (deviceAreaHeight <= 0) { 2030 throw new PrinterException("Paper's imageable height is too small."); 2031 } 2032 2033 /* Figure out the number of lines that will fit into 2034 * our maximum band size. The hard coded 3 reflects the 2035 * fact that we can only create 24 bit per pixel 3 byte BGR 2036 * BufferedImages. FIX. 2037 */ 2038 int bandHeight = (int)(MAX_BAND_SIZE / bandWidth / 3); 2039 2040 int deviceLeft = (int)Math.rint(paper.getImageableX() * xScale); 2041 int deviceTop = (int)Math.rint(paper.getImageableY() * yScale); 2042 2043 /* The device transform is used to move the band down 2044 * the page using translates. Normally this is all it 2045 * would do, but since, when printing, the Window's 2046 * DIB format wants the last line to be first (lowest) in 2047 * memory, the deviceTransform moves the origin to the 2048 * bottom of the band and flips the origin. This way the 2049 * app prints upside down into the band which is the DIB 2050 * format. 2051 */ 2052 AffineTransform deviceTransform = new AffineTransform(); 2053 deviceTransform.translate(-deviceLeft, deviceTop); 2054 deviceTransform.translate(0, bandHeight); 2055 deviceTransform.scale(1, -1); 2056 2057 /* Create a BufferedImage to hold the band. We set the clip 2058 * of the band to be tight around the bits so that the 2059 * application can use it to figure what part of the 2060 * page needs to be drawn. The clip is never altered in 2061 * this method, but we do translate the band's coordinate 2062 * system so that the app will see the clip moving down the 2063 * page though it s always around the same set of pixels. 2064 */ 2065 BufferedImage pBand = new BufferedImage(1, 1, 2066 BufferedImage.TYPE_3BYTE_BGR); 2067 2068 /* Have the app draw into a PeekGraphics object so we can 2069 * learn something about the needs of the print job. 2070 */ 2071 2072 PeekGraphics peekGraphics = createPeekGraphics(pBand.createGraphics(), 2073 this); 2074 2075 Rectangle2D.Double pageFormatArea = 2076 new Rectangle2D.Double(page.getImageableX(), 2077 page.getImageableY(), 2078 page.getImageableWidth(), 2079 page.getImageableHeight()); 2080 peekGraphics.transform(scaleTransform); 2081 peekGraphics.translate(-getPhysicalPrintableX(paper) / xScale, 2082 -getPhysicalPrintableY(paper) / yScale); 2083 peekGraphics.transform(new AffineTransform(page.getMatrix())); 2084 initPrinterGraphics(peekGraphics, pageFormatArea); 2085 AffineTransform pgAt = peekGraphics.getTransform(); 2086 2087 /* Update the information used to return a GraphicsConfiguration 2088 * for this printer device. It needs to be updated per page as 2089 * not all pages in a job may be the same size (different bounds) 2090 * The transform is the scaling transform as this corresponds to 2091 * the default transform for the device. The width and height are 2092 * those of the paper, not the page format, as we want to describe 2093 * the bounds of the device in its natural coordinate system of 2094 * device coordinate whereas a page format may be in a rotated context. 2095 */ 2096 setGraphicsConfigInfo(scaleTransform, 2097 paper.getWidth(), paper.getHeight()); 2098 int pageResult = painter.print(peekGraphics, origPage, pageIndex); 2099 debug_println("pageResult "+pageResult); 2100 if (pageResult == Printable.PAGE_EXISTS) { 2101 debug_println("startPage "+pageIndex); 2102 2103 /* We need to check if the paper size is changed. 2104 * Note that it is not sufficient to ask for the pageformat 2105 * of "pageIndex-1", since PageRanges mean that pages can be 2106 * skipped. So we have to look at the actual last paper size used. 2107 */ 2108 Paper thisPaper = page.getPaper(); 2109 boolean paperChanged = 2110 previousPaper == null || 2111 thisPaper.getWidth() != previousPaper.getWidth() || 2112 thisPaper.getHeight() != previousPaper.getHeight(); 2113 previousPaper = thisPaper; 2114 2115 startPage(page, painter, pageIndex, paperChanged); 2116 Graphics2D pathGraphics = createPathGraphics(peekGraphics, this, 2117 painter, page, 2118 pageIndex); 2119 2120 /* If we can convert the page directly to the 2121 * underlying graphics system then we do not 2122 * need to rasterize. We also may not need to 2123 * create the 'band' if all the pages can take 2124 * this path. 2125 */ 2126 if (pathGraphics != null) { 2127 pathGraphics.transform(scaleTransform); 2128 // user (0,0) should be origin of page, not imageable area 2129 pathGraphics.translate(-getPhysicalPrintableX(paper) / xScale, 2130 -getPhysicalPrintableY(paper) / yScale); 2131 pathGraphics.transform(new AffineTransform(page.getMatrix())); 2132 initPrinterGraphics(pathGraphics, pageFormatArea); 2133 2134 redrawList.clear(); 2135 2136 AffineTransform initialTx = pathGraphics.getTransform(); 2137 2138 painter.print(pathGraphics, origPage, pageIndex); 2139 2140 for (int i=0;i<redrawList.size();i++) { 2141 GraphicsState gstate = (GraphicsState)redrawList.get(i); 2142 pathGraphics.setTransform(initialTx); 2143 ((PathGraphics)pathGraphics).redrawRegion( 2144 gstate.region, 2145 gstate.sx, 2146 gstate.sy, 2147 gstate.theClip, 2148 gstate.theTransform); 2149 } 2150 2151 /* This is the banded-raster printing loop. 2152 * It should be moved into its own method. 2153 */ 2154 } else { 2155 BufferedImage band = cachedBand; 2156 if (cachedBand == null || 2157 bandWidth != cachedBandWidth || 2158 bandHeight != cachedBandHeight) { 2159 band = new BufferedImage(bandWidth, bandHeight, 2160 BufferedImage.TYPE_3BYTE_BGR); 2161 cachedBand = band; 2162 cachedBandWidth = bandWidth; 2163 cachedBandHeight = bandHeight; 2164 } 2165 Graphics2D bandGraphics = band.createGraphics(); 2166 2167 Rectangle2D.Double clipArea = 2168 new Rectangle2D.Double(0, 0, bandWidth, bandHeight); 2169 2170 initPrinterGraphics(bandGraphics, clipArea); 2171 2172 ProxyGraphics2D painterGraphics = 2173 new ProxyGraphics2D(bandGraphics, this); 2174 2175 Graphics2D clearGraphics = band.createGraphics(); 2176 clearGraphics.setColor(Color.white); 2177 2178 /* We need the actual bits of the BufferedImage to send to 2179 * the native Window's code. 'data' points to the actual 2180 * pixels. Right now these are in ARGB format with 8 bits 2181 * per component. We need to use a monochrome BufferedImage 2182 * for monochrome printers when this is supported by 2183 * BufferedImage. FIX 2184 */ 2185 ByteInterleavedRaster tile = (ByteInterleavedRaster)band.getRaster(); 2186 byte[] data = tile.getDataStorage(); 2187 2188 /* Loop over the page moving our band down the page, 2189 * calling the app to render the band, and then send the band 2190 * to the printer. 2191 */ 2192 int deviceBottom = deviceTop + deviceAreaHeight; 2193 2194 /* device's printable x,y is really addressable origin 2195 * we address relative to media origin so when we print a 2196 * band we need to adjust for the different methods of 2197 * addressing it. 2198 */ 2199 int deviceAddressableX = (int)getPhysicalPrintableX(paper); 2200 int deviceAddressableY = (int)getPhysicalPrintableY(paper); 2201 2202 for (int bandTop = 0; bandTop <= deviceAreaHeight; 2203 bandTop += bandHeight) 2204 { 2205 2206 /* Put the band back into device space and 2207 * erase the contents of the band. 2208 */ 2209 clearGraphics.fillRect(0, 0, bandWidth, bandHeight); 2210 2211 /* Put the band into the correct location on the 2212 * page. Once the band is moved we translate the 2213 * device transform so that the band will move down 2214 * the page on the next iteration of the loop. 2215 */ 2216 bandGraphics.setTransform(uniformTransform); 2217 bandGraphics.transform(deviceTransform); 2218 deviceTransform.translate(0, -bandHeight); 2219 2220 /* Switch the band from device space to user, 2221 * 72 dpi, space. 2222 */ 2223 bandGraphics.transform(scaleTransform); 2224 bandGraphics.transform(new AffineTransform(page.getMatrix())); 2225 2226 Rectangle clip = bandGraphics.getClipBounds(); 2227 clip = pgAt.createTransformedShape(clip).getBounds(); 2228 2229 if ((clip == null) || peekGraphics.hitsDrawingArea(clip) && 2230 (bandWidth > 0 && bandHeight > 0)) { 2231 2232 /* if the client has specified an imageable X or Y 2233 * which is off than the physically addressable 2234 * area of the page, then we need to adjust for that 2235 * here so that we pass only non -ve band coordinates 2236 * We also need to translate by the adjusted amount 2237 * so that printing appears in the correct place. 2238 */ 2239 int bandX = deviceLeft - deviceAddressableX; 2240 if (bandX < 0) { 2241 bandGraphics.translate(bandX/xScale,0); 2242 bandX = 0; 2243 } 2244 int bandY = deviceTop + bandTop - deviceAddressableY; 2245 if (bandY < 0) { 2246 bandGraphics.translate(0,bandY/yScale); 2247 bandY = 0; 2248 } 2249 /* Have the app's painter image into the band 2250 * and then send the band to the printer. 2251 */ 2252 painterGraphics.setDelegate((Graphics2D) bandGraphics.create()); 2253 painter.print(painterGraphics, origPage, pageIndex); 2254 painterGraphics.dispose(); 2255 printBand(data, bandX, bandY, bandWidth, bandHeight); 2256 } 2257 } 2258 2259 clearGraphics.dispose(); 2260 bandGraphics.dispose(); 2261 2262 } 2263 debug_println("calling endPage "+pageIndex); 2264 endPage(page, painter, pageIndex); 2265 } 2266 2267 return pageResult; 2268 } 2269 2270 /** 2271 * If a print job is in progress, print() has been 2272 * called but has not returned, then this signals 2273 * that the job should be cancelled and the next 2274 * chance. If there is no print job in progress then 2275 * this call does nothing. 2276 */ 2277 public void cancel() { 2278 synchronized (this) { 2279 if (performingPrinting) { 2280 userCancelled = true; 2281 } 2282 notify(); 2283 } 2284 } 2285 2286 /** 2287 * Returns true is a print job is ongoing but will 2288 * be cancelled and the next opportunity. false is 2289 * returned otherwise. 2290 */ 2291 public boolean isCancelled() { 2292 2293 boolean cancelled = false; 2294 2295 synchronized (this) { 2296 cancelled = (performingPrinting && userCancelled); 2297 notify(); 2298 } 2299 2300 return cancelled; 2301 } 2302 2303 /** 2304 * Return the Pageable describing the pages to be printed. 2305 */ 2306 protected Pageable getPageable() { 2307 return mDocument; 2308 } 2309 2310 /** 2311 * Examine the metrics captured by the 2312 * <code>PeekGraphics</code> instance and 2313 * if capable of directly converting this 2314 * print job to the printer's control language 2315 * or the native OS's graphics primitives, then 2316 * return a <code>PathGraphics</code> to perform 2317 * that conversion. If there is not an object 2318 * capable of the conversion then return 2319 * <code>null</code>. Returning <code>null</code> 2320 * causes the print job to be rasterized. 2321 */ 2322 protected Graphics2D createPathGraphics(PeekGraphics graphics, 2323 PrinterJob printerJob, 2324 Printable painter, 2325 PageFormat pageFormat, 2326 int pageIndex) { 2327 2328 return null; 2329 } 2330 2331 /** 2332 * Create and return an object that will 2333 * gather and hold metrics about the print 2334 * job. This method is passed a <code>Graphics2D</code> 2335 * object that can be used as a proxy for the 2336 * object gathering the print job matrics. The 2337 * method is also supplied with the instance 2338 * controlling the print job, <code>printerJob</code>. 2339 */ 2340 protected PeekGraphics createPeekGraphics(Graphics2D graphics, 2341 PrinterJob printerJob) { 2342 2343 return new PeekGraphics(graphics, printerJob); 2344 } 2345 2346 /** 2347 * Configure the passed in Graphics2D so that 2348 * is contains the defined initial settings 2349 * for a print job. These settings are: 2350 * color: black. 2351 * clip: <as passed in> 2352 */ 2353 // MacOSX - made protected so subclasses can reference it. 2354 protected void initPrinterGraphics(Graphics2D g, Rectangle2D clip) { 2355 2356 g.setClip(clip); 2357 g.setPaint(Color.black); 2358 } 2359 2360 2361 /** 2362 * User dialogs should disable "File" buttons if this returns false. 2363 * 2364 */ 2365 public boolean checkAllowedToPrintToFile() { 2366 try { 2367 throwPrintToFile(); 2368 return true; 2369 } catch (SecurityException e) { 2370 return false; 2371 } 2372 } 2373 2374 /** 2375 * Break this out as it may be useful when we allow API to 2376 * specify printing to a file. In that case its probably right 2377 * to throw a SecurityException if the permission is not granted 2378 */ 2379 private void throwPrintToFile() { 2380 SecurityManager security = System.getSecurityManager(); 2381 if (security != null) { 2382 if (printToFilePermission == null) { 2383 printToFilePermission = 2384 new FilePermission("<<ALL FILES>>", "read,write"); 2385 } 2386 security.checkPermission(printToFilePermission); 2387 } 2388 } 2389 2390 /* On-screen drawString renders most control chars as the missing glyph 2391 * and have the non-zero advance of that glyph. 2392 * Exceptions are \t, \n and \r which are considered zero-width. 2393 * This is a utility method used by subclasses to remove them so we 2394 * don't have to worry about platform or font specific handling of them. 2395 */ 2396 protected String removeControlChars(String s) { 2397 char[] in_chars = s.toCharArray(); 2398 int len = in_chars.length; 2399 char[] out_chars = new char[len]; 2400 int pos = 0; 2401 2402 for (int i = 0; i < len; i++) { 2403 char c = in_chars[i]; 2404 if (c > '\r' || c < '\t' || c == '\u000b' || c == '\u000c') { 2405 out_chars[pos++] = c; 2406 } 2407 } 2408 if (pos == len) { 2409 return s; // no need to make a new String. 2410 } else { 2411 return new String(out_chars, 0, pos); 2412 } 2413 } 2414 }