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