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