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