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