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