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